From 6f7051c3b10fde8062a4ac8844e49efbe910aa24 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 10 Jun 2019 11:49:15 +0200 Subject: [PATCH 01/72] GCode preview shows correct volumetric flow for the wipe tower The neccessary annotations for the GCodeAnalyzer were missing --- src/libslic3r/GCode.cpp | 2 +- src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index c42669de0..436628d8a 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2554,7 +2554,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, gcode += buf; } - if (m_last_mm3_per_mm != path.mm3_per_mm) + if (last_was_wipe_tower || (m_last_mm3_per_mm != path.mm3_per_mm)) { m_last_mm3_per_mm = path.mm3_per_mm; diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index edfe475b5..ffc737607 100644 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -68,6 +68,16 @@ public: return *this; } + Writer& change_analyzer_mm3_per_mm(float len, float e) { + static const float area = M_PI * 1.75f * 1.75f / 4.f; + float mm3_per_mm = (len == 0.f ? 0.f : area * e / len); + // adds tag for analyzer: + char buf[64]; + sprintf(buf, ";%s%f\n", GCodeAnalyzer::Mm3_Per_Mm_Tag.c_str(), mm3_per_mm); + m_gcode += buf; + return *this; + } + Writer& set_initial_position(const WipeTower::xy &pos, float width = 0.f, float depth = 0.f, float internal_angle = 0.f) { m_wipe_tower_width = width; m_wipe_tower_depth = depth; @@ -127,12 +137,12 @@ public: if (record_length) m_used_filament_length += e; - // Now do the "internal rotation" with respect to the wipe tower center WipeTower::xy rotated_current_pos(WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we are WipeTower::xy rot(WipeTower::xy(x,y+m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we want to go if (! m_preview_suppressed && e > 0.f && len > 0.) { + change_analyzer_mm3_per_mm(len, e); // Width of a squished extrusion, corrected for the roundings of the squished extrusions. // This is left zero if it is a travel move. float width = float(double(e) * /*Filament_Area*/2.40528 / (len * m_layer_height)); @@ -155,7 +165,7 @@ public: m_gcode += set_format_E(e); if (f != 0.f && f != m_current_feedrate) - m_gcode += set_format_F(f); + m_gcode += set_format_F(f); m_current_pos.x = x; m_current_pos.y = y; From 079e63e19082f75e33fa73c099c7901395779d40 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 10 Jun 2019 12:26:47 +0200 Subject: [PATCH 02/72] The wipe tower now respects filament max volumetric flow The odd commands that lowered the speed override values for PVA, FLEX etc. were removed Now the wipe tower backups user speed override, sets it to 100%, does what is needed and restores the old value when finished. There are no special cases - lowering the speed for certain materials can be achieved by lowering the volumetric flow. --- src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 55 ++++++++++-------------- src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 47 +++++++++----------- src/libslic3r/Print.cpp | 3 +- 3 files changed, 46 insertions(+), 59 deletions(-) diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index ffc737607..5165ac358 100644 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -40,7 +40,7 @@ namespace PrusaMultiMaterial { class Writer { public: - Writer(float layer_height, float line_width, GCodeFlavor flavor) : + Writer(float layer_height, float line_width, GCodeFlavor flavor, const std::vector& filament_parameters) : m_current_pos(std::numeric_limits::max(), std::numeric_limits::max()), m_current_z(0.f), m_current_feedrate(0.f), @@ -49,7 +49,8 @@ public: m_preview_suppressed(false), m_elapsed_time(0.f), m_default_analyzer_line_width(line_width), - m_gcode_flavor(flavor) + m_gcode_flavor(flavor), + m_filpar(filament_parameters) { // adds tag for analyzer: char buf[64]; @@ -125,7 +126,7 @@ public: float get_and_reset_used_filament_length() { float temp = m_used_filament_length; m_used_filament_length = 0.f; return temp; } // Extrude with an explicitely provided amount of extrusion. - Writer& extrude_explicit(float x, float y, float e, float f = 0.f, bool record_length = false) + Writer& extrude_explicit(float x, float y, float e, float f = 0.f, bool record_length = false, bool limit_volumetric_flow = true) { if (x == m_current_pos.x && y == m_current_pos.y && e == 0.f && (f == 0.f || f == m_current_feedrate)) // Neither extrusion nor a travel move. @@ -164,8 +165,13 @@ public: if (e != 0.f) m_gcode += set_format_E(e); - if (f != 0.f && f != m_current_feedrate) - m_gcode += set_format_F(f); + if (f != 0.f && f != m_current_feedrate) { + if (limit_volumetric_flow) { + float e_speed = e / (((len == 0) ? std::abs(e) : len) / f * 60.f); + f /= std::max(1.f, e_speed / m_filpar[m_current_tool].max_e_speed); + } + m_gcode += set_format_F(f); + } m_current_pos.x = x; m_current_pos.y = y; @@ -176,7 +182,7 @@ public: return *this; } - Writer& extrude_explicit(const WipeTower::xy &dest, float e, float f = 0.f, bool record_length = false) + Writer& extrude_explicit(const WipeTower::xy &dest, float e, float f = 0.f, bool record_length = false, bool limit_volumetric_flow = true) { return extrude_explicit(dest.x, dest.y, e, f, record_length); } // Travel to a new XY position. f=0 means use the current value. @@ -273,8 +279,8 @@ public: // extrude quickly amount e to x2 with feed f. Writer& ram(float x1, float x2, float dy, float e0, float e, float f) { - extrude_explicit(x1, m_current_pos.y + dy, e0, f, true); - extrude_explicit(x2, m_current_pos.y, e, 0.f, true); + extrude_explicit(x1, m_current_pos.y + dy, e0, f, true, false); + extrude_explicit(x2, m_current_pos.y, e, 0.f, true, false); return *this; } @@ -422,6 +428,7 @@ private: const float m_default_analyzer_line_width; float m_used_filament_length = 0.f; GCodeFlavor m_gcode_flavor; + const std::vector& m_filpar; std::string set_format_X(float x) { @@ -528,15 +535,14 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( const float prime_section_width = std::min(240.f / tools.size(), 60.f); box_coordinates cleaning_box(xy(5.f, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f); - PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor); + PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); writer.set_extrusion_flow(m_extrusion_flow) .set_z(m_z_pos) .set_initial_tool(m_current_tool) .append(";--------------------\n" "; CP PRIMING START\n") .append(";--------------------\n"); - if (m_retain_speed_override) - writer.speed_override_backup(); + writer.speed_override_backup(); writer.speed_override(100); writer.set_initial_position(xy(0.f, 0.f)) // Always move to the starting position @@ -571,8 +577,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( // Reset the extruder current to a normal value. if (m_set_extruder_trimpot) writer.set_extruder_trimpot(550); - if (m_retain_speed_override) - writer.speed_override_restore(); + writer.speed_override_restore(); writer.feedrate(6000) .flush_planner_queue() .reset_extruder() @@ -630,7 +635,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo (tool != (unsigned int)(-1) ? /*m_layer_info->depth*/wipe_area+m_depth_traversed-0.5*m_perimeter_width : m_wipe_tower_depth-m_perimeter_width)); - PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor); + PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); writer.set_extrusion_flow(m_extrusion_flow) .set_z(m_z_pos) .set_initial_tool(m_current_tool) @@ -640,8 +645,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo .comment_with_value(" toolchange #", m_num_tool_changes + 1) // the number is zero-based .comment_material(m_filpar[m_current_tool].material) .append(";--------------------\n"); - if (m_retain_speed_override) - writer.speed_override_backup(); + writer.speed_override_backup(); writer.speed_override(100); xy initial_position = cleaning_box.ld + WipeTower::xy(0.f,m_depth_traversed); @@ -679,8 +683,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo if (m_set_extruder_trimpot) writer.set_extruder_trimpot(550); // Reset the extruder current to a normal value. - if (m_retain_speed_override) - writer.speed_override_restore(); + writer.speed_override_restore(); writer.feedrate(6000) .flush_planner_queue() .reset_extruder() @@ -711,7 +714,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo m_wipe_tower_width, m_wipe_tower_depth); - PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor); + PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); writer.set_extrusion_flow(m_extrusion_flow * 1.1f) .set_z(m_z_pos) // Let the writer know the current Z position as a base for Z-hop. .set_initial_tool(m_current_tool) @@ -917,19 +920,7 @@ void WipeTowerPrusaMM::toolchange_Change( if (m_current_tool < m_used_filament_length.size()) m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); - // Speed override for the material. Go slow for flex and soluble materials. - int speed_override; - switch (new_material) { - case PVA: speed_override = (m_z_pos < 0.80f) ? 60 : 80; break; - case SCAFF: speed_override = 35; break; - case FLEX: speed_override = 35; break; - default: speed_override = 100; - } writer.set_tool(new_tool); - if (m_retain_speed_override) - assert(speed_override == 100); - else - writer.speed_override(speed_override); writer.flush_planner_queue(); m_current_tool = new_tool; } @@ -1040,7 +1031,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer() // Otherwise the caller would likely travel to the wipe tower in vain. assert(! this->layer_finished()); - PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor); + PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); writer.set_extrusion_flow(m_extrusion_flow) .set_z(m_z_pos) .set_initial_tool(m_current_tool) diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/src/libslic3r/GCode/WipeTowerPrusaMM.hpp index f8adf4c5f..512733a20 100644 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.hpp +++ b/src/libslic3r/GCode/WipeTowerPrusaMM.hpp @@ -73,17 +73,12 @@ public: // Set the extruder properties. void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed, float loading_speed_start, float unloading_speed, float unloading_speed_start, float delay, int cooling_moves, - float cooling_initial_speed, float cooling_final_speed, std::string ramming_parameters, float nozzle_diameter) + float cooling_initial_speed, float cooling_final_speed, std::string ramming_parameters, float max_volumetric_speed, float nozzle_diameter) { //while (m_filpar.size() < idx+1) // makes sure the required element is in the vector m_filpar.push_back(FilamentParameters()); m_filpar[idx].material = material; - if (material == FLEX || material == SCAFF || material == PVA) { - // MMU2 lowers the print speed using the speed override (M220) for printing of soluble PVA/BVOH and flex materials. - // Therefore it does not make sense to use the new M220 B and M220 R (backup / restore). - m_retain_speed_override = false; - } m_filpar[idx].temperature = temp; m_filpar[idx].first_layer_temperature = first_layer_temp; m_filpar[idx].loading_speed = loading_speed; @@ -94,6 +89,8 @@ public: m_filpar[idx].cooling_moves = cooling_moves; m_filpar[idx].cooling_initial_speed = cooling_initial_speed; m_filpar[idx].cooling_final_speed = cooling_final_speed; + if (max_volumetric_speed != 0.f) + m_filpar[idx].max_e_speed = (max_volumetric_speed / Filament_Area); m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter @@ -188,6 +185,24 @@ public: virtual std::vector get_used_filament() const override { return m_used_filament_length; } virtual int get_number_of_toolchanges() const override { return m_num_tool_changes; } + struct FilamentParameters { + material_type material = PLA; + int temperature = 0; + int first_layer_temperature = 0; + float loading_speed = 0.f; + float loading_speed_start = 0.f; + float unloading_speed = 0.f; + float unloading_speed_start = 0.f; + float delay = 0.f ; + int cooling_moves = 0; + float cooling_initial_speed = 0.f; + float cooling_final_speed = 0.f; + float ramming_line_width_multiplicator = 0.f; + float ramming_step_multiplicator = 0.f; + float max_e_speed = std::numeric_limits::max(); + std::vector ramming_speed; + float nozzle_diameter; + }; private: WipeTowerPrusaMM(); @@ -224,32 +239,12 @@ private: float m_extra_loading_move = 0.f; float m_bridging = 0.f; bool m_set_extruder_trimpot = false; - bool m_retain_speed_override = true; bool m_adhesion = true; GCodeFlavor m_gcode_flavor; float m_perimeter_width = 0.4f * Width_To_Nozzle_Ratio; // Width of an extrusion line, also a perimeter spacing for 100% infill. float m_extrusion_flow = 0.038f; //0.029f;// Extrusion flow is derived from m_perimeter_width, layer height and filament diameter. - - struct FilamentParameters { - material_type material = PLA; - int temperature = 0; - int first_layer_temperature = 0; - float loading_speed = 0.f; - float loading_speed_start = 0.f; - float unloading_speed = 0.f; - float unloading_speed_start = 0.f; - float delay = 0.f ; - int cooling_moves = 0; - float cooling_initial_speed = 0.f; - float cooling_final_speed = 0.f; - float ramming_line_width_multiplicator = 0.f; - float ramming_step_multiplicator = 0.f; - std::vector ramming_speed; - float nozzle_diameter; - }; - // Extruder specific parameters. std::vector m_filpar; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index f9129f15a..02e130573 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -121,7 +121,6 @@ bool Print::invalidate_state_by_config_options(const std::vector( From da1aea889f3cb0390b7a752b42065f6021829f84 Mon Sep 17 00:00:00 2001 From: Thomas Moore Date: Fri, 3 May 2019 00:17:24 -0400 Subject: [PATCH 03/72] Enable wipe tower for all multi-extruder configurations --- src/libslic3r/GCode.cpp | 83 ++++++++++++++++-------- src/libslic3r/GCode.hpp | 4 +- src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 76 ++++++++++++---------- src/libslic3r/Print.cpp | 81 ++++++++++++++++------- src/slic3r/GUI/GLCanvas3D.cpp | 5 +- src/slic3r/GUI/Tab.cpp | 7 +- 6 files changed, 166 insertions(+), 90 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 436628d8a..d6355e4d2 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -167,7 +167,7 @@ static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const Wipe return Point(scale_(wipe_tower_pt.x - gcodegen.origin()(0)), scale_(wipe_tower_pt.y - gcodegen.origin()(1))); } -std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const +std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, float print_z) const { std::string gcode; @@ -195,24 +195,57 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T "Travel to a Wipe Tower"); gcode += gcodegen.unretract(); - // Let the tool change be executed by the wipe tower class. - // Inform the G-code writer about the changes done behind its back. - gcode += tcr_rotated_gcode; - // Let the m_writer know the current extruder_id, but ignore the generated G-code. - if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id)) - gcodegen.writer().toolchange(new_extruder_id); - gcodegen.placeholder_parser().set("current_extruder", new_extruder_id); - - // Always append the filament start G-code even if the extruder did not switch, - // because the wipe tower resets the linear advance and we want it to be re-enabled. + // Process the end filament gcode. + std::string end_filament_gcode_str; + if (gcodegen.writer().extruder() != nullptr) { + // Process the custom end_filament_gcode in case of single_extruder_multi_material. + unsigned int old_extruder_id = gcodegen.writer().extruder()->id(); + const std::string &end_filament_gcode = gcodegen.config().end_filament_gcode.get_at(old_extruder_id); + if (gcodegen.writer().extruder() != nullptr && ! end_filament_gcode.empty()) { + end_filament_gcode_str = gcodegen.placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id); + check_add_eol(end_filament_gcode_str); + } + } + + // Process the tool chagne gcode. + std::string toolchange_gcode_str; + const std::string &toolchange_gcode = gcodegen.config().toolchange_gcode.value; + if (gcodegen.writer().extruder() != nullptr && ! toolchange_gcode.empty()) { + // Process the custom toolchange_gcode. + DynamicConfig config; + config.set_key_value("previous_extruder", new ConfigOptionInt((int)gcodegen.writer().extruder()->id())); + config.set_key_value("next_extruder", new ConfigOptionInt((int)new_extruder_id)); + config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); + toolchange_gcode_str = gcodegen.placeholder_parser_process("toolchange_gcode", toolchange_gcode, new_extruder_id, &config); + check_add_eol(toolchange_gcode_str); + } + + // Process the start filament gcode. + std::string start_filament_gcode_str; const std::string &start_filament_gcode = gcodegen.config().start_filament_gcode.get_at(new_extruder_id); if (! start_filament_gcode.empty()) { // Process the start_filament_gcode for the active filament only. DynamicConfig config; config.set_key_value("filament_extruder_id", new ConfigOptionInt(new_extruder_id)); - gcode += gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config); - check_add_eol(gcode); + start_filament_gcode_str = gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config); + check_add_eol(start_filament_gcode_str); } + + // Insert the end filament, toolchange, and start filament gcode. + DynamicConfig config; + config.set_key_value("end_filament_gcode", new ConfigOptionString(end_filament_gcode_str)); + config.set_key_value("toolchange_gcode", new ConfigOptionString(toolchange_gcode_str)); + config.set_key_value("start_filament_gcode", new ConfigOptionString(start_filament_gcode_str)); + std::string tcr_gcode, tcr_escaped_gcode = gcodegen.placeholder_parser_process("tcr_rotated_gcode", tcr_rotated_gcode, new_extruder_id, &config); + unescape_string_cstyle(tcr_escaped_gcode, tcr_gcode); + gcode += tcr_gcode; + check_add_eol(toolchange_gcode_str); + + // Let the m_writer know the current extruder_id, but ignore the generated G-code. + if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id)) + gcodegen.writer().toolchange(new_extruder_id); + // A phony move to the end position at the wipe tower. gcodegen.writer().travel_to_xy(Vec2d(end_pos.x, end_pos.y)); gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos)); @@ -313,14 +346,14 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen) return gcode; } -std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, bool finish_layer) +std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, bool finish_layer, float print_z) { std::string gcode; assert(m_layer_idx >= 0 && m_layer_idx <= m_tool_changes.size()); if (! m_brim_done || gcodegen.writer().need_toolchange(extruder_id) || finish_layer) { if (m_layer_idx < m_tool_changes.size()) { assert(m_tool_change_idx < m_tool_changes[m_layer_idx].size()); - gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id); + gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id, print_z); } m_brim_done = true; } @@ -333,7 +366,7 @@ std::string WipeTowerIntegration::finalize(GCode &gcodegen) std::string gcode; if (std::abs(gcodegen.writer().get_position()(2) - m_final_purge.print_z) > EPSILON) gcode += gcodegen.change_layer(m_final_purge.print_z); - gcode += append_tcr(gcodegen, m_final_purge, -1); + gcode += append_tcr(gcodegen, m_final_purge, -1, m_final_purge.print_z); return gcode; } @@ -805,15 +838,13 @@ void GCode::_do_export(Print &print, FILE *file) // Write the custom start G-code _writeln(file, start_gcode); // Process filament-specific gcode in extruder order. - if (print.config().single_extruder_multi_material) { - if (has_wipe_tower) { - // Wipe tower will control the extruder switching, it will call the start_filament_gcode. - } else { - // Only initialize the initial extruder. - DynamicConfig config; - config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(initial_extruder_id))); - _writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config)); - } + if (has_wipe_tower) { + // Wipe tower will control the extruder switching, it will call the start_filament_gcode. + } else if (print.config().single_extruder_multi_material) { + // Only initialize the initial extruder. + DynamicConfig config; + config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(initial_extruder_id))); + _writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config)); } else { DynamicConfig config; for (const std::string &start_gcode : print.config().start_filament_gcode.values) { @@ -1598,7 +1629,7 @@ void GCode::process_layer( for (unsigned int extruder_id : layer_tools.extruders) { gcode += (layer_tools.has_wipe_tower && m_wipe_tower) ? - m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()) : + m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back(), print_z) : this->set_extruder(extruder_id, print_z); // let analyzer tag generator aware of a role type change diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 21957d32c..94f5a76aa 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -99,13 +99,13 @@ public: std::string prime(GCode &gcodegen); void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; } - std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer); + std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer, float print_z); std::string finalize(GCode &gcodegen); std::vector used_filament_length() const; private: WipeTowerIntegration& operator=(const WipeTowerIntegration&); - std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const; + std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, float print_z) const; // Postprocesses gcode: rotates and moves all G1 extrusions and returns result std::string rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const; diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index 5165ac358..dfcecc46b 100644 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -857,19 +857,21 @@ void WipeTowerPrusaMM::toolchange_Unload( // Retraction: float old_x = writer.x(); float turning_point = (!m_left_to_right ? xl : xr ); - float total_retraction_distance = m_cooling_tube_retraction + m_cooling_tube_length/2.f - 15.f; // the 15mm is reserved for the first part after ramming - writer.suppress_preview() - .retract(15.f, m_filpar[m_current_tool].unloading_speed_start * 60.f) // feedrate 5000mm/min = 83mm/s - .retract(0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed * 60.f) - .retract(0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed * 60.f) - .retract(0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed * 60.f) - - /*.load_move_x_advanced(turning_point, -15.f, 83.f, 50.f) // this is done at fixed speed - .load_move_x_advanced(old_x, -0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed) - .load_move_x_advanced(turning_point, -0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed) - .load_move_x_advanced(old_x, -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed) - .travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate*/ - .resume_preview(); + if ((m_cooling_tube_retraction != 0 || m_cooling_tube_length != 0) && m_filpar[m_current_tool].unloading_speed_start != 0 && m_filpar[m_current_tool].unloading_speed != 0) { + float total_retraction_distance = m_cooling_tube_retraction + m_cooling_tube_length/2.f - 15.f; // the 15mm is reserved for the first part after ramming + writer.suppress_preview() + .retract(15.f, m_filpar[m_current_tool].unloading_speed_start * 60.f) // feedrate 5000mm/min = 83mm/s + .retract(0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed * 60.f) + .retract(0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed * 60.f) + .retract(0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed * 60.f) + + /*.load_move_x_advanced(turning_point, -15.f, 83.f, 50.f) // this is done at fixed speed + .load_move_x_advanced(old_x, -0.70f * total_retraction_distance, 1.0f * m_filpar[m_current_tool].unloading_speed) + .load_move_x_advanced(turning_point, -0.20f * total_retraction_distance, 0.5f * m_filpar[m_current_tool].unloading_speed) + .load_move_x_advanced(old_x, -0.10f * total_retraction_distance, 0.3f * m_filpar[m_current_tool].unloading_speed) + .travel(old_x, writer.y()) // in case previous move was shortened to limit feedrate*/ + .resume_preview(); + } if (new_temperature != 0 && (new_temperature != m_old_temperature || m_is_first_layer) ) { // Set the extruder temperature, but don't wait. // If the required temperature is the same as last time, don't emit the M104 again (if user adjusted the value, it would be reset) // However, always change temperatures on the first layer (this is to avoid issues with priming lines turned off). @@ -920,7 +922,11 @@ void WipeTowerPrusaMM::toolchange_Change( if (m_current_tool < m_used_filament_length.size()) m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); + writer.append("[end_filament_gcode]\n"); + writer.append("[toolchange_gcode]\n"); writer.set_tool(new_tool); + writer.append("[start_filament_gcode]\n"); + writer.flush_planner_queue(); m_current_tool = new_tool; } @@ -928,32 +934,34 @@ void WipeTowerPrusaMM::toolchange_Change( void WipeTowerPrusaMM::toolchange_Load( PrusaMultiMaterial::Writer &writer, const box_coordinates &cleaning_box) -{ - float xl = cleaning_box.ld.x + m_perimeter_width * 0.75f; - float xr = cleaning_box.rd.x - m_perimeter_width * 0.75f; - float oldx = writer.x(); // the nozzle is in place to do the first wiping moves, we will remember the position +{ + if ((m_parking_pos_retraction != 0 || m_extra_loading_move != 0) && m_filpar[m_current_tool].loading_speed_start != 0 && m_filpar[m_current_tool].loading_speed != 0) { + float xl = cleaning_box.ld.x + m_perimeter_width * 0.75f; + float xr = cleaning_box.rd.x - m_perimeter_width * 0.75f; + float oldx = writer.x(); // the nozzle is in place to do the first wiping moves, we will remember the position - // Load the filament while moving left / right, so the excess material will not create a blob at a single position. - float turning_point = ( oldx-xl < xr-oldx ? xr : xl ); - float edist = m_parking_pos_retraction+m_extra_loading_move; + // Load the filament while moving left / right, so the excess material will not create a blob at a single position. + float turning_point = ( oldx-xl < xr-oldx ? xr : xl ); + float edist = m_parking_pos_retraction+m_extra_loading_move; - writer.append("; CP TOOLCHANGE LOAD\n") - .suppress_preview() - /*.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Acceleration - .load_move_x_advanced(oldx, 0.5f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase - .load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Slowing down - .load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/ + writer.append("; CP TOOLCHANGE LOAD\n") + .suppress_preview() + /*.load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Acceleration + .load_move_x_advanced(oldx, 0.5f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase + .load_move_x_advanced(turning_point, 0.2f * edist, 0.3f * m_filpar[m_current_tool].loading_speed) // Slowing down + .load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/ - .load(0.2f * edist, 60.f * m_filpar[m_current_tool].loading_speed_start) - .load_move_x_advanced(turning_point, 0.7f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase - .load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/ + .load(0.2f * edist, 60.f * m_filpar[m_current_tool].loading_speed_start) + .load_move_x_advanced(turning_point, 0.7f * edist, m_filpar[m_current_tool].loading_speed) // Fast phase + .load_move_x_advanced(oldx, 0.1f * edist, 0.1f * m_filpar[m_current_tool].loading_speed) // Super slow*/ - .travel(oldx, writer.y()) // in case last move was shortened to limit x feedrate - .resume_preview(); + .travel(oldx, writer.y()) // in case last move was shortened to limit x feedrate + .resume_preview(); - // Reset the extruder current to the normal value. - if (m_set_extruder_trimpot) - writer.set_extruder_trimpot(550); + // Reset the extruder current to the normal value. + if (m_set_extruder_trimpot) + writer.set_extruder_trimpot(550); + } } // Wipe the newly loaded filament until the end of the assigned wipe area. diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 02e130573..d09fd2bfe 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1211,17 +1211,15 @@ std::string Print::validate() const return L("The Spiral Vase option can only be used when printing single material objects."); } - if (m_config.single_extruder_multi_material) { - for (size_t i=1; ihas_wipe_tower() && ! m_objects.empty()) { if (m_config.gcode_flavor != gcfRepRap && m_config.gcode_flavor != gcfRepetier && m_config.gcode_flavor != gcfMarlin) return L("The Wipe Tower is currently only supported for the Marlin, RepRap/Sprinter and Repetier G-code flavors."); if (! m_config.use_relative_e_distances) return L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1)."); + + for (size_t i=1; i 1) { bool has_custom_layering = false; @@ -1730,7 +1728,6 @@ void Print::_make_brim() bool Print::has_wipe_tower() const { return - m_config.single_extruder_multi_material.value && ! m_config.spiral_vase.value && m_config.wipe_tower.value && m_config.nozzle_diameter.values.size() > 1; @@ -1792,38 +1789,78 @@ void Print::_make_wipe_tower() } } this->throw_if_canceled(); + + bool semm = m_config.single_extruder_multi_material.value; + float cooling_tube_retraction = 0.0; + float cooling_tube_length = 0.0; + float parking_pos_retraction = 0.0; + bool extra_loading_move = 0.0; + bool high_current_on_filament_swap = false; + + if (semm) { + cooling_tube_retraction = float(m_config.cooling_tube_retraction.value); + cooling_tube_length = float(m_config.cooling_tube_length.value); + parking_pos_retraction = float(m_config.parking_pos_retraction.value); + extra_loading_move = float(m_config.extra_loading_move.value); + high_current_on_filament_swap = m_config.high_current_on_filament_swap.value; + } // Initialize the wipe tower. WipeTowerPrusaMM wipe_tower( float(m_config.wipe_tower_x.value), float(m_config.wipe_tower_y.value), float(m_config.wipe_tower_width.value), - float(m_config.wipe_tower_rotation_angle.value), float(m_config.cooling_tube_retraction.value), - float(m_config.cooling_tube_length.value), float(m_config.parking_pos_retraction.value), - float(m_config.extra_loading_move.value), float(m_config.wipe_tower_bridging), - m_config.high_current_on_filament_swap.value, m_config.gcode_flavor, wipe_volumes, + float(m_config.wipe_tower_rotation_angle.value), cooling_tube_retraction, + cooling_tube_length, parking_pos_retraction, + extra_loading_move, float(m_config.wipe_tower_bridging), + high_current_on_filament_swap, m_config.gcode_flavor, wipe_volumes, m_wipe_tower_data.tool_ordering.first_extruder()); //wipe_tower.set_retract(); //wipe_tower.set_zhop(); // Set the extruder & material properties at the wipe tower object. - for (size_t i = 0; i < number_of_extruders; ++ i) + for (size_t i = 0; i < number_of_extruders; ++ i) { + float loading_speed = 0.0; + float loading_speed_start = 0.0; + float unloading_speed = 0.0; + float unloading_speed_start = 0.0; + float toolchange_delay = 0.0; + int cooling_moves = 0; + float cooling_initial_speed = 0.0; + float cooling_final_speed = 0.0; + float max_volumetric_speed = 0.f; + std::string ramming_parameters; + + if (semm) { + loading_speed = m_config.filament_loading_speed.get_at(i); + loading_speed_start = m_config.filament_loading_speed_start.get_at(i); + unloading_speed = m_config.filament_unloading_speed.get_at(i); + unloading_speed_start = m_config.filament_unloading_speed_start.get_at(i); + toolchange_delay = m_config.filament_toolchange_delay.get_at(i); + cooling_moves = m_config.filament_cooling_moves.get_at(i); + cooling_initial_speed = m_config.filament_cooling_initial_speed.get_at(i); + cooling_final_speed = m_config.filament_cooling_final_speed.get_at(i); + ramming_parameters = m_config.filament_ramming_parameters.get_at(i); + max_volumetric_speed = m_config.filament_max_volumetric_speed.get_at(i); + } + wipe_tower.set_extruder( i, WipeTowerPrusaMM::parse_material(m_config.filament_type.get_at(i).c_str()), m_config.temperature.get_at(i), m_config.first_layer_temperature.get_at(i), - m_config.filament_loading_speed.get_at(i), - m_config.filament_loading_speed_start.get_at(i), - m_config.filament_unloading_speed.get_at(i), - m_config.filament_unloading_speed_start.get_at(i), - m_config.filament_toolchange_delay.get_at(i), - m_config.filament_cooling_moves.get_at(i), - m_config.filament_cooling_initial_speed.get_at(i), - m_config.filament_cooling_final_speed.get_at(i), - m_config.filament_ramming_parameters.get_at(i), - m_config.filament_max_volumetric_speed.get_at(i), + loading_speed, + loading_speed_start, + unloading_speed, + unloading_speed_start, + toolchange_delay, + cooling_moves, + cooling_initial_speed, + cooling_final_speed, + ramming_parameters, + max_volumetric_speed, m_config.nozzle_diameter.get_at(i)); + } m_wipe_tower_data.priming = Slic3r::make_unique( wipe_tower.prime(this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false)); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 21f1d23cd..216848477 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2049,11 +2049,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // Should the wipe tower be visualized ? unsigned int extruders_count = (unsigned int)dynamic_cast(m_config->option("nozzle_diameter"))->values.size(); - bool semm = dynamic_cast(m_config->option("single_extruder_multi_material"))->value; bool wt = dynamic_cast(m_config->option("wipe_tower"))->value; bool co = dynamic_cast(m_config->option("complete_objects"))->value; - if ((extruders_count > 1) && semm && wt && !co) + if ((extruders_count > 1) && wt && !co) { // Height of a print (Show at least a slab) double height = std::max(m_model->bounding_box().max(2), 10.0); @@ -5466,7 +5465,7 @@ void GLCanvas3D::_load_fff_shells() double max_z = print->objects()[0]->model_object()->get_model()->bounding_box().max(2); const PrintConfig& config = print->config(); unsigned int extruders_count = config.nozzle_diameter.size(); - if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) { + if ((extruders_count > 1) && config.wipe_tower && !config.complete_objects) { float depth = print->get_wipe_tower_depth(); // Calculate wipe tower brim spacing. diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 6cd32d397..078293d67 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -874,11 +874,10 @@ void Tab::update_wiping_button_visibility() { return; // ys_FIXME bool wipe_tower_enabled = dynamic_cast( (m_preset_bundle->prints.get_edited_preset().config ).option("wipe_tower"))->value; bool multiple_extruders = dynamic_cast((m_preset_bundle->printers.get_edited_preset().config).option("nozzle_diameter"))->values.size() > 1; - bool single_extruder_mm = dynamic_cast( (m_preset_bundle->printers.get_edited_preset().config).option("single_extruder_multi_material"))->value; auto wiping_dialog_button = wxGetApp().sidebar().get_wiping_dialog_button(); if (wiping_dialog_button) { - wiping_dialog_button->Show(wipe_tower_enabled && multiple_extruders && single_extruder_mm); + wiping_dialog_button->Show(wipe_tower_enabled && multiple_extruders); wiping_dialog_button->GetParent()->Layout(); } } @@ -1557,6 +1556,9 @@ void TabFilament::build() }; optgroup->append_line(line); + optgroup = page->new_optgroup(_(L("Wipe tower parameters"))); + optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower"); + optgroup = page->new_optgroup(_(L("Toolchange parameters with single extruder MM printers"))); optgroup->append_single_option_line("filament_loading_speed_start"); optgroup->append_single_option_line("filament_loading_speed"); @@ -1568,7 +1570,6 @@ void TabFilament::build() optgroup->append_single_option_line("filament_cooling_moves"); optgroup->append_single_option_line("filament_cooling_initial_speed"); optgroup->append_single_option_line("filament_cooling_final_speed"); - optgroup->append_single_option_line("filament_minimal_purge_on_wipe_tower"); line = optgroup->create_single_option_line("filament_ramming_parameters");// { _(L("Ramming")), "" }; line.widget = [this](wxWindow* parent) { From 9df93c012506763b7348b60fa14d9f3883018347 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 12 Jun 2019 10:54:52 +0200 Subject: [PATCH 04/72] Mostly refactoring of the wipe tower improvements - setting of the wipe tower parameters based od whether SE MM printer is selected is done in the WipeTowerPrusaMM constructor, so it does not distract in Print.cpp - WipeTowerPrusaMM.cpp conditions checking for SE MM printer are now using a more descriptive const member variable, not the loading/unloading speeds (hopefully the functionality is the same) --- src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 4 +- src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 51 ++++++++++------- src/libslic3r/Print.cpp | 72 ++++++------------------ 3 files changed, 51 insertions(+), 76 deletions(-) diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index dfcecc46b..2a74d8248 100644 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -857,7 +857,7 @@ void WipeTowerPrusaMM::toolchange_Unload( // Retraction: float old_x = writer.x(); float turning_point = (!m_left_to_right ? xl : xr ); - if ((m_cooling_tube_retraction != 0 || m_cooling_tube_length != 0) && m_filpar[m_current_tool].unloading_speed_start != 0 && m_filpar[m_current_tool].unloading_speed != 0) { + if (m_semm && (m_cooling_tube_retraction != 0 || m_cooling_tube_length != 0)) { float total_retraction_distance = m_cooling_tube_retraction + m_cooling_tube_length/2.f - 15.f; // the 15mm is reserved for the first part after ramming writer.suppress_preview() .retract(15.f, m_filpar[m_current_tool].unloading_speed_start * 60.f) // feedrate 5000mm/min = 83mm/s @@ -935,7 +935,7 @@ void WipeTowerPrusaMM::toolchange_Load( PrusaMultiMaterial::Writer &writer, const box_coordinates &cleaning_box) { - if ((m_parking_pos_retraction != 0 || m_extra_loading_move != 0) && m_filpar[m_current_tool].loading_speed_start != 0 && m_filpar[m_current_tool].loading_speed != 0) { + if (m_semm && (m_parking_pos_retraction != 0 || m_extra_loading_move != 0)) { float xl = cleaning_box.ld.x + m_perimeter_width * 0.75f; float xr = cleaning_box.rd.x - m_perimeter_width * 0.75f; float oldx = writer.x(); // the nozzle is in place to do the first wiping moves, we will remember the position diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/src/libslic3r/GCode/WipeTowerPrusaMM.hpp index 512733a20..8e1e494a7 100644 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.hpp +++ b/src/libslic3r/GCode/WipeTowerPrusaMM.hpp @@ -46,26 +46,32 @@ public: // y -- y coordinates of wipe tower in mm ( left bottom corner ) // width -- width of wipe tower in mm ( default 60 mm - leave as it is ) // wipe_area -- space available for one toolchange in mm - WipeTowerPrusaMM(float x, float y, float width, float rotation_angle, float cooling_tube_retraction, + WipeTowerPrusaMM(bool semm, float x, float y, float width, float rotation_angle, float cooling_tube_retraction, float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, float bridging, bool set_extruder_trimpot, GCodeFlavor flavor, const std::vector>& wiping_matrix, unsigned int initial_tool) : - m_wipe_tower_pos(x, y), + m_semm(semm), + m_wipe_tower_pos(x, y), m_wipe_tower_width(width), m_wipe_tower_rotation_angle(rotation_angle), m_y_shift(0.f), m_z_pos(0.f), m_is_first_layer(false), - m_cooling_tube_retraction(cooling_tube_retraction), - m_cooling_tube_length(cooling_tube_length), - m_parking_pos_retraction(parking_pos_retraction), - m_extra_loading_move(extra_loading_move), - m_bridging(bridging), - m_set_extruder_trimpot(set_extruder_trimpot), m_gcode_flavor(flavor), + m_bridging(bridging), m_current_tool(initial_tool), wipe_volumes(wiping_matrix) - {} + { + // If this is a single extruder MM printer, we will use all the SE-specific config values. + // Otherwise, the defaults will be used to turn off the SE stuff. + if (m_semm) { + m_cooling_tube_retraction = cooling_tube_retraction; + m_cooling_tube_length = cooling_tube_length; + m_parking_pos_retraction = parking_pos_retraction; + m_extra_loading_move = extra_loading_move; + m_set_extruder_trimpot = set_extruder_trimpot; + } + } virtual ~WipeTowerPrusaMM() {} @@ -81,21 +87,27 @@ public: m_filpar[idx].material = material; m_filpar[idx].temperature = temp; m_filpar[idx].first_layer_temperature = first_layer_temp; - m_filpar[idx].loading_speed = loading_speed; - m_filpar[idx].loading_speed_start = loading_speed_start; - m_filpar[idx].unloading_speed = unloading_speed; - m_filpar[idx].unloading_speed_start = unloading_speed_start; - m_filpar[idx].delay = delay; - m_filpar[idx].cooling_moves = cooling_moves; - m_filpar[idx].cooling_initial_speed = cooling_initial_speed; - m_filpar[idx].cooling_final_speed = cooling_final_speed; + + // If this is a single extruder MM printer, we will use all the SE-specific config values. + // Otherwise, the defaults will be used to turn off the SE stuff. + if (m_semm) { + m_filpar[idx].loading_speed = loading_speed; + m_filpar[idx].loading_speed_start = loading_speed_start; + m_filpar[idx].unloading_speed = unloading_speed; + m_filpar[idx].unloading_speed_start = unloading_speed_start; + m_filpar[idx].delay = delay; + m_filpar[idx].cooling_moves = cooling_moves; + m_filpar[idx].cooling_initial_speed = cooling_initial_speed; + m_filpar[idx].cooling_final_speed = cooling_final_speed; + } + if (max_volumetric_speed != 0.f) m_filpar[idx].max_e_speed = (max_volumetric_speed / Filament_Area); m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM m_perimeter_width = nozzle_diameter * Width_To_Nozzle_Ratio; // all extruders are now assumed to have the same diameter - std::stringstream stream{ramming_parameters}; + std::stringstream stream{m_semm ? ramming_parameters : std::string()}; float speed = 0.f; stream >> m_filpar[idx].ramming_line_width_multiplicator >> m_filpar[idx].ramming_step_multiplicator; m_filpar[idx].ramming_line_width_multiplicator /= 100; @@ -220,7 +232,8 @@ private: const float WT_EPSILON = 1e-3f; - xy m_wipe_tower_pos; // Left front corner of the wipe tower in mm. + bool m_semm = true; // Are we using a single extruder multimaterial printer? + xy m_wipe_tower_pos; // Left front corner of the wipe tower in mm. float m_wipe_tower_width; // Width of the wipe tower. float m_wipe_tower_depth = 0.f; // Depth of the wipe tower float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index d09fd2bfe..0b73f56e8 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1789,78 +1789,40 @@ void Print::_make_wipe_tower() } } this->throw_if_canceled(); - - bool semm = m_config.single_extruder_multi_material.value; - float cooling_tube_retraction = 0.0; - float cooling_tube_length = 0.0; - float parking_pos_retraction = 0.0; - bool extra_loading_move = 0.0; - bool high_current_on_filament_swap = false; - - if (semm) { - cooling_tube_retraction = float(m_config.cooling_tube_retraction.value); - cooling_tube_length = float(m_config.cooling_tube_length.value); - parking_pos_retraction = float(m_config.parking_pos_retraction.value); - extra_loading_move = float(m_config.extra_loading_move.value); - high_current_on_filament_swap = m_config.high_current_on_filament_swap.value; - } // Initialize the wipe tower. WipeTowerPrusaMM wipe_tower( + m_config.single_extruder_multi_material.value, float(m_config.wipe_tower_x.value), float(m_config.wipe_tower_y.value), float(m_config.wipe_tower_width.value), - float(m_config.wipe_tower_rotation_angle.value), cooling_tube_retraction, - cooling_tube_length, parking_pos_retraction, - extra_loading_move, float(m_config.wipe_tower_bridging), - high_current_on_filament_swap, m_config.gcode_flavor, wipe_volumes, + float(m_config.wipe_tower_rotation_angle.value), float(m_config.cooling_tube_retraction.value), + float(m_config.cooling_tube_length.value), float(m_config.parking_pos_retraction.value), + float(m_config.extra_loading_move.value), float(m_config.wipe_tower_bridging), + m_config.high_current_on_filament_swap.value, m_config.gcode_flavor, wipe_volumes, m_wipe_tower_data.tool_ordering.first_extruder()); //wipe_tower.set_retract(); //wipe_tower.set_zhop(); // Set the extruder & material properties at the wipe tower object. - for (size_t i = 0; i < number_of_extruders; ++ i) { - float loading_speed = 0.0; - float loading_speed_start = 0.0; - float unloading_speed = 0.0; - float unloading_speed_start = 0.0; - float toolchange_delay = 0.0; - int cooling_moves = 0; - float cooling_initial_speed = 0.0; - float cooling_final_speed = 0.0; - float max_volumetric_speed = 0.f; - std::string ramming_parameters; - - if (semm) { - loading_speed = m_config.filament_loading_speed.get_at(i); - loading_speed_start = m_config.filament_loading_speed_start.get_at(i); - unloading_speed = m_config.filament_unloading_speed.get_at(i); - unloading_speed_start = m_config.filament_unloading_speed_start.get_at(i); - toolchange_delay = m_config.filament_toolchange_delay.get_at(i); - cooling_moves = m_config.filament_cooling_moves.get_at(i); - cooling_initial_speed = m_config.filament_cooling_initial_speed.get_at(i); - cooling_final_speed = m_config.filament_cooling_final_speed.get_at(i); - ramming_parameters = m_config.filament_ramming_parameters.get_at(i); - max_volumetric_speed = m_config.filament_max_volumetric_speed.get_at(i); - } - + for (size_t i = 0; i < number_of_extruders; ++ i) + wipe_tower.set_extruder( i, WipeTowerPrusaMM::parse_material(m_config.filament_type.get_at(i).c_str()), m_config.temperature.get_at(i), m_config.first_layer_temperature.get_at(i), - loading_speed, - loading_speed_start, - unloading_speed, - unloading_speed_start, - toolchange_delay, - cooling_moves, - cooling_initial_speed, - cooling_final_speed, - ramming_parameters, - max_volumetric_speed, + m_config.filament_loading_speed.get_at(i), + m_config.filament_loading_speed_start.get_at(i), + m_config.filament_unloading_speed.get_at(i), + m_config.filament_unloading_speed_start.get_at(i), + m_config.filament_toolchange_delay.get_at(i), + m_config.filament_cooling_moves.get_at(i), + m_config.filament_cooling_initial_speed.get_at(i), + m_config.filament_cooling_final_speed.get_at(i), + m_config.filament_ramming_parameters.get_at(i), + m_config.filament_max_volumetric_speed.get_at(i), m_config.nozzle_diameter.get_at(i)); - } m_wipe_tower_data.priming = Slic3r::make_unique( wipe_tower.prime(this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false)); From 41164a9cb3ce34f26f4a5d4424423fb6639f87eb Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 12 Jun 2019 15:47:05 +0200 Subject: [PATCH 05/72] Multimaterial printing: Changed the way how custom gcodes are inserted Each toolchange now emits: - end filament custom gcode - toolchange custom gcode; if not provided, a standard Tn command is inserted - start filament gcode Hopefully it is now consistent for SE/ME printers with/without the wipe tower The priming line does not work - will be fixed in the next commit --- src/libslic3r/GCode.cpp | 119 +++++++++++++---------- src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 7 +- 2 files changed, 75 insertions(+), 51 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index d6355e4d2..3cd381aca 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -195,6 +195,7 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T "Travel to a Wipe Tower"); gcode += gcodegen.unretract(); + // Process the end filament gcode. std::string end_filament_gcode_str; if (gcodegen.writer().extruder() != nullptr) { @@ -206,21 +207,36 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T check_add_eol(end_filament_gcode_str); } } - - // Process the tool chagne gcode. + + // Process the custom toolchange_gcode. If it is empty, provide a simple Tn command to change the filament. + // Otherwise, leave control to the user completely. std::string toolchange_gcode_str; - const std::string &toolchange_gcode = gcodegen.config().toolchange_gcode.value; - if (gcodegen.writer().extruder() != nullptr && ! toolchange_gcode.empty()) { - // Process the custom toolchange_gcode. - DynamicConfig config; - config.set_key_value("previous_extruder", new ConfigOptionInt((int)gcodegen.writer().extruder()->id())); - config.set_key_value("next_extruder", new ConfigOptionInt((int)new_extruder_id)); - config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); - toolchange_gcode_str = gcodegen.placeholder_parser_process("toolchange_gcode", toolchange_gcode, new_extruder_id, &config); - check_add_eol(toolchange_gcode_str); + if (gcodegen.writer().extruder() != nullptr) { + const std::string& toolchange_gcode = gcodegen.config().toolchange_gcode.value; + if (!toolchange_gcode.empty()) { + DynamicConfig config; + config.set_key_value("previous_extruder", new ConfigOptionInt((int)gcodegen.writer().extruder()->id())); + config.set_key_value("next_extruder", new ConfigOptionInt((int)new_extruder_id)); + config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); + toolchange_gcode_str = gcodegen.placeholder_parser_process("toolchange_gcode", toolchange_gcode, new_extruder_id, &config); + check_add_eol(toolchange_gcode_str); + } + + std::string toolchange_command; + if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id)) + toolchange_command = gcodegen.writer().toolchange(new_extruder_id); + if (toolchange_gcode.empty()) + toolchange_gcode_str = toolchange_command; + else { + // We have informed the m_writer about the current extruder_id, we can ignore the generated G-code. + } } - + + + + gcodegen.placeholder_parser().set("current_extruder", new_extruder_id); + // Process the start filament gcode. std::string start_filament_gcode_str; const std::string &start_filament_gcode = gcodegen.config().start_filament_gcode.get_at(new_extruder_id); @@ -231,8 +247,8 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T start_filament_gcode_str = gcodegen.placeholder_parser_process("start_filament_gcode", start_filament_gcode, new_extruder_id, &config); check_add_eol(start_filament_gcode_str); } - - // Insert the end filament, toolchange, and start filament gcode. + + // Insert the end filament, toolchange, and start filament gcode into the generated gcode. DynamicConfig config; config.set_key_value("end_filament_gcode", new ConfigOptionString(end_filament_gcode_str)); config.set_key_value("toolchange_gcode", new ConfigOptionString(toolchange_gcode_str)); @@ -242,9 +258,6 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T gcode += tcr_gcode; check_add_eol(toolchange_gcode_str); - // Let the m_writer know the current extruder_id, but ignore the generated G-code. - if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id)) - gcodegen.writer().toolchange(new_extruder_id); // A phony move to the end position at the wipe tower. gcodegen.writer().travel_to_xy(Vec2d(end_pos.x, end_pos.y)); @@ -837,22 +850,16 @@ void GCode::_do_export(Print &print, FILE *file) // Write the custom start G-code _writeln(file, start_gcode); - // Process filament-specific gcode in extruder order. - if (has_wipe_tower) { + + // Process filament-specific gcode. + /* if (has_wipe_tower) { // Wipe tower will control the extruder switching, it will call the start_filament_gcode. - } else if (print.config().single_extruder_multi_material) { - // Only initialize the initial extruder. - DynamicConfig config; - config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(initial_extruder_id))); - _writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config)); } else { - DynamicConfig config; - for (const std::string &start_gcode : print.config().start_filament_gcode.values) { - int extruder_id = (unsigned int)(&start_gcode - &print.config().start_filament_gcode.values.front()); - config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id)); - _writeln(file, this->placeholder_parser_process("start_filament_gcode", start_gcode, extruder_id, &config)); - } + DynamicConfig config; + config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(initial_extruder_id))); + _writeln(file, this->placeholder_parser_process("start_filament_gcode", print.config().start_filament_gcode.values[initial_extruder_id], initial_extruder_id, &config)); } +*/ this->_print_first_layer_extruder_temperatures(file, print, start_gcode, initial_extruder_id, true); print.throw_if_canceled(); @@ -2763,38 +2770,50 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) m_wipe.reset_path(); if (m_writer.extruder() != nullptr) { - // Process the custom end_filament_gcode in case of single_extruder_multi_material. + // Process the custom end_filament_gcode. set_extruder() is only called if there is no wipe tower + // so it should not be injected twice. unsigned int old_extruder_id = m_writer.extruder()->id(); const std::string &end_filament_gcode = m_config.end_filament_gcode.get_at(old_extruder_id); - if (m_config.single_extruder_multi_material && ! end_filament_gcode.empty()) { + if (! end_filament_gcode.empty()) { gcode += placeholder_parser_process("end_filament_gcode", end_filament_gcode, old_extruder_id); check_add_eol(gcode); } } - m_placeholder_parser.set("current_extruder", extruder_id); - - if (m_writer.extruder() != nullptr && ! m_config.toolchange_gcode.value.empty()) { - // Process the custom toolchange_gcode. - DynamicConfig config; - config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id())); - config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id)); - config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); - gcode += placeholder_parser_process("toolchange_gcode", m_config.toolchange_gcode.value, extruder_id, &config); - check_add_eol(gcode); - } // If ooze prevention is enabled, park current extruder in the nearest // standby point and set it to the standby temperature. if (m_ooze_prevention.enable && m_writer.extruder() != nullptr) gcode += m_ooze_prevention.pre_toolchange(*this); - // Append the toolchange command. - gcode += m_writer.toolchange(extruder_id); - // Append the filament start G-code for single_extruder_multi_material. + + const std::string& toolchange_gcode = m_config.toolchange_gcode.value; + if (m_writer.extruder() != nullptr) { + // Process the custom toolchange_gcode. If it is empty, insert just a Tn command. + if (!toolchange_gcode.empty()) { + DynamicConfig config; + config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id())); + config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id)); + config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); + gcode += placeholder_parser_process("toolchange_gcode", toolchange_gcode, extruder_id, &config); + check_add_eol(gcode); + } + } + + // We inform the writer about what is happening, but we may not use the resulting gcode. + std::string toolchange_command = m_writer.toolchange(extruder_id); + if (toolchange_gcode.empty()) + gcode += toolchange_command; + else { + // user provided his own toolchange gcode, no need to do anything + } + + m_placeholder_parser.set("current_extruder", extruder_id); + + // Append the filament start G-code. const std::string &start_filament_gcode = m_config.start_filament_gcode.get_at(extruder_id); - if (m_config.single_extruder_multi_material && ! start_filament_gcode.empty()) { - // Process the start_filament_gcode for the active filament only. + if (! start_filament_gcode.empty()) { + // Process the start_filament_gcode for the new filament. gcode += this->placeholder_parser_process("start_filament_gcode", start_filament_gcode, extruder_id); check_add_eol(gcode); } diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index 2a74d8248..0d0422fdc 100644 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -922,9 +922,14 @@ void WipeTowerPrusaMM::toolchange_Change( if (m_current_tool < m_used_filament_length.size()) m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); + // This is where we want to place the custom gcodes. We will use placeholders for this. + // These will be substituted by the actual gcodes when the gcode is generated. writer.append("[end_filament_gcode]\n"); writer.append("[toolchange_gcode]\n"); - writer.set_tool(new_tool); + + // The toolchange Tn command will be inserted later, only in case that the user does + // not provide a custom toolchange gcode. + //writer.set_tool(new_tool); writer.append("[start_filament_gcode]\n"); writer.flush_planner_queue(); From aee376762e508df543d9805f0233a3f987999b32 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 14 Jun 2019 12:28:24 +0200 Subject: [PATCH 06/72] Changed handling of priming extrusions to allow injection of filament and toolchange custom gcodes The priming extrusions were handled separately from the rest of the wipe tower toolchanges. In order to be able to use the logic from previous commit for them (custom toolchange gcodes etc), some unpleasant code shuffling was needed --- src/libslic3r/GCode.cpp | 81 ++++++++++------- src/libslic3r/GCode.hpp | 4 +- src/libslic3r/GCode/PrintExtents.cpp | 25 ++--- src/libslic3r/GCode/WipeTower.hpp | 8 +- src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 111 +++++++++++++++-------- src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 2 +- src/libslic3r/Print.cpp | 2 +- src/libslic3r/Print.hpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 3 +- 9 files changed, 146 insertions(+), 92 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 3cd381aca..768cec6f4 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -176,24 +176,29 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T float alpha = m_wipe_tower_rotation/180.f * float(M_PI); WipeTower::xy start_pos = tcr.start_pos; WipeTower::xy end_pos = tcr.end_pos; - start_pos.rotate(alpha); - start_pos.translate(m_wipe_tower_pos); - end_pos.rotate(alpha); - end_pos.translate(m_wipe_tower_pos); - std::string tcr_rotated_gcode = rotate_wipe_tower_moves(tcr.gcode, tcr.start_pos, m_wipe_tower_pos, alpha); + if (!tcr.priming) { + start_pos.rotate(alpha); + start_pos.translate(m_wipe_tower_pos); + end_pos.rotate(alpha); + end_pos.translate(m_wipe_tower_pos); + } + std::string tcr_rotated_gcode = tcr.priming ? tcr.gcode : rotate_wipe_tower_moves(tcr.gcode, tcr.start_pos, m_wipe_tower_pos, alpha); // Disable linear advance for the wipe tower operations. gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n")); - // Move over the wipe tower. - // Retract for a tool change, using the toolchange retract value and setting the priming extra length. - gcode += gcodegen.retract(true); - gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true; - gcode += gcodegen.travel_to( - wipe_tower_point_to_object_point(gcodegen, start_pos), - erMixed, - "Travel to a Wipe Tower"); - gcode += gcodegen.unretract(); + + if (!tcr.priming) { + // Move over the wipe tower. + // Retract for a tool change, using the toolchange retract value and setting the priming extra length. + gcode += gcodegen.retract(true); + gcodegen.m_avoid_crossing_perimeters.use_external_mp_once = true; + gcode += gcodegen.travel_to( + wipe_tower_point_to_object_point(gcodegen, start_pos), + erMixed, + "Travel to a Wipe Tower"); + gcode += gcodegen.unretract(); + } // Process the end filament gcode. @@ -211,11 +216,12 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T // Process the custom toolchange_gcode. If it is empty, provide a simple Tn command to change the filament. // Otherwise, leave control to the user completely. std::string toolchange_gcode_str; - if (gcodegen.writer().extruder() != nullptr) { + if (true /*gcodegen.writer().extruder() != nullptr*/) { const std::string& toolchange_gcode = gcodegen.config().toolchange_gcode.value; if (!toolchange_gcode.empty()) { DynamicConfig config; - config.set_key_value("previous_extruder", new ConfigOptionInt((int)gcodegen.writer().extruder()->id())); + int previous_extruder_id = gcodegen.writer().extruder() ? (int)gcodegen.writer().extruder()->id() : -1; + config.set_key_value("previous_extruder", new ConfigOptionInt(previous_extruder_id)); config.set_key_value("next_extruder", new ConfigOptionInt((int)new_extruder_id)); config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index)); config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); @@ -224,7 +230,7 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T } std::string toolchange_command; - if (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id)) + if (tcr.priming || (new_extruder_id >= 0 && gcodegen.writer().need_toolchange(new_extruder_id))) toolchange_command = gcodegen.writer().toolchange(new_extruder_id); if (toolchange_gcode.empty()) toolchange_gcode_str = toolchange_command; @@ -233,8 +239,6 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T } } - - gcodegen.placeholder_parser().set("current_extruder", new_extruder_id); // Process the start filament gcode. @@ -334,27 +338,36 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen) assert(m_layer_idx == 0); std::string gcode; - if (&m_priming != nullptr && ! m_priming.extrusions.empty()) { + if (&m_priming != nullptr) { // Disable linear advance for the wipe tower operations. - gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n")); - // Let the tool change be executed by the wipe tower class. - // Inform the G-code writer about the changes done behind its back. - gcode += m_priming.gcode; - // Let the m_writer know the current extruder_id, but ignore the generated G-code. - unsigned int current_extruder_id = m_priming.extrusions.back().tool; - gcodegen.writer().toolchange(current_extruder_id); - gcodegen.placeholder_parser().set("current_extruder", current_extruder_id); + //gcode += (gcodegen.config().gcode_flavor == gcfRepRap ? std::string("M572 D0 S0\n") : std::string("M900 K0\n")); + + for (const WipeTower::ToolChangeResult& tcr : m_priming) { + if (!tcr.extrusions.empty()) + gcode += append_tcr(gcodegen, tcr, tcr.new_tool, tcr.print_z); + + + // Let the tool change be executed by the wipe tower class. + // Inform the G-code writer about the changes done behind its back. + //gcode += tcr.gcode; + // Let the m_writer know the current extruder_id, but ignore the generated G-code. + // unsigned int current_extruder_id = tcr.extrusions.back().tool; + // gcodegen.writer().toolchange(current_extruder_id); + // gcodegen.placeholder_parser().set("current_extruder", current_extruder_id); + + } + // A phony move to the end position at the wipe tower. - gcodegen.writer().travel_to_xy(Vec2d(m_priming.end_pos.x, m_priming.end_pos.y)); - gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.end_pos)); + /* gcodegen.writer().travel_to_xy(Vec2d(m_priming.back().end_pos.x, m_priming.back().end_pos.y)); + gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, m_priming.back().end_pos)); // Prepare a future wipe. gcodegen.m_wipe.path.points.clear(); // Start the wipe at the current position. - gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, m_priming.end_pos)); + gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, m_priming.back().end_pos)); // Wipe end point: Wipe direction away from the closer tower edge to the further tower edge. - gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, - WipeTower::xy((std::abs(m_left - m_priming.end_pos.x) < std::abs(m_right - m_priming.end_pos.x)) ? m_right : m_left, - m_priming.end_pos.y))); + gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, + WipeTower::xy((std::abs(m_left - m_priming.back().end_pos.x) < std::abs(m_right - m_priming.back().end_pos.x)) ? m_right : m_left, + m_priming.back().end_pos.y)));*/ } return gcode; } diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 94f5a76aa..35f5fb485 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -83,7 +83,7 @@ class WipeTowerIntegration { public: WipeTowerIntegration( const PrintConfig &print_config, - const WipeTower::ToolChangeResult &priming, + const std::vector &priming, const std::vector> &tool_changes, const WipeTower::ToolChangeResult &final_purge) : m_left(/*float(print_config.wipe_tower_x.value)*/ 0.f), @@ -116,7 +116,7 @@ private: const WipeTower::xy m_wipe_tower_pos; const float m_wipe_tower_rotation; // Reference to cached values at the Printer class. - const WipeTower::ToolChangeResult &m_priming; + const std::vector &m_priming; const std::vector> &m_tool_changes; const WipeTower::ToolChangeResult &m_final_purge; // Current layer index. diff --git a/src/libslic3r/GCode/PrintExtents.cpp b/src/libslic3r/GCode/PrintExtents.cpp index 92a58fdf0..4ff7c1cbd 100644 --- a/src/libslic3r/GCode/PrintExtents.cpp +++ b/src/libslic3r/GCode/PrintExtents.cpp @@ -165,18 +165,19 @@ BoundingBoxf get_wipe_tower_priming_extrusions_extents(const Print &print) { BoundingBoxf bbox; if (print.wipe_tower_data().priming != nullptr) { - const WipeTower::ToolChangeResult &tcr = *print.wipe_tower_data().priming; - for (size_t i = 1; i < tcr.extrusions.size(); ++ i) { - const WipeTower::Extrusion &e = tcr.extrusions[i]; - if (e.width > 0) { - Vec2d p1((&e - 1)->pos.x, (&e - 1)->pos.y); - Vec2d p2(e.pos.x, e.pos.y); - bbox.merge(p1); - coordf_t radius = 0.5 * e.width; - bbox.min(0) = std::min(bbox.min(0), std::min(p1(0), p2(0)) - radius); - bbox.min(1) = std::min(bbox.min(1), std::min(p1(1), p2(1)) - radius); - bbox.max(0) = std::max(bbox.max(0), std::max(p1(0), p2(0)) + radius); - bbox.max(1) = std::max(bbox.max(1), std::max(p1(1), p2(1)) + radius); + for (const WipeTower::ToolChangeResult &tcr : *print.wipe_tower_data().priming) { + for (size_t i = 1; i < tcr.extrusions.size(); ++ i) { + const WipeTower::Extrusion &e = tcr.extrusions[i]; + if (e.width > 0) { + Vec2d p1((&e - 1)->pos.x, (&e - 1)->pos.y); + Vec2d p2(e.pos.x, e.pos.y); + bbox.merge(p1); + coordf_t radius = 0.5 * e.width; + bbox.min(0) = std::min(bbox.min(0), std::min(p1(0), p2(0)) - radius); + bbox.min(1) = std::min(bbox.min(1), std::min(p1(1), p2(1)) - radius); + bbox.max(0) = std::max(bbox.max(0), std::max(p1(0), p2(0)) + radius); + bbox.max(1) = std::max(bbox.max(1), std::max(p1(1), p2(1)) + radius); + } } } } diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index 8ea3abd93..ba841fdd7 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -119,6 +119,12 @@ public: // Is this a priming extrusion? (If so, the wipe tower rotation & translation will not be applied later) bool priming; + // Initial tool + int initial_tool; + + // New tool + int new_tool; + // Sum the total length of the extrusion. float total_extrusion_length_in_plane() { float e_length = 0.f; @@ -134,7 +140,7 @@ public: }; // Returns gcode to prime the nozzles at the front edge of the print bed. - virtual ToolChangeResult prime( + virtual std::vector prime( // print_z of the first layer. float first_layer_height, // Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object. diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index 0d0422fdc..692397276 100644 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -515,7 +515,7 @@ std::string WipeTowerPrusaMM::to_string(material_type material) } // Returns gcode to prime the nozzles at the front edge of the print bed. -WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( +std::vector WipeTowerPrusaMM::prime( // print_z of the first layer. float first_layer_height, // Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object. @@ -535,22 +535,34 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( const float prime_section_width = std::min(240.f / tools.size(), 60.f); box_coordinates cleaning_box(xy(5.f, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f); - PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); - writer.set_extrusion_flow(m_extrusion_flow) - .set_z(m_z_pos) - .set_initial_tool(m_current_tool) - .append(";--------------------\n" - "; CP PRIMING START\n") - .append(";--------------------\n"); - writer.speed_override_backup(); - writer.speed_override(100); - writer.set_initial_position(xy(0.f, 0.f)) // Always move to the starting position - .travel(cleaning_box.ld, 7200); - if (m_set_extruder_trimpot) - writer.set_extruder_trimpot(750); // Increase the extruder driver current to allow fast ramming. + std::vector results; + // Iterate over all priming toolchanges and push respective ToolChangeResults into results vector. for (size_t idx_tool = 0; idx_tool < tools.size(); ++ idx_tool) { + int old_tool = m_current_tool; + + PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); + writer.set_extrusion_flow(m_extrusion_flow) + .set_z(m_z_pos) + .set_initial_tool(m_current_tool); + + // This is the first toolchange - initiate priming + if (idx_tool == 0) { + writer.append(";--------------------\n" + "; CP PRIMING START\n") + .append(";--------------------\n") + .speed_override_backup() + .speed_override(100) + .set_initial_position(xy(0.f, 0.f)) // Always move to the starting position + .travel(cleaning_box.ld, 7200); + if (m_set_extruder_trimpot) + writer.set_extruder_trimpot(750); // Increase the extruder driver current to allow fast ramming. + } + else + writer.set_initial_position(results.back().end_pos); + + unsigned int tool = tools[idx_tool]; m_left_to_right = true; toolchange_Change(writer, tool, m_filpar[tool].material); // Select the tool, set a speed override for soluble and flex materials. @@ -569,39 +581,48 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::prime( writer.travel(cleaning_box.ld, 7200); } ++ m_num_tool_changes; + + + // Ask our writer about how much material was consumed: + if (m_current_tool < m_used_filament_length.size()) + m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); + + ToolChangeResult result; + result.priming = true; + result.initial_tool = old_tool; + result.new_tool = m_current_tool; + result.print_z = this->m_z_pos; + result.layer_height = this->m_layer_height; + result.gcode = writer.gcode(); + result.elapsed_time = writer.elapsed_time(); + result.extrusions = writer.extrusions(); + result.start_pos = writer.start_pos_rotated(); + result.end_pos = writer.pos_rotated(); + + results.push_back(std::move(result)); + + // This is the last priming toolchange - finish priming + if (idx_tool+1 == tools.size()) { + // Reset the extruder current to a normal value. + if (m_set_extruder_trimpot) + writer.set_extruder_trimpot(550); + writer.speed_override_restore() + .feedrate(6000) + .flush_planner_queue() + .reset_extruder() + .append("; CP PRIMING END\n" + ";------------------\n" + "\n\n"); + } } m_old_temperature = -1; // If the priming is turned off in config, the temperature changing commands will not actually appear // in the output gcode - we should not remember emitting them (we will output them twice in the worst case) - // Reset the extruder current to a normal value. - if (m_set_extruder_trimpot) - writer.set_extruder_trimpot(550); - writer.speed_override_restore(); - writer.feedrate(6000) - .flush_planner_queue() - .reset_extruder() - .append("; CP PRIMING END\n" - ";------------------\n" - "\n\n"); - // so that tool_change() will know to extrude the wipe tower brim: m_print_brim = true; - // Ask our writer about how much material was consumed: - if (m_current_tool < m_used_filament_length.size()) - m_used_filament_length[m_current_tool] += writer.get_and_reset_used_filament_length(); - - ToolChangeResult result; - result.priming = true; - result.print_z = this->m_z_pos; - result.layer_height = this->m_layer_height; - result.gcode = writer.gcode(); - result.elapsed_time = writer.elapsed_time(); - result.extrusions = writer.extrusions(); - result.start_pos = writer.start_pos_rotated(); - result.end_pos = writer.pos_rotated(); - return result; + return results; } WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, bool last_in_layer) @@ -609,6 +630,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo if ( m_print_brim ) return toolchange_Brim(); + int old_tool = m_current_tool; + float wipe_area = 0.f; bool last_change_in_layer = false; float wipe_volume = 0.f; @@ -697,6 +720,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo ToolChangeResult result; result.priming = false; + result.initial_tool = old_tool; + result.new_tool = m_current_tool; result.print_z = this->m_z_pos; result.layer_height = this->m_layer_height; result.gcode = writer.gcode(); @@ -709,6 +734,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, float y_offset) { + int old_tool = m_current_tool; + const box_coordinates wipeTower_box( WipeTower::xy(0.f, 0.f), m_wipe_tower_width, @@ -751,6 +778,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo ToolChangeResult result; result.priming = false; + result.initial_tool = old_tool; + result.new_tool = m_current_tool; result.print_z = this->m_z_pos; result.layer_height = this->m_layer_height; result.gcode = writer.gcode(); @@ -1044,6 +1073,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer() // Otherwise the caller would likely travel to the wipe tower in vain. assert(! this->layer_finished()); + int old_tool = m_current_tool; + PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); writer.set_extrusion_flow(m_extrusion_flow) .set_z(m_z_pos) @@ -1125,6 +1156,8 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer() ToolChangeResult result; result.priming = false; + result.initial_tool = old_tool; + result.new_tool = m_current_tool; result.print_z = this->m_z_pos; result.layer_height = this->m_layer_height; result.gcode = writer.gcode(); diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/src/libslic3r/GCode/WipeTowerPrusaMM.hpp index 8e1e494a7..575234c4c 100644 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.hpp +++ b/src/libslic3r/GCode/WipeTowerPrusaMM.hpp @@ -172,7 +172,7 @@ public: virtual bool finished() const { return m_max_color_changes == 0; } // Returns gcode to prime the nozzles at the front edge of the print bed. - virtual ToolChangeResult prime( + virtual std::vector prime( // print_z of the first layer. float first_layer_height, // Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object. diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 0b73f56e8..1cf79340e 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1824,7 +1824,7 @@ void Print::_make_wipe_tower() m_config.filament_max_volumetric_speed.get_at(i), m_config.nozzle_diameter.get_at(i)); - m_wipe_tower_data.priming = Slic3r::make_unique( + m_wipe_tower_data.priming = Slic3r::make_unique>( wipe_tower.prime(this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false)); // Lets go through the wipe tower layers and determine pairs of extruder changes for each diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 53d6d692d..431f8023e 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -213,7 +213,7 @@ struct WipeTowerData // Cache it here, so it does not need to be recalculated during the G-code generation. ToolOrdering tool_ordering; // Cache of tool changes per print layer. - std::unique_ptr priming; + std::unique_ptr> priming; std::vector> tool_changes; std::unique_ptr final_purge; std::vector used_filament; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 216848477..65e06e432 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4804,7 +4804,8 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector& str_ ctxt.print = print; ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; if (print->wipe_tower_data().priming && print->config().single_extruder_multi_material_priming) - ctxt.priming.emplace_back(*print->wipe_tower_data().priming.get()); + for (int i=0; iwipe_tower_data().priming.get()->size(); ++i) + ctxt.priming.emplace_back(print->wipe_tower_data().priming.get()->at(i)); if (print->wipe_tower_data().final_purge) ctxt.final.emplace_back(*print->wipe_tower_data().final_purge.get()); From 678d0e18a7c18717edbd5b8ead848926f3dd6342 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 14 Jun 2019 12:49:43 +0200 Subject: [PATCH 07/72] WipeTowerIntegration class: print_z is not passed around, ToolChangeResult objects are aware of it --- src/libslic3r/GCode.cpp | 14 +++++++------- src/libslic3r/GCode.hpp | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 768cec6f4..a606f5666 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -167,7 +167,7 @@ static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const Wipe return Point(scale_(wipe_tower_pt.x - gcodegen.origin()(0)), scale_(wipe_tower_pt.y - gcodegen.origin()(1))); } -std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, float print_z) const +std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const { std::string gcode; @@ -224,7 +224,7 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T config.set_key_value("previous_extruder", new ConfigOptionInt(previous_extruder_id)); config.set_key_value("next_extruder", new ConfigOptionInt((int)new_extruder_id)); config.set_key_value("layer_num", new ConfigOptionInt(gcodegen.m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); + config.set_key_value("layer_z", new ConfigOptionFloat(tcr.print_z)); toolchange_gcode_str = gcodegen.placeholder_parser_process("toolchange_gcode", toolchange_gcode, new_extruder_id, &config); check_add_eol(toolchange_gcode_str); } @@ -344,7 +344,7 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen) for (const WipeTower::ToolChangeResult& tcr : m_priming) { if (!tcr.extrusions.empty()) - gcode += append_tcr(gcodegen, tcr, tcr.new_tool, tcr.print_z); + gcode += append_tcr(gcodegen, tcr, tcr.new_tool); // Let the tool change be executed by the wipe tower class. @@ -372,14 +372,14 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen) return gcode; } -std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, bool finish_layer, float print_z) +std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, bool finish_layer) { std::string gcode; assert(m_layer_idx >= 0 && m_layer_idx <= m_tool_changes.size()); if (! m_brim_done || gcodegen.writer().need_toolchange(extruder_id) || finish_layer) { if (m_layer_idx < m_tool_changes.size()) { assert(m_tool_change_idx < m_tool_changes[m_layer_idx].size()); - gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id, print_z); + gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id); } m_brim_done = true; } @@ -392,7 +392,7 @@ std::string WipeTowerIntegration::finalize(GCode &gcodegen) std::string gcode; if (std::abs(gcodegen.writer().get_position()(2) - m_final_purge.print_z) > EPSILON) gcode += gcodegen.change_layer(m_final_purge.print_z); - gcode += append_tcr(gcodegen, m_final_purge, -1, m_final_purge.print_z); + gcode += append_tcr(gcodegen, m_final_purge, -1); return gcode; } @@ -1649,7 +1649,7 @@ void GCode::process_layer( for (unsigned int extruder_id : layer_tools.extruders) { gcode += (layer_tools.has_wipe_tower && m_wipe_tower) ? - m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back(), print_z) : + m_wipe_tower->tool_change(*this, extruder_id, extruder_id == layer_tools.extruders.back()) : this->set_extruder(extruder_id, print_z); // let analyzer tag generator aware of a role type change diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 35f5fb485..639c83aa4 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -99,13 +99,13 @@ public: std::string prime(GCode &gcodegen); void next_layer() { ++ m_layer_idx; m_tool_change_idx = 0; } - std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer, float print_z); + std::string tool_change(GCode &gcodegen, int extruder_id, bool finish_layer); std::string finalize(GCode &gcodegen); std::vector used_filament_length() const; private: WipeTowerIntegration& operator=(const WipeTowerIntegration&); - std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id, float print_z) const; + std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const; // Postprocesses gcode: rotates and moves all G1 extrusions and returns result std::string rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const; From 0eecfc660435b591a3fccf58e6d583c8fc3e0273 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 14 Jun 2019 14:31:53 +0200 Subject: [PATCH 08/72] Wipe tower - removed the obsolete material_type enum no longer necessary because the speed overrides that the enum controlled were recently removed the comment in gcode is now just about appending the config string --- src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 64 +++--------------------- src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 27 ++-------- src/libslic3r/Print.cpp | 2 +- 3 files changed, 13 insertions(+), 80 deletions(-) diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index 692397276..0c32bd50c 100644 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -398,13 +398,6 @@ public: return *this; } - Writer& comment_material(WipeTowerPrusaMM::material_type material) - { - m_gcode += "; material : "; - m_gcode += WipeTowerPrusaMM::to_string(material) + "\n"; - return *this; - }; - Writer& append(const char *text) { m_gcode += text; return *this; } private: @@ -470,50 +463,6 @@ private: }; // namespace PrusaMultiMaterial - -WipeTowerPrusaMM::material_type WipeTowerPrusaMM::parse_material(const char *name) -{ - if (strcasecmp(name, "PLA") == 0) - return PLA; - if (strcasecmp(name, "ABS") == 0) - return ABS; - if (strcasecmp(name, "PET") == 0) - return PET; - if (strcasecmp(name, "HIPS") == 0) - return HIPS; - if (strcasecmp(name, "FLEX") == 0) - return FLEX; - if (strcasecmp(name, "SCAFF") == 0) - return SCAFF; - if (strcasecmp(name, "EDGE") == 0) - return EDGE; - if (strcasecmp(name, "NGEN") == 0) - return NGEN; - if (strcasecmp(name, "PVA") == 0) - return PVA; - if (strcasecmp(name, "PC") == 0) - return PC; - return INVALID; -} - -std::string WipeTowerPrusaMM::to_string(material_type material) -{ - switch (material) { - case PLA: return "PLA"; - case ABS: return "ABS"; - case PET: return "PET"; - case HIPS: return "HIPS"; - case FLEX: return "FLEX"; - case SCAFF: return "SCAFF"; - case EDGE: return "EDGE"; - case NGEN: return "NGEN"; - case PVA: return "PVA"; - case PC: return "PC"; - case INVALID: - default: return "INVALID"; - } -} - // Returns gcode to prime the nozzles at the front edge of the print bed. std::vector WipeTowerPrusaMM::prime( // print_z of the first layer. @@ -665,9 +614,12 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo .set_y_shift(m_y_shift + (tool!=(unsigned int)(-1) && (m_current_shape == SHAPE_REVERSED && !m_peters_wipe_tower) ? m_layer_info->depth - m_layer_info->toolchanges_depth(): 0.f)) .append(";--------------------\n" "; CP TOOLCHANGE START\n") - .comment_with_value(" toolchange #", m_num_tool_changes + 1) // the number is zero-based - .comment_material(m_filpar[m_current_tool].material) - .append(";--------------------\n"); + .comment_with_value(" toolchange #", m_num_tool_changes + 1); // the number is zero-based + + if (tool != (unsigned)(-1)) + writer.append(std::string("; material : " + (m_current_tool < m_filpar.size() ? m_filpar[m_current_tool].material : "(NONE)") + " -> " + m_filpar[tool].material + "\n").c_str()) + .append(";--------------------\n"); + writer.speed_override_backup(); writer.speed_override(100); @@ -796,7 +748,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo void WipeTowerPrusaMM::toolchange_Unload( PrusaMultiMaterial::Writer &writer, const box_coordinates &cleaning_box, - const material_type current_material, + const std::string& current_material, const int new_temperature) { float xl = cleaning_box.ld.x + 1.f * m_perimeter_width; @@ -945,7 +897,7 @@ void WipeTowerPrusaMM::toolchange_Unload( void WipeTowerPrusaMM::toolchange_Change( PrusaMultiMaterial::Writer &writer, const unsigned int new_tool, - material_type new_material) + const std::string& new_material) { // Ask the writer about how much of the old filament we consumed: if (m_current_tool < m_used_filament_length.size()) diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/src/libslic3r/GCode/WipeTowerPrusaMM.hpp index 575234c4c..a6478ee58 100644 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.hpp +++ b/src/libslic3r/GCode/WipeTowerPrusaMM.hpp @@ -23,25 +23,6 @@ namespace PrusaMultiMaterial { class WipeTowerPrusaMM : public WipeTower { public: - enum material_type - { - INVALID = -1, - PLA = 0, // E:210C B:55C - ABS = 1, // E:255C B:100C - PET = 2, // E:240C B:90C - HIPS = 3, // E:220C B:100C - FLEX = 4, // E:245C B:80C - SCAFF = 5, // E:215C B:55C - EDGE = 6, // E:240C B:80C - NGEN = 7, // E:230C B:80C - PVA = 8, // E:210C B:80C - PC = 9 - }; - - // Parse material name into material_type. - static material_type parse_material(const char *name); - static std::string to_string(material_type material); - // x -- x coordinates of wipe tower in mm ( left bottom corner ) // y -- y coordinates of wipe tower in mm ( left bottom corner ) // width -- width of wipe tower in mm ( default 60 mm - leave as it is ) @@ -77,7 +58,7 @@ public: // Set the extruder properties. - void set_extruder(size_t idx, material_type material, int temp, int first_layer_temp, float loading_speed, float loading_speed_start, + void set_extruder(size_t idx, std::string material, int temp, int first_layer_temp, float loading_speed, float loading_speed_start, float unloading_speed, float unloading_speed_start, float delay, int cooling_moves, float cooling_initial_speed, float cooling_final_speed, std::string ramming_parameters, float max_volumetric_speed, float nozzle_diameter) { @@ -198,7 +179,7 @@ public: virtual int get_number_of_toolchanges() const override { return m_num_tool_changes; } struct FilamentParameters { - material_type material = PLA; + std::string material = "PLA"; int temperature = 0; int first_layer_temperature = 0; float loading_speed = 0.f; @@ -370,13 +351,13 @@ private: void toolchange_Unload( PrusaMultiMaterial::Writer &writer, const box_coordinates &cleaning_box, - const material_type current_material, + const std::string& current_material, const int new_temperature); void toolchange_Change( PrusaMultiMaterial::Writer &writer, const unsigned int new_tool, - material_type new_material); + const std::string& new_material); void toolchange_Load( PrusaMultiMaterial::Writer &writer, diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 1cf79340e..7a9bb785f 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1809,7 +1809,7 @@ void Print::_make_wipe_tower() wipe_tower.set_extruder( i, - WipeTowerPrusaMM::parse_material(m_config.filament_type.get_at(i).c_str()), + m_config.filament_type.get_at(i), m_config.temperature.get_at(i), m_config.first_layer_temperature.get_at(i), m_config.filament_loading_speed.get_at(i), From 05e6dbbe4be46295533a36672718c5eef57847d6 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 17 Jun 2019 10:16:07 +0200 Subject: [PATCH 09/72] Wipe tower - refactoring (removed the abstract WipeTower class) - abstract class WipeTower and its descendant WipeTowerPrusaMM were merged into a single (non-abstract) WipeTower class - all uses of WipeTower::xy struct were replaced by Eigen Vec2f (it is no longer necessary to be independent on libraries that PrusaSlicer uses) - the WipeTowerPrusaMM.hpp/.cpp will be renamed in the next commit (hopefully it will retain its git history that way) --- src/libslic3r/GCode.cpp | 48 ++-- src/libslic3r/GCode.hpp | 4 +- src/libslic3r/GCode/PrintExtents.cpp | 8 +- src/libslic3r/GCode/WipeTower.hpp | 174 ------------- src/libslic3r/GCode/WipeTowerPrusaMM.cpp | 304 ++++++++++++----------- src/libslic3r/GCode/WipeTowerPrusaMM.hpp | 143 +++++++---- src/libslic3r/Print.cpp | 4 +- src/slic3r/GUI/GLCanvas3D.cpp | 14 +- 8 files changed, 293 insertions(+), 406 deletions(-) delete mode 100644 src/libslic3r/GCode/WipeTower.hpp diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index a606f5666..dadf9f26e 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -4,7 +4,7 @@ #include "EdgeGrid.hpp" #include "Geometry.hpp" #include "GCode/PrintExtents.hpp" -#include "GCode/WipeTowerPrusaMM.hpp" +#include "GCode/WipeTower.hpp" #include "Utils.hpp" #include @@ -162,9 +162,9 @@ std::string Wipe::wipe(GCode &gcodegen, bool toolchange) return gcode; } -static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const WipeTower::xy &wipe_tower_pt) +static inline Point wipe_tower_point_to_object_point(GCode &gcodegen, const Vec2f &wipe_tower_pt) { - return Point(scale_(wipe_tower_pt.x - gcodegen.origin()(0)), scale_(wipe_tower_pt.y - gcodegen.origin()(1))); + return Point(scale_(wipe_tower_pt.x() - gcodegen.origin()(0)), scale_(wipe_tower_pt.y() - gcodegen.origin()(1))); } std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const @@ -174,13 +174,13 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T // Toolchangeresult.gcode assumes the wipe tower corner is at the origin // We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position float alpha = m_wipe_tower_rotation/180.f * float(M_PI); - WipeTower::xy start_pos = tcr.start_pos; - WipeTower::xy end_pos = tcr.end_pos; + Vec2f start_pos = tcr.start_pos; + Vec2f end_pos = tcr.end_pos; if (!tcr.priming) { - start_pos.rotate(alpha); - start_pos.translate(m_wipe_tower_pos); - end_pos.rotate(alpha); - end_pos.translate(m_wipe_tower_pos); + start_pos = Eigen::Rotation2Df(alpha) * start_pos; + start_pos += m_wipe_tower_pos; + end_pos = Eigen::Rotation2Df(alpha) * end_pos; + end_pos += m_wipe_tower_pos; } std::string tcr_rotated_gcode = tcr.priming ? tcr.gcode : rotate_wipe_tower_moves(tcr.gcode, tcr.start_pos, m_wipe_tower_pos, alpha); @@ -264,7 +264,7 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T // A phony move to the end position at the wipe tower. - gcodegen.writer().travel_to_xy(Vec2d(end_pos.x, end_pos.y)); + gcodegen.writer().travel_to_xy(end_pos.cast()); gcodegen.set_last_pos(wipe_tower_point_to_object_point(gcodegen, end_pos)); // Prepare a future wipe. @@ -274,8 +274,8 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, end_pos)); // Wipe end point: Wipe direction away from the closer tower edge to the further tower edge. gcodegen.m_wipe.path.points.emplace_back(wipe_tower_point_to_object_point(gcodegen, - WipeTower::xy((std::abs(m_left - end_pos.x) < std::abs(m_right - end_pos.x)) ? m_right : m_left, - end_pos.y))); + Vec2f((std::abs(m_left - end_pos.x()) < std::abs(m_right - end_pos.x())) ? m_right : m_left, + end_pos.y()))); } // Let the planner know we are traveling between objects. @@ -285,14 +285,14 @@ std::string WipeTowerIntegration::append_tcr(GCode &gcodegen, const WipeTower::T // This function postprocesses gcode_original, rotates and moves all G1 extrusions and returns resulting gcode // Starting position has to be supplied explicitely (otherwise it would fail in case first G1 command only contained one coordinate) -std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const +std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gcode_original, const Vec2f& start_pos, const Vec2f& translation, float angle) const { std::istringstream gcode_str(gcode_original); std::string gcode_out; std::string line; - WipeTower::xy pos = start_pos; - WipeTower::xy transformed_pos; - WipeTower::xy old_pos(-1000.1f, -1000.1f); + Vec2f pos = start_pos; + Vec2f transformed_pos; + Vec2f old_pos(-1000.1f, -1000.1f); while (gcode_str) { std::getline(gcode_str, line); // we read the gcode line by line @@ -303,25 +303,25 @@ std::string WipeTowerIntegration::rotate_wipe_tower_moves(const std::string& gco char ch = 0; while (line_str >> ch) { if (ch == 'X') - line_str >> pos.x; + line_str >> pos.x(); else if (ch == 'Y') - line_str >> pos.y; + line_str >> pos.y(); else line_out << ch; } transformed_pos = pos; - transformed_pos.rotate(angle); - transformed_pos.translate(translation); + transformed_pos = Eigen::Rotation2Df(angle) * transformed_pos; + transformed_pos += translation; if (transformed_pos != old_pos) { line = line_out.str(); char buf[2048] = "G1"; - if (transformed_pos.x != old_pos.x) - sprintf(buf + strlen(buf), " X%.3f", transformed_pos.x); - if (transformed_pos.y != old_pos.y) - sprintf(buf + strlen(buf), " Y%.3f", transformed_pos.y); + if (transformed_pos.x() != old_pos.x()) + sprintf(buf + strlen(buf), " X%.3f", transformed_pos.x()); + if (transformed_pos.y() != old_pos.y()) + sprintf(buf + strlen(buf), " Y%.3f", transformed_pos.y()); line.replace(line.find("G1 "), 3, buf); old_pos = transformed_pos; diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 639c83aa4..4b81b42aa 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -108,12 +108,12 @@ private: std::string append_tcr(GCode &gcodegen, const WipeTower::ToolChangeResult &tcr, int new_extruder_id) const; // Postprocesses gcode: rotates and moves all G1 extrusions and returns result - std::string rotate_wipe_tower_moves(const std::string& gcode_original, const WipeTower::xy& start_pos, const WipeTower::xy& translation, float angle) const; + std::string rotate_wipe_tower_moves(const std::string& gcode_original, const Vec2f& start_pos, const Vec2f& translation, float angle) const; // Left / right edges of the wipe tower, for the planning of wipe moves. const float m_left; const float m_right; - const WipeTower::xy m_wipe_tower_pos; + const Vec2f m_wipe_tower_pos; const float m_wipe_tower_rotation; // Reference to cached values at the Printer class. const std::vector &m_priming; diff --git a/src/libslic3r/GCode/PrintExtents.cpp b/src/libslic3r/GCode/PrintExtents.cpp index 4ff7c1cbd..07a71a0ea 100644 --- a/src/libslic3r/GCode/PrintExtents.cpp +++ b/src/libslic3r/GCode/PrintExtents.cpp @@ -149,8 +149,8 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_ const WipeTower::Extrusion &e = tcr.extrusions[i]; if (e.width > 0) { Vec2d delta = 0.5 * Vec2d(e.width, e.width); - Vec2d p1 = trafo * Vec2d((&e - 1)->pos.x, (&e - 1)->pos.y); - Vec2d p2 = trafo * Vec2d(e.pos.x, e.pos.y); + Vec2d p1 = trafo * (&e - 1)->pos.cast(); + Vec2d p2 = trafo * e.pos.cast(); bbox.merge(p1.cwiseMin(p2) - delta); bbox.merge(p1.cwiseMax(p2) + delta); } @@ -169,8 +169,8 @@ BoundingBoxf get_wipe_tower_priming_extrusions_extents(const Print &print) for (size_t i = 1; i < tcr.extrusions.size(); ++ i) { const WipeTower::Extrusion &e = tcr.extrusions[i]; if (e.width > 0) { - Vec2d p1((&e - 1)->pos.x, (&e - 1)->pos.y); - Vec2d p2(e.pos.x, e.pos.y); + const Vec2d& p1 = (&e - 1)->pos.cast(); + const Vec2d& p2 = e.pos.cast(); bbox.merge(p1); coordf_t radius = 0.5 * e.width; bbox.min(0) = std::min(bbox.min(0), std::min(p1(0), p2(0)) - radius); diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp deleted file mode 100644 index ba841fdd7..000000000 --- a/src/libslic3r/GCode/WipeTower.hpp +++ /dev/null @@ -1,174 +0,0 @@ -#ifndef slic3r_WipeTower_hpp_ -#define slic3r_WipeTower_hpp_ - -#include -#include -#include -#include - -namespace Slic3r -{ - -// A pure virtual WipeTower definition. -class WipeTower -{ -public: - // Internal point class, to make the wipe tower independent from other slic3r modules. - // This is important for Prusa Research as we want to build the wipe tower post-processor independently from slic3r. - struct xy - { - xy(float x = 0.f, float y = 0.f) : x(x), y(y) {} - xy(const xy& pos,float xp,float yp) : x(pos.x+xp), y(pos.y+yp) {} - xy operator+(const xy &rhs) const { xy out(*this); out.x += rhs.x; out.y += rhs.y; return out; } - xy operator-(const xy &rhs) const { xy out(*this); out.x -= rhs.x; out.y -= rhs.y; return out; } - xy& operator+=(const xy &rhs) { x += rhs.x; y += rhs.y; return *this; } - xy& operator-=(const xy &rhs) { x -= rhs.x; y -= rhs.y; return *this; } - bool operator==(const xy &rhs) const { return x == rhs.x && y == rhs.y; } - bool operator!=(const xy &rhs) const { return x != rhs.x || y != rhs.y; } - - // Rotate the point around center of the wipe tower about given angle (in degrees) - xy rotate(float width, float depth, float angle) const { - xy out(0,0); - float temp_x = x - width / 2.f; - float temp_y = y - depth / 2.f; - angle *= float(M_PI/180.); - out.x += temp_x * cos(angle) - temp_y * sin(angle) + width / 2.f; - out.y += temp_x * sin(angle) + temp_y * cos(angle) + depth / 2.f; - return out; - } - - // Rotate the point around origin about given angle in degrees - void rotate(float angle) { - float temp_x = x * cos(angle) - y * sin(angle); - y = x * sin(angle) + y * cos(angle); - x = temp_x; - } - - void translate(const xy& vect) { - x += vect.x; - y += vect.y; - } - - float x; - float y; - }; - - WipeTower() {} - virtual ~WipeTower() {} - - // Return the wipe tower position. - virtual const xy& position() const = 0; - - // Return the wipe tower width. - virtual float width() const = 0; - - // The wipe tower is finished, there should be no more tool changes or wipe tower prints. - virtual bool finished() const = 0; - - // Switch to a next layer. - virtual void set_layer( - // Print height of this layer. - float print_z, - // Layer height, used to calculate extrusion the rate. - float layer_height, - // Maximum number of tool changes on this layer or the layers below. - size_t max_tool_changes, - // Is this the first layer of the print? In that case print the brim first. - bool is_first_layer, - // Is this the last layer of the wipe tower? - bool is_last_layer) = 0; - - enum Purpose { - PURPOSE_MOVE_TO_TOWER, - PURPOSE_EXTRUDE, - PURPOSE_MOVE_TO_TOWER_AND_EXTRUDE, - }; - - // Extrusion path of the wipe tower, for 3D preview of the generated tool paths. - struct Extrusion - { - Extrusion(const xy &pos, float width, unsigned int tool) : pos(pos), width(width), tool(tool) {} - // End position of this extrusion. - xy pos; - // Width of a squished extrusion, corrected for the roundings of the squished extrusions. - // This is left zero if it is a travel move. - float width; - // Current extruder index. - unsigned int tool; - }; - - struct ToolChangeResult - { - // Print heigh of this tool change. - float print_z; - float layer_height; - // G-code section to be directly included into the output G-code. - std::string gcode; - // For path preview. - std::vector extrusions; - // Initial position, at which the wipe tower starts its action. - // At this position the extruder is loaded and there is no Z-hop applied. - xy start_pos; - // Last point, at which the normal G-code generator of Slic3r shall continue. - // At this position the extruder is loaded and there is no Z-hop applied. - xy end_pos; - // Time elapsed over this tool change. - // This is useful not only for the print time estimation, but also for the control of layer cooling. - float elapsed_time; - - // Is this a priming extrusion? (If so, the wipe tower rotation & translation will not be applied later) - bool priming; - - // Initial tool - int initial_tool; - - // New tool - int new_tool; - - // Sum the total length of the extrusion. - float total_extrusion_length_in_plane() { - float e_length = 0.f; - for (size_t i = 1; i < this->extrusions.size(); ++ i) { - const Extrusion &e = this->extrusions[i]; - if (e.width > 0) { - xy v = e.pos - (&e - 1)->pos; - e_length += sqrt(v.x*v.x+v.y*v.y); - } - } - return e_length; - } - }; - - // Returns gcode to prime the nozzles at the front edge of the print bed. - virtual std::vector prime( - // print_z of the first layer. - float first_layer_height, - // Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object. - const std::vector &tools, - // If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower. - // If false, the last priming are will be large enough to wipe the last extruder sufficiently. - bool last_wipe_inside_wipe_tower) = 0; - - // Returns gcode for toolchange and the end position. - // if new_tool == -1, just unload the current filament over the wipe tower. - virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer) = 0; - - // Close the current wipe tower layer with a perimeter and possibly fill the unfilled space with a zig-zag. - // Call this method only if layer_finished() is false. - virtual ToolChangeResult finish_layer() = 0; - - // Is the current layer finished? A layer is finished if either the wipe tower is finished, or - // the wipe tower has been completely covered by the tool change extrusions, - // or the rest of the tower has been filled by a sparse infill with the finish_layer() method. - virtual bool layer_finished() const = 0; - - // Returns used filament length per extruder: - virtual std::vector get_used_filament() const = 0; - - // Returns total number of toolchanges: - virtual int get_number_of_toolchanges() const = 0; -}; - -}; // namespace Slic3r - -#endif /* slic3r_WipeTower_hpp_ */ diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp index 0c32bd50c..e901ba296 100644 --- a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp +++ b/src/libslic3r/GCode/WipeTowerPrusaMM.cpp @@ -13,7 +13,7 @@ TODO LIST */ -#include "WipeTowerPrusaMM.hpp" +#include "WipeTower.hpp" #include #include @@ -35,12 +35,23 @@ TODO LIST namespace Slic3r { -namespace PrusaMultiMaterial { +// Rotate the point around center of the wipe tower about given angle (in degrees) +static Vec2f rotate(const Vec2f& pt, float width, float depth, float angle) +{ + Vec2f out(0,0); + float temp_x = pt(0) - width / 2.f; + float temp_y = pt(1) - depth / 2.f; + angle *= float(M_PI/180.); + out.x() += temp_x * cos(angle) - temp_y * sin(angle) + width / 2.f; + out.y() += temp_x * sin(angle) + temp_y * cos(angle) + depth / 2.f; + return out; +} -class Writer + +class WipeTowerWriter { public: - Writer(float layer_height, float line_width, GCodeFlavor flavor, const std::vector& filament_parameters) : + WipeTowerWriter(float layer_height, float line_width, GCodeFlavor flavor, const std::vector& filament_parameters) : m_current_pos(std::numeric_limits::max(), std::numeric_limits::max()), m_current_z(0.f), m_current_feedrate(0.f), @@ -61,7 +72,7 @@ public: change_analyzer_line_width(line_width); } - Writer& change_analyzer_line_width(float line_width) { + WipeTowerWriter& change_analyzer_line_width(float line_width) { // adds tag for analyzer: char buf[64]; sprintf(buf, ";%s%f\n", GCodeAnalyzer::Width_Tag.c_str(), line_width); @@ -69,7 +80,7 @@ public: return *this; } - Writer& change_analyzer_mm3_per_mm(float len, float e) { + WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) { static const float area = M_PI * 1.75f * 1.75f / 4.f; float mm3_per_mm = (len == 0.f ? 0.f : area * e / len); // adds tag for analyzer: @@ -79,25 +90,25 @@ public: return *this; } - Writer& set_initial_position(const WipeTower::xy &pos, float width = 0.f, float depth = 0.f, float internal_angle = 0.f) { + WipeTowerWriter& set_initial_position(const Vec2f &pos, float width = 0.f, float depth = 0.f, float internal_angle = 0.f) { m_wipe_tower_width = width; m_wipe_tower_depth = depth; m_internal_angle = internal_angle; - m_start_pos = WipeTower::xy(pos,0.f,m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); + m_start_pos = rotate(pos + Vec2f(0.f,m_y_shift), m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); m_current_pos = pos; return *this; } - Writer& set_initial_tool(const unsigned int tool) { m_current_tool = tool; return *this; } + WipeTowerWriter& set_initial_tool(const unsigned int tool) { m_current_tool = tool; return *this; } - Writer& set_z(float z) + WipeTowerWriter& set_z(float z) { m_current_z = z; return *this; } - Writer& set_extrusion_flow(float flow) + WipeTowerWriter& set_extrusion_flow(float flow) { m_extrusion_flow = flow; return *this; } - Writer& set_y_shift(float shift) { - m_current_pos.y -= shift-m_y_shift; + WipeTowerWriter& set_y_shift(float shift) { + m_current_pos.y() -= shift-m_y_shift; m_y_shift = shift; return (*this); } @@ -105,10 +116,10 @@ public: // Suppress / resume G-code preview in Slic3r. Slic3r will have difficulty to differentiate the various // filament loading and cooling moves from normal extrusion moves. Therefore the writer // is asked to suppres output of some lines, which look like extrusions. - Writer& suppress_preview() { change_analyzer_line_width(0.f); m_preview_suppressed = true; return *this; } - Writer& resume_preview() { change_analyzer_line_width(m_default_analyzer_line_width); m_preview_suppressed = false; return *this; } + WipeTowerWriter& suppress_preview() { change_analyzer_line_width(0.f); m_preview_suppressed = true; return *this; } + WipeTowerWriter& resume_preview() { change_analyzer_line_width(m_default_analyzer_line_width); m_preview_suppressed = false; return *this; } - Writer& feedrate(float f) + WipeTowerWriter& feedrate(float f) { if (f != m_current_feedrate) m_gcode += "G1" + set_format_F(f) + "\n"; @@ -117,30 +128,30 @@ public: const std::string& gcode() const { return m_gcode; } const std::vector& extrusions() const { return m_extrusions; } - float x() const { return m_current_pos.x; } - float y() const { return m_current_pos.y; } - const WipeTower::xy& pos() const { return m_current_pos; } - const WipeTower::xy start_pos_rotated() const { return m_start_pos; } - const WipeTower::xy pos_rotated() const { return WipeTower::xy(m_current_pos, 0.f, m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); } + float x() const { return m_current_pos.x(); } + float y() const { return m_current_pos.y(); } + const Vec2f& pos() const { return m_current_pos; } + const Vec2f start_pos_rotated() const { return m_start_pos; } + const Vec2f pos_rotated() const { return rotate(m_current_pos + Vec2f(0.f, m_y_shift), m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle); } float elapsed_time() const { return m_elapsed_time; } float get_and_reset_used_filament_length() { float temp = m_used_filament_length; m_used_filament_length = 0.f; return temp; } // Extrude with an explicitely provided amount of extrusion. - Writer& extrude_explicit(float x, float y, float e, float f = 0.f, bool record_length = false, bool limit_volumetric_flow = true) + WipeTowerWriter& extrude_explicit(float x, float y, float e, float f = 0.f, bool record_length = false, bool limit_volumetric_flow = true) { - if (x == m_current_pos.x && y == m_current_pos.y && e == 0.f && (f == 0.f || f == m_current_feedrate)) + if (x == m_current_pos.x() && y == m_current_pos.y() && e == 0.f && (f == 0.f || f == m_current_feedrate)) // Neither extrusion nor a travel move. return *this; - float dx = x - m_current_pos.x; - float dy = y - m_current_pos.y; + float dx = x - m_current_pos.x(); + float dy = y - m_current_pos.y(); double len = sqrt(dx*dx+dy*dy); if (record_length) m_used_filament_length += e; // Now do the "internal rotation" with respect to the wipe tower center - WipeTower::xy rotated_current_pos(WipeTower::xy(m_current_pos,0.f,m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we are - WipeTower::xy rot(WipeTower::xy(x,y+m_y_shift).rotate(m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we want to go + Vec2f rotated_current_pos(rotate(m_current_pos + Vec2f(0.f,m_y_shift), m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we are + Vec2f rot(rotate(Vec2f(x,y+m_y_shift), m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we want to go if (! m_preview_suppressed && e > 0.f && len > 0.) { change_analyzer_mm3_per_mm(len, e); @@ -151,15 +162,15 @@ public: width += m_layer_height * float(1. - M_PI / 4.); if (m_extrusions.empty() || m_extrusions.back().pos != rotated_current_pos) m_extrusions.emplace_back(WipeTower::Extrusion(rotated_current_pos, 0, m_current_tool)); - m_extrusions.emplace_back(WipeTower::Extrusion(WipeTower::xy(rot.x, rot.y), width, m_current_tool)); + m_extrusions.emplace_back(WipeTower::Extrusion(rot, width, m_current_tool)); } m_gcode += "G1"; - if (std::abs(rot.x - rotated_current_pos.x) > EPSILON) - m_gcode += set_format_X(rot.x); + if (std::abs(rot.x() - rotated_current_pos.x()) > EPSILON) + m_gcode += set_format_X(rot.x()); - if (std::abs(rot.y - rotated_current_pos.y) > EPSILON) - m_gcode += set_format_Y(rot.y); + if (std::abs(rot.y() - rotated_current_pos.y()) > EPSILON) + m_gcode += set_format_Y(rot.y()); if (e != 0.f) @@ -173,8 +184,8 @@ public: m_gcode += set_format_F(f); } - m_current_pos.x = x; - m_current_pos.y = y; + m_current_pos.x() = x; + m_current_pos.y() = y; // Update the elapsed time with a rough estimate. m_elapsed_time += ((len == 0) ? std::abs(e) : len) / m_current_feedrate * 60.f; @@ -182,42 +193,42 @@ public: return *this; } - Writer& extrude_explicit(const WipeTower::xy &dest, float e, float f = 0.f, bool record_length = false, bool limit_volumetric_flow = true) - { return extrude_explicit(dest.x, dest.y, e, f, record_length); } + WipeTowerWriter& extrude_explicit(const Vec2f &dest, float e, float f = 0.f, bool record_length = false, bool limit_volumetric_flow = true) + { return extrude_explicit(dest.x(), dest.y(), e, f, record_length); } // Travel to a new XY position. f=0 means use the current value. - Writer& travel(float x, float y, float f = 0.f) + WipeTowerWriter& travel(float x, float y, float f = 0.f) { return extrude_explicit(x, y, 0.f, f); } - Writer& travel(const WipeTower::xy &dest, float f = 0.f) - { return extrude_explicit(dest.x, dest.y, 0.f, f); } + WipeTowerWriter& travel(const Vec2f &dest, float f = 0.f) + { return extrude_explicit(dest.x(), dest.y(), 0.f, f); } // Extrude a line from current position to x, y with the extrusion amount given by m_extrusion_flow. - Writer& extrude(float x, float y, float f = 0.f) + WipeTowerWriter& extrude(float x, float y, float f = 0.f) { - float dx = x - m_current_pos.x; - float dy = y - m_current_pos.y; + float dx = x - m_current_pos.x(); + float dy = y - m_current_pos.y(); return extrude_explicit(x, y, sqrt(dx*dx+dy*dy) * m_extrusion_flow, f, true); } - Writer& extrude(const WipeTower::xy &dest, const float f = 0.f) - { return extrude(dest.x, dest.y, f); } + WipeTowerWriter& extrude(const Vec2f &dest, const float f = 0.f) + { return extrude(dest.x(), dest.y(), f); } - Writer& rectangle(const WipeTower::xy& ld,float width,float height,const float f = 0.f) + WipeTowerWriter& rectangle(const Vec2f& ld,float width,float height,const float f = 0.f) { - WipeTower::xy corners[4]; + Vec2f corners[4]; corners[0] = ld; - corners[1] = WipeTower::xy(ld,width,0.f); - corners[2] = WipeTower::xy(ld,width,height); - corners[3] = WipeTower::xy(ld,0.f,height); + corners[1] = ld + Vec2f(width,0.f); + corners[2] = ld + Vec2f(width,height); + corners[3] = ld + Vec2f(0.f,height); int index_of_closest = 0; - if (x()-ld.x > ld.x+width-x()) // closer to the right + if (x()-ld.x() > ld.x()+width-x()) // closer to the right index_of_closest = 1; - if (y()-ld.y > ld.y+height-y()) // closer to the top + if (y()-ld.y() > ld.y()+height-y()) // closer to the top index_of_closest = (index_of_closest==0 ? 3 : 2); - travel(corners[index_of_closest].x, y()); // travel to the closest corner - travel(x(),corners[index_of_closest].y); + travel(corners[index_of_closest].x(), y()); // travel to the closest corner + travel(x(),corners[index_of_closest].y()); int i = index_of_closest; do { @@ -228,7 +239,7 @@ public: return (*this); } - Writer& load(float e, float f = 0.f) + WipeTowerWriter& load(float e, float f = 0.f) { if (e == 0.f && (f == 0.f || f == m_current_feedrate)) return *this; @@ -244,14 +255,14 @@ public: // Derectract while moving in the X direction. // If |x| > 0, the feed rate relates to the x distance, // otherwise the feed rate relates to the e distance. - Writer& load_move_x(float x, float e, float f = 0.f) - { return extrude_explicit(x, m_current_pos.y, e, f); } + WipeTowerWriter& load_move_x(float x, float e, float f = 0.f) + { return extrude_explicit(x, m_current_pos.y(), e, f); } - Writer& retract(float e, float f = 0.f) + WipeTowerWriter& retract(float e, float f = 0.f) { return load(-e, f); } // Loads filament while also moving towards given points in x-axis (x feedrate is limited by cutting the distance short if necessary) - Writer& load_move_x_advanced(float farthest_x, float loading_dist, float loading_speed, float max_x_speed = 50.f) + WipeTowerWriter& load_move_x_advanced(float farthest_x, float loading_dist, float loading_speed, float max_x_speed = 50.f) { float time = std::abs(loading_dist / loading_speed); float x_speed = std::min(max_x_speed, std::abs(farthest_x - x()) / time); @@ -262,7 +273,7 @@ public: } // Elevate the extruder head above the current print_z position. - Writer& z_hop(float hop, float f = 0.f) + WipeTowerWriter& z_hop(float hop, float f = 0.f) { m_gcode += std::string("G1") + set_format_Z(m_current_z + hop); if (f != 0 && f != m_current_feedrate) @@ -272,29 +283,29 @@ public: } // Lower the extruder head back to the current print_z position. - Writer& z_hop_reset(float f = 0.f) + WipeTowerWriter& z_hop_reset(float f = 0.f) { return z_hop(0, f); } // Move to x1, +y_increment, // extrude quickly amount e to x2 with feed f. - Writer& ram(float x1, float x2, float dy, float e0, float e, float f) + WipeTowerWriter& ram(float x1, float x2, float dy, float e0, float e, float f) { - extrude_explicit(x1, m_current_pos.y + dy, e0, f, true, false); - extrude_explicit(x2, m_current_pos.y, e, 0.f, true, false); + extrude_explicit(x1, m_current_pos.y() + dy, e0, f, true, false); + extrude_explicit(x2, m_current_pos.y(), e, 0.f, true, false); return *this; } // Let the end of the pulled out filament cool down in the cooling tube // by moving up and down and moving the print head left / right // at the current Y position to spread the leaking material. - Writer& cool(float x1, float x2, float e1, float e2, float f) + WipeTowerWriter& cool(float x1, float x2, float e1, float e2, float f) { - extrude_explicit(x1, m_current_pos.y, e1, f); - extrude_explicit(x2, m_current_pos.y, e2); + extrude_explicit(x1, m_current_pos.y(), e1, f); + extrude_explicit(x2, m_current_pos.y(), e2); return *this; } - Writer& set_tool(int tool) + WipeTowerWriter& set_tool(int tool) { char buf[64]; sprintf(buf, "T%d\n", tool); @@ -304,7 +315,7 @@ public: } // Set extruder temperature, don't wait by default. - Writer& set_extruder_temp(int temperature, bool wait = false) + WipeTowerWriter& set_extruder_temp(int temperature, bool wait = false) { char buf[128]; sprintf(buf, "M%d S%d\n", wait ? 109 : 104, temperature); @@ -313,7 +324,7 @@ public: }; // Wait for a period of time (seconds). - Writer& wait(float time) + WipeTowerWriter& wait(float time) { if (time==0) return *this; @@ -324,7 +335,7 @@ public: }; // Set speed factor override percentage. - Writer& speed_override(int speed) + WipeTowerWriter& speed_override(int speed) { char buf[128]; sprintf(buf, "M220 S%d\n", speed); @@ -333,21 +344,21 @@ public: }; // Let the firmware back up the active speed override value. - Writer& speed_override_backup() + WipeTowerWriter& speed_override_backup() { m_gcode += "M220 B\n"; return *this; }; // Let the firmware restore the active speed override value. - Writer& speed_override_restore() + WipeTowerWriter& speed_override_restore() { m_gcode += "M220 R\n"; return *this; }; // Set digital trimpot motor - Writer& set_extruder_trimpot(int current) + WipeTowerWriter& set_extruder_trimpot(int current) { char buf[128]; if (m_gcode_flavor == gcfRepRap) @@ -358,20 +369,20 @@ public: return *this; }; - Writer& flush_planner_queue() + WipeTowerWriter& flush_planner_queue() { m_gcode += "G4 S0\n"; return *this; } // Reset internal extruder counter. - Writer& reset_extruder() + WipeTowerWriter& reset_extruder() { m_gcode += "G92 E0\n"; return *this; } - Writer& comment_with_value(const char *comment, int value) + WipeTowerWriter& comment_with_value(const char *comment, int value) { char strvalue[64]; sprintf(strvalue, "%d", value); @@ -380,7 +391,7 @@ public: }; - Writer& set_fan(unsigned int speed) + WipeTowerWriter& set_fan(unsigned int speed) { if (speed == m_last_fan_speed) return *this; @@ -398,11 +409,11 @@ public: return *this; } - Writer& append(const char *text) { m_gcode += text; return *this; } + WipeTowerWriter& append(const char *text) { m_gcode += text; return *this; } private: - WipeTower::xy m_start_pos; - WipeTower::xy m_current_pos; + Vec2f m_start_pos; + Vec2f m_current_pos; float m_current_z; float m_current_feedrate; unsigned int m_current_tool; @@ -421,20 +432,20 @@ private: const float m_default_analyzer_line_width; float m_used_filament_length = 0.f; GCodeFlavor m_gcode_flavor; - const std::vector& m_filpar; + const std::vector& m_filpar; std::string set_format_X(float x) { char buf[64]; sprintf(buf, " X%.3f", x); - m_current_pos.x = x; + m_current_pos.x() = x; return buf; } std::string set_format_Y(float y) { char buf[64]; sprintf(buf, " Y%.3f", y); - m_current_pos.y = y; + m_current_pos.y() = y; return buf; } @@ -457,14 +468,13 @@ private: return buf; } - Writer& operator=(const Writer &rhs); -}; // class Writer + WipeTowerWriter& operator=(const WipeTowerWriter &rhs); +}; // class WipeTowerWriter -}; // namespace PrusaMultiMaterial // Returns gcode to prime the nozzles at the front edge of the print bed. -std::vector WipeTowerPrusaMM::prime( +std::vector WipeTower::prime( // print_z of the first layer. float first_layer_height, // Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object. @@ -482,7 +492,7 @@ std::vector WipeTowerPrusaMM::prime( // box_coordinates cleaning_box(xy(0.5f, - 1.5f), m_wipe_tower_width, wipe_area); const float prime_section_width = std::min(240.f / tools.size(), 60.f); - box_coordinates cleaning_box(xy(5.f, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f); + box_coordinates cleaning_box(Vec2f(5.f, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f); std::vector results; @@ -491,7 +501,7 @@ std::vector WipeTowerPrusaMM::prime( for (size_t idx_tool = 0; idx_tool < tools.size(); ++ idx_tool) { int old_tool = m_current_tool; - PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); + WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); writer.set_extrusion_flow(m_extrusion_flow) .set_z(m_z_pos) .set_initial_tool(m_current_tool); @@ -503,7 +513,7 @@ std::vector WipeTowerPrusaMM::prime( .append(";--------------------\n") .speed_override_backup() .speed_override(100) - .set_initial_position(xy(0.f, 0.f)) // Always move to the starting position + .set_initial_position(Vec2f::Zero()) // Always move to the starting position .travel(cleaning_box.ld, 7200); if (m_set_extruder_trimpot) writer.set_extruder_trimpot(750); // Increase the extruder driver current to allow fast ramming. @@ -524,7 +534,7 @@ std::vector WipeTowerPrusaMM::prime( //writer.travel(writer.x(), writer.y() + m_perimeter_width, 7200); toolchange_Wipe(writer, cleaning_box , 20.f); box_coordinates box = cleaning_box; - box.translate(0.f, writer.y() - cleaning_box.ld.y + m_perimeter_width); + box.translate(0.f, writer.y() - cleaning_box.ld.y() + m_perimeter_width); toolchange_Unload(writer, box , m_filpar[m_current_tool].material, m_filpar[tools[idx_tool + 1]].first_layer_temperature); cleaning_box.translate(prime_section_width, 0.f); writer.travel(cleaning_box.ld, 7200); @@ -574,7 +584,7 @@ std::vector WipeTowerPrusaMM::prime( return results; } -WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, bool last_in_layer) +WipeTower::ToolChangeResult WipeTower::tool_change(unsigned int tool, bool last_in_layer) { if ( m_print_brim ) return toolchange_Brim(); @@ -602,12 +612,12 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo } box_coordinates cleaning_box( - xy(m_perimeter_width / 2.f, m_perimeter_width / 2.f), + Vec2f(m_perimeter_width / 2.f, m_perimeter_width / 2.f), m_wipe_tower_width - m_perimeter_width, (tool != (unsigned int)(-1) ? /*m_layer_info->depth*/wipe_area+m_depth_traversed-0.5*m_perimeter_width : m_wipe_tower_depth-m_perimeter_width)); - PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); + WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); writer.set_extrusion_flow(m_extrusion_flow) .set_z(m_z_pos) .set_initial_tool(m_current_tool) @@ -623,7 +633,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo writer.speed_override_backup(); writer.speed_override(100); - xy initial_position = cleaning_box.ld + WipeTower::xy(0.f,m_depth_traversed); + Vec2f initial_position = cleaning_box.ld + Vec2f(0.f, m_depth_traversed); writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation); // Increase the extruder driver current to allow fast ramming. @@ -647,9 +657,9 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo if (last_change_in_layer) {// draw perimeter line writer.set_y_shift(m_y_shift); if (m_peters_wipe_tower) - writer.rectangle(WipeTower::xy(0.f, 0.f),m_layer_info->depth + 3*m_perimeter_width,m_wipe_tower_depth); + writer.rectangle(Vec2f::Zero(), m_layer_info->depth + 3*m_perimeter_width, m_wipe_tower_depth); else { - writer.rectangle(WipeTower::xy(0.f, 0.f),m_wipe_tower_width, m_layer_info->depth + m_perimeter_width); + writer.rectangle(Vec2f::Zero(), m_wipe_tower_width, m_layer_info->depth + m_perimeter_width); if (layer_finished()) { // no finish_layer will be called, we must wipe the nozzle writer.travel(writer.x()> m_wipe_tower_width / 2.f ? 0.f : m_wipe_tower_width, writer.y()); } @@ -684,27 +694,27 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::tool_change(unsigned int tool, boo return result; } -WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, float y_offset) +WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_offset) { int old_tool = m_current_tool; const box_coordinates wipeTower_box( - WipeTower::xy(0.f, 0.f), + Vec2f::Zero(), m_wipe_tower_width, m_wipe_tower_depth); - PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); + WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); writer.set_extrusion_flow(m_extrusion_flow * 1.1f) .set_z(m_z_pos) // Let the writer know the current Z position as a base for Z-hop. .set_initial_tool(m_current_tool) .append(";-------------------------------------\n" "; CP WIPE TOWER FIRST LAYER BRIM START\n"); - xy initial_position = wipeTower_box.lu - xy(m_perimeter_width * 6.f, 0); + Vec2f initial_position = wipeTower_box.lu - Vec2f(m_perimeter_width * 6.f, 0); writer.set_initial_position(initial_position, m_wipe_tower_width, m_wipe_tower_depth, m_internal_rotation); - writer.extrude_explicit(wipeTower_box.ld - xy(m_perimeter_width * 6.f, 0), // Prime the extruder left of the wipe tower. - 1.5f * m_extrusion_flow * (wipeTower_box.lu.y - wipeTower_box.ld.y), 2400); + writer.extrude_explicit(wipeTower_box.ld - Vec2f(m_perimeter_width * 6.f, 0), // Prime the extruder left of the wipe tower. + 1.5f * m_extrusion_flow * (wipeTower_box.lu.y() - wipeTower_box.ld.y()), 2400); // The tool is supposed to be active and primed at the time when the wipe tower brim is extruded. // Extrude 4 rounds of a brim around the future wipe tower. @@ -745,14 +755,14 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::toolchange_Brim(bool sideOnly, flo // Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool. -void WipeTowerPrusaMM::toolchange_Unload( - PrusaMultiMaterial::Writer &writer, +void WipeTower::toolchange_Unload( + WipeTowerWriter &writer, const box_coordinates &cleaning_box, const std::string& current_material, const int new_temperature) { - float xl = cleaning_box.ld.x + 1.f * m_perimeter_width; - float xr = cleaning_box.rd.x - 1.f * m_perimeter_width; + float xl = cleaning_box.ld.x() + 1.f * m_perimeter_width; + float xr = cleaning_box.rd.x() - 1.f * m_perimeter_width; const float line_width = m_perimeter_width * m_filpar[m_current_tool].ramming_line_width_multiplicator; // desired ramming line thickness const float y_step = line_width * m_filpar[m_current_tool].ramming_step_multiplicator * m_extra_spacing; // spacing between lines in mm @@ -765,7 +775,7 @@ void WipeTowerPrusaMM::toolchange_Unload( float remaining = xr - xl ; // keeps track of distance to the next turnaround float e_done = 0; // measures E move done from each segment - writer.travel(xl, cleaning_box.ld.y + m_depth_traversed + y_step/2.f ); // move to starting position + writer.travel(xl, cleaning_box.ld.y() + m_depth_traversed + y_step/2.f ); // move to starting position // if the ending point of the ram would end up in mid air, align it with the end of the wipe tower: if (m_layer_info > m_plan.begin() && m_layer_info < m_plan.end() && (m_layer_info-1!=m_plan.begin() || !m_adhesion )) { @@ -832,7 +842,7 @@ void WipeTowerPrusaMM::toolchange_Unload( e_done = 0; } } - WipeTower::xy end_of_ramming(writer.x(),writer.y()); + Vec2f end_of_ramming(writer.x(),writer.y()); writer.change_analyzer_line_width(m_perimeter_width); // so the next lines are not affected by ramming_line_width_multiplier // Retraction: @@ -887,15 +897,15 @@ void WipeTowerPrusaMM::toolchange_Unload( // this is to align ramming and future wiping extrusions, so the future y-steps can be uniform from the start: // the perimeter_width will later be subtracted, it is there to not load while moving over just extruded material - writer.travel(end_of_ramming.x, end_of_ramming.y + (y_step/m_extra_spacing-m_perimeter_width) / 2.f + m_perimeter_width, 2400.f); + writer.travel(end_of_ramming.x(), end_of_ramming.y() + (y_step/m_extra_spacing-m_perimeter_width) / 2.f + m_perimeter_width, 2400.f); writer.resume_preview() .flush_planner_queue(); } // Change the tool, set a speed override for soluble and flex materials. -void WipeTowerPrusaMM::toolchange_Change( - PrusaMultiMaterial::Writer &writer, +void WipeTower::toolchange_Change( + WipeTowerWriter &writer, const unsigned int new_tool, const std::string& new_material) { @@ -917,13 +927,13 @@ void WipeTowerPrusaMM::toolchange_Change( m_current_tool = new_tool; } -void WipeTowerPrusaMM::toolchange_Load( - PrusaMultiMaterial::Writer &writer, +void WipeTower::toolchange_Load( + WipeTowerWriter &writer, const box_coordinates &cleaning_box) { if (m_semm && (m_parking_pos_retraction != 0 || m_extra_loading_move != 0)) { - float xl = cleaning_box.ld.x + m_perimeter_width * 0.75f; - float xr = cleaning_box.rd.x - m_perimeter_width * 0.75f; + float xl = cleaning_box.ld.x() + m_perimeter_width * 0.75f; + float xr = cleaning_box.rd.x() - m_perimeter_width * 0.75f; float oldx = writer.x(); // the nozzle is in place to do the first wiping moves, we will remember the position // Load the filament while moving left / right, so the excess material will not create a blob at a single position. @@ -951,8 +961,8 @@ void WipeTowerPrusaMM::toolchange_Load( } // Wipe the newly loaded filament until the end of the assigned wipe area. -void WipeTowerPrusaMM::toolchange_Wipe( - PrusaMultiMaterial::Writer &writer, +void WipeTower::toolchange_Wipe( + WipeTowerWriter &writer, const box_coordinates &cleaning_box, float wipe_volume) { @@ -960,8 +970,8 @@ void WipeTowerPrusaMM::toolchange_Wipe( writer.set_extrusion_flow(m_extrusion_flow * (m_is_first_layer ? 1.18f : 1.f)) .append("; CP TOOLCHANGE WIPE\n"); float wipe_coeff = m_is_first_layer ? 0.5f : 1.f; - const float& xl = cleaning_box.ld.x; - const float& xr = cleaning_box.rd.x; + const float& xl = cleaning_box.ld.x(); + const float& xr = cleaning_box.rd.x(); // Variables x_to_wipe and traversed_x are here to be able to make sure it always wipes at least // the ordered volume, even if it means violating the box. This can later be removed and simply @@ -992,7 +1002,7 @@ void WipeTowerPrusaMM::toolchange_Wipe( else writer.extrude(xl + (i % 4 == 1 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff); - if (writer.y()+EPSILON > cleaning_box.lu.y-0.5f*m_perimeter_width) + if (writer.y()+EPSILON > cleaning_box.lu.y()-0.5f*m_perimeter_width) break; // in case next line would not fit traversed_x -= writer.x(); @@ -1019,7 +1029,7 @@ void WipeTowerPrusaMM::toolchange_Wipe( -WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer() +WipeTower::ToolChangeResult WipeTower::finish_layer() { // This should only be called if the layer is not finished yet. // Otherwise the caller would likely travel to the wipe tower in vain. @@ -1027,7 +1037,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer() int old_tool = m_current_tool; - PrusaMultiMaterial::Writer writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); + WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); writer.set_extrusion_flow(m_extrusion_flow) .set_z(m_z_pos) .set_initial_tool(m_current_tool) @@ -1039,7 +1049,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer() // Slow down on the 1st layer. float speed_factor = m_is_first_layer ? 0.5f : 1.f; float current_depth = m_layer_info->depth - m_layer_info->toolchanges_depth(); - box_coordinates fill_box(xy(m_perimeter_width, m_depth_traversed + m_perimeter_width), + box_coordinates fill_box(Vec2f(m_perimeter_width, m_depth_traversed + m_perimeter_width), m_wipe_tower_width - 2 * m_perimeter_width, current_depth-m_perimeter_width); @@ -1053,44 +1063,44 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer() else box.expand(-m_perimeter_width); } else i=2; // only draw the inner perimeter, outer has been already drawn by tool_change(...) - writer.rectangle(box.ld,box.rd.x-box.ld.x,box.ru.y-box.rd.y,2900*speed_factor); + writer.rectangle(box.ld, box.rd.x()-box.ld.x(), box.ru.y()-box.rd.y(), 2900*speed_factor); } // we are in one of the corners, travel to ld along the perimeter: - if (writer.x() > fill_box.ld.x+EPSILON) writer.travel(fill_box.ld.x,writer.y()); - if (writer.y() > fill_box.ld.y+EPSILON) writer.travel(writer.x(),fill_box.ld.y); + if (writer.x() > fill_box.ld.x()+EPSILON) writer.travel(fill_box.ld.x(),writer.y()); + if (writer.y() > fill_box.ld.y()+EPSILON) writer.travel(writer.x(),fill_box.ld.y()); if (m_is_first_layer && m_adhesion) { // Extrude a dense infill at the 1st layer to improve 1st layer adhesion of the wipe tower. box.expand(-m_perimeter_width/2.f); - int nsteps = int(floor((box.lu.y - box.ld.y) / (2*m_perimeter_width))); - float step = (box.lu.y - box.ld.y) / nsteps; - writer.travel(box.ld-xy(m_perimeter_width/2.f,m_perimeter_width/2.f)); + int nsteps = int(floor((box.lu.y() - box.ld.y()) / (2*m_perimeter_width))); + float step = (box.lu.y() - box.ld.y()) / nsteps; + writer.travel(box.ld - Vec2f(m_perimeter_width/2.f, m_perimeter_width/2.f)); if (nsteps >= 0) for (int i = 0; i < nsteps; ++i) { - writer.extrude(box.ld.x+m_perimeter_width/2.f, writer.y() + 0.5f * step); - writer.extrude(box.rd.x - m_perimeter_width / 2.f, writer.y()); - writer.extrude(box.rd.x - m_perimeter_width / 2.f, writer.y() + 0.5f * step); - writer.extrude(box.ld.x + m_perimeter_width / 2.f, writer.y()); + writer.extrude(box.ld.x()+m_perimeter_width/2.f, writer.y() + 0.5f * step); + writer.extrude(box.rd.x() - m_perimeter_width / 2.f, writer.y()); + writer.extrude(box.rd.x() - m_perimeter_width / 2.f, writer.y() + 0.5f * step); + writer.extrude(box.ld.x() + m_perimeter_width / 2.f, writer.y()); } - writer.travel(box.rd.x-m_perimeter_width/2.f,writer.y()); // wipe the nozzle + writer.travel(box.rd.x()-m_perimeter_width/2.f,writer.y()); // wipe the nozzle } else { // Extrude a sparse infill to support the material to be printed above. - const float dy = (fill_box.lu.y - fill_box.ld.y - m_perimeter_width); - const float left = fill_box.lu.x+2*m_perimeter_width; - const float right = fill_box.ru.x - 2 * m_perimeter_width; + const float dy = (fill_box.lu.y() - fill_box.ld.y() - m_perimeter_width); + const float left = fill_box.lu.x() + 2*m_perimeter_width; + const float right = fill_box.ru.x() - 2 * m_perimeter_width; if (dy > m_perimeter_width) { // Extrude an inverse U at the left of the region. - writer.travel(fill_box.ld + xy(m_perimeter_width * 2, 0.f)) - .extrude(fill_box.lu + xy(m_perimeter_width * 2, 0.f), 2900 * speed_factor); + writer.travel(fill_box.ld + Vec2f(m_perimeter_width * 2, 0.f)) + .extrude(fill_box.lu + Vec2f(m_perimeter_width * 2, 0.f), 2900 * speed_factor); const int n = 1+(right-left)/(m_bridging); const float dx = (right-left)/n; for (int i=1;i<=n;++i) { float x=left+dx*i; writer.travel(x,writer.y()); - writer.extrude(x,i%2 ? fill_box.rd.y : fill_box.ru.y); + writer.extrude(x,i%2 ? fill_box.rd.y() : fill_box.ru.y()); } writer.travel(left,writer.y(),7200); // wipes the nozzle before moving away from the wipe tower } @@ -1121,7 +1131,7 @@ WipeTower::ToolChangeResult WipeTowerPrusaMM::finish_layer() } // Appends a toolchange into m_plan and calculates neccessary depth of the corresponding box -void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume) +void WipeTower::plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume) { assert(m_plan.empty() || m_plan.back().z <= z_par + WT_EPSILON); // refuses to add a layer below the last one @@ -1157,7 +1167,7 @@ void WipeTowerPrusaMM::plan_toolchange(float z_par, float layer_height_par, unsi -void WipeTowerPrusaMM::plan_tower() +void WipeTower::plan_tower() { // Calculate m_wipe_tower_depth (maximum depth for all the layers) and propagate depths downwards m_wipe_tower_depth = 0.f; @@ -1180,7 +1190,7 @@ void WipeTowerPrusaMM::plan_tower() } } -void WipeTowerPrusaMM::save_on_last_wipe() +void WipeTower::save_on_last_wipe() { for (m_layer_info=m_plan.begin();m_layer_infoz, m_layer_info->height, 0, m_layer_info->z == m_plan.front().z, m_layer_info->z == m_plan.back().z); @@ -1205,7 +1215,7 @@ void WipeTowerPrusaMM::save_on_last_wipe() // Processes vector m_plan and calls respective functions to generate G-code for the wipe tower // Resulting ToolChangeResults are appended into vector "result" -void WipeTowerPrusaMM::generate(std::vector> &result) +void WipeTower::generate(std::vector> &result) { if (m_plan.empty()) @@ -1251,7 +1261,7 @@ void WipeTowerPrusaMM::generate(std::vector #include @@ -7,30 +7,81 @@ #include #include -#include "WipeTower.hpp" -#include "PrintConfig.hpp" +#include "libslic3r/PrintConfig.hpp" namespace Slic3r { -namespace PrusaMultiMaterial { - class Writer; -}; +class WipeTowerWriter; -class WipeTowerPrusaMM : public WipeTower +class WipeTower { public: + struct Extrusion + { + Extrusion(const Vec2f &pos, float width, unsigned int tool) : pos(pos), width(width), tool(tool) {} + // End position of this extrusion. + Vec2f pos; + // Width of a squished extrusion, corrected for the roundings of the squished extrusions. + // This is left zero if it is a travel move. + float width; + // Current extruder index. + unsigned int tool; + }; + + struct ToolChangeResult + { + // Print heigh of this tool change. + float print_z; + float layer_height; + // G-code section to be directly included into the output G-code. + std::string gcode; + // For path preview. + std::vector extrusions; + // Initial position, at which the wipe tower starts its action. + // At this position the extruder is loaded and there is no Z-hop applied. + Vec2f start_pos; + // Last point, at which the normal G-code generator of Slic3r shall continue. + // At this position the extruder is loaded and there is no Z-hop applied. + Vec2f end_pos; + // Time elapsed over this tool change. + // This is useful not only for the print time estimation, but also for the control of layer cooling. + float elapsed_time; + + // Is this a priming extrusion? (If so, the wipe tower rotation & translation will not be applied later) + bool priming; + + // Initial tool + int initial_tool; + + // New tool + int new_tool; + + // Sum the total length of the extrusion. + float total_extrusion_length_in_plane() { + float e_length = 0.f; + for (size_t i = 1; i < this->extrusions.size(); ++ i) { + const Extrusion &e = this->extrusions[i]; + if (e.width > 0) { + Vec2f v = e.pos - (&e - 1)->pos; + e_length += v.norm(); + } + } + return e_length; + } + }; + // x -- x coordinates of wipe tower in mm ( left bottom corner ) // y -- y coordinates of wipe tower in mm ( left bottom corner ) // width -- width of wipe tower in mm ( default 60 mm - leave as it is ) // wipe_area -- space available for one toolchange in mm - WipeTowerPrusaMM(bool semm, float x, float y, float width, float rotation_angle, float cooling_tube_retraction, - float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, - float bridging, bool set_extruder_trimpot, GCodeFlavor flavor, - const std::vector>& wiping_matrix, unsigned int initial_tool) : + WipeTower(bool semm, float x, float y, float width, float rotation_angle, float cooling_tube_retraction, + float cooling_tube_length, float parking_pos_retraction, float extra_loading_move, + float bridging, bool set_extruder_trimpot, GCodeFlavor flavor, + const std::vector>& wiping_matrix, unsigned int initial_tool) : m_semm(semm), m_wipe_tower_pos(x, y), m_wipe_tower_width(width), @@ -54,7 +105,7 @@ public: } } - virtual ~WipeTowerPrusaMM() {} + virtual ~WipeTower() {} // Set the extruder properties. @@ -105,14 +156,14 @@ public: void plan_toolchange(float z_par, float layer_height_par, unsigned int old_tool, unsigned int new_tool, bool brim, float wipe_volume = 0.f); // Iterates through prepared m_plan, generates ToolChangeResults and appends them to "result" - void generate(std::vector> &result); + void generate(std::vector> &result); float get_depth() const { return m_wipe_tower_depth; } // Switch to a next layer. - virtual void set_layer( + void set_layer( // Print height of this layer. float print_z, // Layer height, used to calculate extrusion the rate. @@ -146,14 +197,14 @@ public: } // Return the wipe tower position. - virtual const xy& position() const { return m_wipe_tower_pos; } + const Vec2f& position() const { return m_wipe_tower_pos; } // Return the wipe tower width. - virtual float width() const { return m_wipe_tower_width; } + float width() const { return m_wipe_tower_width; } // The wipe tower is finished, there should be no more tool changes or wipe tower prints. - virtual bool finished() const { return m_max_color_changes == 0; } + bool finished() const { return m_max_color_changes == 0; } // Returns gcode to prime the nozzles at the front edge of the print bed. - virtual std::vector prime( + std::vector prime( // print_z of the first layer. float first_layer_height, // Extruder indices, in the order to be primed. The last extruder will later print the wipe tower brim, print brim and the object. @@ -164,19 +215,19 @@ public: // Returns gcode for a toolchange and a final print head position. // On the first layer, extrude a brim around the future wipe tower first. - virtual ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer); + ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer); // Fill the unfilled space with a sparse infill. // Call this method only if layer_finished() is false. - virtual ToolChangeResult finish_layer(); + ToolChangeResult finish_layer(); // Is the current layer finished? - virtual bool layer_finished() const { + bool layer_finished() const { return ( (m_is_first_layer ? m_wipe_tower_depth - m_perimeter_width : m_layer_info->depth) - WT_EPSILON < m_depth_traversed); } - virtual std::vector get_used_filament() const override { return m_used_filament_length; } - virtual int get_number_of_toolchanges() const override { return m_num_tool_changes; } + std::vector get_used_filament() const { return m_used_filament_length; } + int get_number_of_toolchanges() const { return m_num_tool_changes; } struct FilamentParameters { std::string material = "PLA"; @@ -198,7 +249,7 @@ public: }; private: - WipeTowerPrusaMM(); + WipeTower(); enum wipe_shape // A fill-in direction { @@ -214,7 +265,7 @@ private: bool m_semm = true; // Are we using a single extruder multimaterial printer? - xy m_wipe_tower_pos; // Left front corner of the wipe tower in mm. + Vec2f m_wipe_tower_pos; // Left front corner of the wipe tower in mm. float m_wipe_tower_width; // Width of the wipe tower. float m_wipe_tower_depth = 0.f; // Depth of the wipe tower float m_wipe_tower_rotation_angle = 0.f; // Wipe tower rotation angle in degrees (with respect to x axis) @@ -287,28 +338,28 @@ private: lu(left , bottom + height), rd(left + width, bottom ), ru(left + width, bottom + height) {} - box_coordinates(const xy &pos, float width, float height) : box_coordinates(pos.x, pos.y, width, height) {} - void translate(const xy &shift) { + box_coordinates(const Vec2f &pos, float width, float height) : box_coordinates(pos(0), pos(1), width, height) {} + void translate(const Vec2f &shift) { ld += shift; lu += shift; rd += shift; ru += shift; } - void translate(const float dx, const float dy) { translate(xy(dx, dy)); } + void translate(const float dx, const float dy) { translate(Vec2f(dx, dy)); } void expand(const float offset) { - ld += xy(- offset, - offset); - lu += xy(- offset, offset); - rd += xy( offset, - offset); - ru += xy( offset, offset); + ld += Vec2f(- offset, - offset); + lu += Vec2f(- offset, offset); + rd += Vec2f( offset, - offset); + ru += Vec2f( offset, offset); } void expand(const float offset_x, const float offset_y) { - ld += xy(- offset_x, - offset_y); - lu += xy(- offset_x, offset_y); - rd += xy( offset_x, - offset_y); - ru += xy( offset_x, offset_y); + ld += Vec2f(- offset_x, - offset_y); + lu += Vec2f(- offset_x, offset_y); + rd += Vec2f( offset_x, - offset_y); + ru += Vec2f( offset_x, offset_y); } - xy ld; // left down - xy lu; // left upper - xy rd; // right lower - xy ru; // right upper + Vec2f ld; // left down + Vec2f lu; // left upper + Vec2f rd; // right lower + Vec2f ru; // right upper }; @@ -349,22 +400,22 @@ private: ToolChangeResult toolchange_Brim(bool sideOnly = false, float y_offset = 0.f); void toolchange_Unload( - PrusaMultiMaterial::Writer &writer, + WipeTowerWriter &writer, const box_coordinates &cleaning_box, const std::string& current_material, const int new_temperature); void toolchange_Change( - PrusaMultiMaterial::Writer &writer, + WipeTowerWriter &writer, const unsigned int new_tool, const std::string& new_material); void toolchange_Load( - PrusaMultiMaterial::Writer &writer, + WipeTowerWriter &writer, const box_coordinates &cleaning_box); void toolchange_Wipe( - PrusaMultiMaterial::Writer &writer, + WipeTowerWriter &writer, const box_coordinates &cleaning_box, float wipe_volume); }; @@ -374,4 +425,4 @@ private: }; // namespace Slic3r -#endif /* WipeTowerPrusaMM_hpp_ */ +#endif // WipeTowerPrusaMM_hpp_ diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 7a9bb785f..6615bff72 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -7,7 +7,7 @@ #include "I18N.hpp" #include "SupportMaterial.hpp" #include "GCode.hpp" -#include "GCode/WipeTowerPrusaMM.hpp" +#include "GCode/WipeTower.hpp" #include "Utils.hpp" //#include "PrintExport.hpp" @@ -1791,7 +1791,7 @@ void Print::_make_wipe_tower() this->throw_if_canceled(); // Initialize the wipe tower. - WipeTowerPrusaMM wipe_tower( + WipeTower wipe_tower( m_config.single_extruder_multi_material.value, float(m_config.wipe_tower_x.value), float(m_config.wipe_tower_y.value), float(m_config.wipe_tower_width.value), diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 65e06e432..6b55bd751 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -4773,7 +4773,7 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector& str_ { const Print *print; const std::vector *tool_colors; - WipeTower::xy wipe_tower_pos; + Vec2f wipe_tower_pos; float wipe_tower_angle; // Number of vertices (each vertex is 6x4=24 bytes long) @@ -4810,7 +4810,7 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector& str_ ctxt.final.emplace_back(*print->wipe_tower_data().final_purge.get()); ctxt.wipe_tower_angle = ctxt.print->config().wipe_tower_rotation_angle.value/180.f * PI; - ctxt.wipe_tower_pos = WipeTower::xy(ctxt.print->config().wipe_tower_x.value, ctxt.print->config().wipe_tower_y.value); + ctxt.wipe_tower_pos = Vec2f(ctxt.print->config().wipe_tower_x.value, ctxt.print->config().wipe_tower_y.value); BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start"; @@ -4872,19 +4872,19 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector& str_ WipeTower::Extrusion e_prev = extrusions.extrusions[i-1]; if (!extrusions.priming) { // wipe tower extrusions describe the wipe tower at the origin with no rotation - e_prev.pos.rotate(ctxt.wipe_tower_angle); - e_prev.pos.translate(ctxt.wipe_tower_pos); + e_prev.pos = Eigen::Rotation2Df(ctxt.wipe_tower_angle) * e_prev.pos; + e_prev.pos += ctxt.wipe_tower_pos; } for (; i < j; ++i) { WipeTower::Extrusion e = extrusions.extrusions[i]; assert(e.width > 0.f); if (!extrusions.priming) { - e.pos.rotate(ctxt.wipe_tower_angle); - e.pos.translate(ctxt.wipe_tower_pos); + e.pos = Eigen::Rotation2Df(ctxt.wipe_tower_angle) * e.pos; + e.pos += ctxt.wipe_tower_pos; } - lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y)); + lines.emplace_back(Point::new_scale(e_prev.pos.x(), e_prev.pos.y()), Point::new_scale(e.pos.x(), e.pos.y())); widths.emplace_back(e.width); e_prev = e; From a643a221511098350ffe3032e885b2216a1d43c1 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 17 Jun 2019 10:26:33 +0200 Subject: [PATCH 10/72] Wipe tower - renaming files (to conclude work from previous commit and not lose history of those files) --- src/libslic3r/CMakeLists.txt | 3 +-- src/libslic3r/GCode/{WipeTowerPrusaMM.cpp => WipeTower.cpp} | 0 src/libslic3r/GCode/{WipeTowerPrusaMM.hpp => WipeTower.hpp} | 0 3 files changed, 1 insertion(+), 2 deletions(-) rename src/libslic3r/GCode/{WipeTowerPrusaMM.cpp => WipeTower.cpp} (100%) rename src/libslic3r/GCode/{WipeTowerPrusaMM.hpp => WipeTower.hpp} (100%) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 312a82c4c..9c1e82b7a 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -81,9 +81,8 @@ add_library(libslic3r STATIC GCode/SpiralVase.hpp GCode/ToolOrdering.cpp GCode/ToolOrdering.hpp + GCode/WipeTower.cpp GCode/WipeTower.hpp - GCode/WipeTowerPrusaMM.cpp - GCode/WipeTowerPrusaMM.hpp GCode.cpp GCode.hpp GCodeReader.cpp diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.cpp b/src/libslic3r/GCode/WipeTower.cpp similarity index 100% rename from src/libslic3r/GCode/WipeTowerPrusaMM.cpp rename to src/libslic3r/GCode/WipeTower.cpp diff --git a/src/libslic3r/GCode/WipeTowerPrusaMM.hpp b/src/libslic3r/GCode/WipeTower.hpp similarity index 100% rename from src/libslic3r/GCode/WipeTowerPrusaMM.hpp rename to src/libslic3r/GCode/WipeTower.hpp From 1152bd5a09bb329caa7d8f3657fa33472b1ccaa9 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 17 Jun 2019 11:22:17 +0200 Subject: [PATCH 11/72] Fixup of 41164a9 The WipeTowerWriter did not now which tool is being used, so it limited the volumetric flow based on different filament settings --- src/libslic3r/GCode/WipeTower.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index e901ba296..52601886c 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -307,9 +307,6 @@ public: WipeTowerWriter& set_tool(int tool) { - char buf[64]; - sprintf(buf, "T%d\n", tool); - m_gcode += buf; m_current_tool = tool; return *this; } @@ -920,7 +917,7 @@ void WipeTower::toolchange_Change( // The toolchange Tn command will be inserted later, only in case that the user does // not provide a custom toolchange gcode. - //writer.set_tool(new_tool); + writer.set_tool(new_tool); // This outputs nothing, the writer just needs to know the tool has changed. writer.append("[start_filament_gcode]\n"); writer.flush_planner_queue(); From 95ad76a0dc45a380478bd4bdb3cc04e552950a5a Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 17 Jun 2019 12:59:30 +0200 Subject: [PATCH 12/72] Wipe tower - fixed a long existent bug that sometimes resulted in inexact feedrate on the loading moves --- src/libslic3r/GCode/WipeTower.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 52601886c..8f7f9f26c 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -251,12 +251,6 @@ public: m_gcode += "\n"; return *this; } - - // Derectract while moving in the X direction. - // If |x| > 0, the feed rate relates to the x distance, - // otherwise the feed rate relates to the e distance. - WipeTowerWriter& load_move_x(float x, float e, float f = 0.f) - { return extrude_explicit(x, m_current_pos.y(), e, f); } WipeTowerWriter& retract(float e, float f = 0.f) { return load(-e, f); } @@ -264,12 +258,18 @@ public: // Loads filament while also moving towards given points in x-axis (x feedrate is limited by cutting the distance short if necessary) WipeTowerWriter& load_move_x_advanced(float farthest_x, float loading_dist, float loading_speed, float max_x_speed = 50.f) { - float time = std::abs(loading_dist / loading_speed); - float x_speed = std::min(max_x_speed, std::abs(farthest_x - x()) / time); - float feedrate = 60.f * std::hypot(x_speed, loading_speed); + float time = std::abs(loading_dist / loading_speed); // time that the move must take + float x_distance = std::abs(farthest_x - x()); // max x-distance that we can travel + float x_speed = x_distance / time; // x-speed to do it in that time - float end_point = x() + (farthest_x > x() ? 1.f : -1.f) * x_speed * time; - return extrude_explicit(end_point, y(), loading_dist, feedrate); + if (x_speed > max_x_speed) { + // Necessary x_speed is too high - we must shorten the distance to achieve max_x_speed and still respect the time. + x_distance = max_x_speed * time; + x_speed = max_x_speed; + } + + float end_point = x() + (farthest_x > x() ? 1.f : -1.f) * x_distance; + return extrude_explicit(end_point, y(), loading_dist, x_speed * 60.f, false, false); } // Elevate the extruder head above the current print_z position. @@ -300,8 +300,8 @@ public: // at the current Y position to spread the leaking material. WipeTowerWriter& cool(float x1, float x2, float e1, float e2, float f) { - extrude_explicit(x1, m_current_pos.y(), e1, f); - extrude_explicit(x2, m_current_pos.y(), e2); + extrude_explicit(x1, m_current_pos.y(), e1, f, false, false); + extrude_explicit(x2, m_current_pos.y(), e2, false, false); return *this; } @@ -406,7 +406,7 @@ public: return *this; } - WipeTowerWriter& append(const char *text) { m_gcode += text; return *this; } + WipeTowerWriter& append(const std::string& text) { m_gcode += text; return *this; } private: Vec2f m_start_pos; @@ -825,7 +825,7 @@ void WipeTower::toolchange_Unload( const float e = m_filpar[m_current_tool].ramming_speed[i] * 0.25f / Filament_Area; // transform volume per sec to E move; const float dist = std::min(x - e_done, remaining); // distance to travel for either the next 0.25s, or to the next turnaround const float actual_time = dist/x * 0.25; - writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0, 0, e * (dist / x), std::hypot(dist, e * (dist / x)) / (actual_time / 60.)); + writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0, 0, e * (dist / x), dist / (actual_time / 60.)); remaining -= dist; if (remaining < WT_EPSILON) { // we reached a turning point From 82de7bedb12e8d8d1c1d5f69e596bb44917f9ba4 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 19 Jun 2019 14:51:17 +0200 Subject: [PATCH 13/72] WipeTowerDialog.cpp - wxEXPAND conflicted with wxALIGN_CENTER_HORIZONTAL (triggered a wxWidgets assert) --- src/slic3r/GUI/WipeTowerDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/WipeTowerDialog.cpp b/src/slic3r/GUI/WipeTowerDialog.cpp index 4c2b2480e..894b1ee62 100644 --- a/src/slic3r/GUI/WipeTowerDialog.cpp +++ b/src/slic3r/GUI/WipeTowerDialog.cpp @@ -172,7 +172,7 @@ void WipingPanel::format_sizer(wxSizer* sizer, wxPanel* page, wxGridSizer* grid_ wxSize text_size = GetTextExtent(info); auto info_str = new wxStaticText(page, wxID_ANY, info ,wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER); info_str->Wrap(int(0.6*text_size.x)); - sizer->Add( info_str, 0, wxALIGN_CENTER_HORIZONTAL | wxEXPAND); + sizer->Add( info_str, 0, wxEXPAND); auto table_sizer = new wxBoxSizer(wxVERTICAL); sizer->Add(table_sizer, 0, wxALIGN_CENTER | wxCENTER, table_lshift); table_sizer->Add(new wxStaticText(page, wxID_ANY, table_title), 0, wxALIGN_CENTER | wxTOP, 50); From 743a08f0cf52d65fead7be251a466f915d22aece Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Fri, 21 Jun 2019 10:58:20 +0200 Subject: [PATCH 14/72] WipeTower - fixed a crash in extrude_explicit when called from finish_layer before the first toolchange --- src/libslic3r/GCode/WipeTower.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index 8f7f9f26c..37e4040d1 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -1230,7 +1230,15 @@ void WipeTower::generate(std::vector> & make_wipe_tower_square(); m_layer_info = m_plan.begin(); - m_current_tool = (unsigned int)(-2); // we don't know which extruder to start with - we'll set it according to the first toolchange + + // we don't know which extruder to start with - we'll set it according to the first toolchange + for (const auto& layer : m_plan) { + if (!layer.tool_changes.empty()) { + m_current_tool = layer.tool_changes.front().old_tool; + break; + } + } + for (auto& used : m_used_filament_length) // reset used filament stats used = 0.f; @@ -1246,11 +1254,8 @@ void WipeTower::generate(std::vector> & if (!m_peters_wipe_tower && m_layer_info->depth < m_wipe_tower_depth - m_perimeter_width) m_y_shift = (m_wipe_tower_depth-m_layer_info->depth-m_perimeter_width)/2.f; - for (const auto &toolchange : layer.tool_changes) { - if (m_current_tool == (unsigned int)(-2)) - m_current_tool = toolchange.old_tool; + for (const auto &toolchange : layer.tool_changes) layer_result.emplace_back(tool_change(toolchange.new_tool, false)); - } if (! layer_finished()) { auto finish_layer_toolchange = finish_layer(); From 471331e8c17c57b23fd8f55e09416789ce53750b Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 25 Jun 2019 13:03:10 +0200 Subject: [PATCH 15/72] CMake: -Wignored-attributes is not supported in GCC<6.1 -turned off -Wunknown-pragmas for GCC --- CMakeLists.txt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ba264c8c3..346e2dfd5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -169,8 +169,19 @@ if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" STRE # On GCC and Clang, no return from a non-void function is a warning only. Here, we make it an error. add_compile_options(-Werror=return-type) - #removes LOTS of extraneous Eigen warnings - # add_compile_options(-Wno-ignored-attributes) # Tamas: Eigen include dirs are marked as SYSTEM + #removes LOTS of extraneous Eigen warnings (GCC only supports it since 6.1) + #if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 6.1) + # add_compile_options(-Wno-ignored-attributes) # Tamas: Eigen include dirs are marked as SYSTEM + #endif() + + #GCC generates loads of -Wunknown-pragmas when compiling igl. The fix is not easy due to a bug in gcc, see + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66943 or + # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431 + # We will turn the warning of for GCC for now: + if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") + add_compile_options(-Wno-unknown-pragmas) + endif() + if (SLIC3R_ASAN) add_compile_options(-fsanitize=address -fno-omit-frame-pointer) From cb916c4ddae18ccf13df15af03fdbb212cade047 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 25 Jun 2019 13:06:04 +0200 Subject: [PATCH 16/72] Fixed warnings in libslic3r --- src/libslic3r/EdgeGrid.cpp | 24 ++++++------ src/libslic3r/EdgeGrid.hpp | 4 +- src/libslic3r/ExtrusionSimulator.cpp | 16 ++++---- src/libslic3r/Fill/FillPlanePath.cpp | 6 +-- src/libslic3r/Fill/FillRectilinear2.cpp | 13 ++++--- src/libslic3r/Fill/FillRectilinear3.cpp | 6 +-- src/libslic3r/Format/3mf.cpp | 6 +-- src/libslic3r/Format/AMF.cpp | 8 ++-- src/libslic3r/Format/objparser.cpp | 4 +- src/libslic3r/GCode.cpp | 38 +++++++++--------- src/libslic3r/GCode/CoolingBuffer.cpp | 2 +- src/libslic3r/GCode/PreviewData.cpp | 4 ++ src/libslic3r/GCode/SpiralVase.cpp | 2 +- src/libslic3r/GCode/ToolOrdering.cpp | 6 +-- src/libslic3r/Geometry.cpp | 2 +- src/libslic3r/LayerRegion.cpp | 4 +- src/libslic3r/Line.cpp | 1 - src/libslic3r/MotionPlanner.cpp | 2 +- src/libslic3r/PerimeterGenerator.cpp | 4 +- src/libslic3r/PlaceholderParser.cpp | 6 +-- src/libslic3r/PolylineCollection.cpp | 2 +- src/libslic3r/Print.cpp | 11 +++--- src/libslic3r/Print.hpp | 2 +- src/libslic3r/PrintConfig.cpp | 51 ++++++++++++------------- src/libslic3r/PrintObject.cpp | 15 +++----- src/libslic3r/SLA/SLAAutoSupports.cpp | 9 ++--- src/libslic3r/SLAPrint.cpp | 2 - src/libslic3r/Slicing.cpp | 4 -- src/libslic3r/SupportMaterial.cpp | 18 ++++----- src/libslic3r/utils.cpp | 4 +- 30 files changed, 129 insertions(+), 147 deletions(-) diff --git a/src/libslic3r/EdgeGrid.cpp b/src/libslic3r/EdgeGrid.cpp index c34aca8f2..9d02ef09b 100644 --- a/src/libslic3r/EdgeGrid.cpp +++ b/src/libslic3r/EdgeGrid.cpp @@ -146,10 +146,10 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) coord_t iy = p1(1) / m_resolution; coord_t ixb = p2(0) / m_resolution; coord_t iyb = p2(1) / m_resolution; - assert(ix >= 0 && ix < m_cols); - assert(iy >= 0 && iy < m_rows); - assert(ixb >= 0 && ixb < m_cols); - assert(iyb >= 0 && iyb < m_rows); + assert(ix >= 0 && size_t(ix) < m_cols); + assert(iy >= 0 && size_t(iy) < m_rows); + assert(ixb >= 0 && size_t(ixb) < m_cols); + assert(iyb >= 0 && size_t(iyb) < m_rows); // Account for the end points. ++ m_cells[iy*m_cols+ix].end; if (ix == ixb && iy == iyb) @@ -290,10 +290,10 @@ void EdgeGrid::Grid::create_from_m_contours(coord_t resolution) coord_t iy = p1(1) / m_resolution; coord_t ixb = p2(0) / m_resolution; coord_t iyb = p2(1) / m_resolution; - assert(ix >= 0 && ix < m_cols); - assert(iy >= 0 && iy < m_rows); - assert(ixb >= 0 && ixb < m_cols); - assert(iyb >= 0 && iyb < m_rows); + assert(ix >= 0 && size_t(ix) < m_cols); + assert(iy >= 0 && size_t(iy) < m_rows); + assert(ixb >= 0 && size_t(ixb) < m_cols); + assert(iyb >= 0 && size_t(iyb) < m_rows); // Account for the end points. m_cell_data[m_cells[iy*m_cols + ix].end++] = std::pair(i, j); if (ix == ixb && iy == iyb) @@ -775,11 +775,11 @@ void EdgeGrid::Grid::calculate_sdf() // For each corner of this cell and its 1 ring neighbours: for (int corner_y = -1; corner_y < 3; ++ corner_y) { coord_t corner_r = r + corner_y; - if (corner_r < 0 || corner_r >= nrows) + if (corner_r < 0 || (size_t)corner_r >= nrows) continue; for (int corner_x = -1; corner_x < 3; ++ corner_x) { coord_t corner_c = c + corner_x; - if (corner_c < 0 || corner_c >= ncols) + if (corner_c < 0 || (size_t)corner_c >= ncols) continue; float &d_min = m_signed_distance_field[corner_r * ncols + corner_c]; Slic3r::Point pt(m_bbox.min(0) + corner_c * m_resolution, m_bbox.min(1) + corner_r * m_resolution); @@ -1137,9 +1137,9 @@ bool EdgeGrid::Grid::signed_distance_edges(const Point &pt, coord_t search_radiu return false; bbox.max(0) /= m_resolution; bbox.max(1) /= m_resolution; - if (bbox.max(0) >= m_cols) + if ((size_t)bbox.max(0) >= m_cols) bbox.max(0) = m_cols - 1; - if (bbox.max(1) >= m_rows) + if ((size_t)bbox.max(1) >= m_rows) bbox.max(1) = m_rows - 1; // Lower boundary, round to grid and test validity. bbox.min(0) -= search_radius; diff --git a/src/libslic3r/EdgeGrid.hpp b/src/libslic3r/EdgeGrid.hpp index f99ab30c4..7faafdb3e 100644 --- a/src/libslic3r/EdgeGrid.hpp +++ b/src/libslic3r/EdgeGrid.hpp @@ -78,8 +78,8 @@ protected: #endif bool cell_inside_or_crossing(int r, int c) const { - if (r < 0 || r >= m_rows || - c < 0 || c >= m_cols) + if (r < 0 || (size_t)r >= m_rows || + c < 0 || (size_t)c >= m_cols) // The cell is outside the domain. Hoping that the contours were correctly oriented, so // there is a CCW outmost contour so the out of domain cells are outside. return false; diff --git a/src/libslic3r/ExtrusionSimulator.cpp b/src/libslic3r/ExtrusionSimulator.cpp index fcb2fe825..8c2ab037f 100644 --- a/src/libslic3r/ExtrusionSimulator.cpp +++ b/src/libslic3r/ExtrusionSimulator.cpp @@ -660,7 +660,7 @@ void gcode_spread_points( for (ExtrusionPoints::const_iterator it = points.begin(); it != points.end(); ++ it) { const V2f ¢er = it->center; const float radius = it->radius; - const float radius2 = radius * radius; + //const float radius2 = radius * radius; const float height_target = it->height; B2f bbox(center - V2f(radius, radius), center + V2f(radius, radius)); B2i bboxi( @@ -774,8 +774,8 @@ void gcode_spread_points( } } #endif - float area_circle_total2 = float(M_PI) * sqr(radius); - float area_err = fabs(area_circle_total2 - area_circle_total) / area_circle_total2; +// float area_circle_total2 = float(M_PI) * sqr(radius); +// float area_err = fabs(area_circle_total2 - area_circle_total) / area_circle_total2; // printf("area_circle_total: %f, %f, %f\n", area_circle_total, area_circle_total2, area_err); float volume_full = float(M_PI) * sqr(radius) * height_target; // if (true) { @@ -905,8 +905,8 @@ void ExtrusionSimulator::set_image_size(const Point &image_size) // printf("Allocating image data, allocated\n"); //FIXME fill the image with red vertical lines. - for (size_t r = 0; r < image_size.y(); ++ r) { - for (size_t c = 0; c < image_size.x(); c += 2) { + for (size_t r = 0; r < size_t(image_size.y()); ++ r) { + for (size_t c = 0; c < size_t(image_size.x()); c += 2) { // Color red pimpl->image_data[r * image_size.x() * 4 + c * 4] = 255; // Opacity full @@ -958,7 +958,7 @@ void ExtrusionSimulator::extrude_to_accumulator(const ExtrusionPath &path, const float scalex = float(viewport.size().x()) / float(bbox.size().x()); float scaley = float(viewport.size().y()) / float(bbox.size().y()); float w = scale_(path.width) * scalex; - float h = scale_(path.height) * scalex; + //float h = scale_(path.height) * scalex; w = scale_(path.mm3_per_mm / path.height) * scalex; // printf("scalex: %f, scaley: %f\n", scalex, scaley); // printf("bbox: %d,%d %d,%d\n", bbox.min.x(), bbox.min.y, bbox.max.x(), bbox.max.y); @@ -993,8 +993,8 @@ void ExtrusionSimulator::evaluate_accumulator(ExtrusionSimulationType simulation for (int r = 0; r < sz.y(); ++r) { for (int c = 0; c < sz.x(); ++c) { float p = 0; - for (int j = 0; j < pimpl->bitmap_oversampled; ++ j) { - for (int i = 0; i < pimpl->bitmap_oversampled; ++ i) { + for (unsigned int j = 0; j < pimpl->bitmap_oversampled; ++ j) { + for (unsigned int i = 0; i < pimpl->bitmap_oversampled; ++ i) { if (pimpl->bitmap[r * pimpl->bitmap_oversampled + j][c * pimpl->bitmap_oversampled + i]) p += 1.f; } diff --git a/src/libslic3r/Fill/FillPlanePath.cpp b/src/libslic3r/Fill/FillPlanePath.cpp index c52353b02..3b9266a0f 100644 --- a/src/libslic3r/Fill/FillPlanePath.cpp +++ b/src/libslic3r/Fill/FillPlanePath.cpp @@ -130,14 +130,14 @@ static inline Point hilbert_n_to_xy(const size_t n) } } int state = (ndigits & 1) ? 4 : 0; - int dirstate = (ndigits & 1) ? 0 : 4; +// int dirstate = (ndigits & 1) ? 0 : 4; coord_t x = 0; coord_t y = 0; for (int i = (int)ndigits - 1; i >= 0; -- i) { int digit = (n >> (i * 2)) & 3; state += digit; - if (digit != 3) - dirstate = state; // lowest non-3 digit +// if (digit != 3) +// dirstate = state; // lowest non-3 digit x |= digit_to_x[state] << i; y |= digit_to_y[state] << i; state = next_state[state]; diff --git a/src/libslic3r/Fill/FillRectilinear2.cpp b/src/libslic3r/Fill/FillRectilinear2.cpp index 65440d0ef..8aea75886 100644 --- a/src/libslic3r/Fill/FillRectilinear2.cpp +++ b/src/libslic3r/Fill/FillRectilinear2.cpp @@ -287,7 +287,8 @@ public: assert(aoffset1 < 0); assert(aoffset2 < 0); assert(aoffset2 < aoffset1); - bool sticks_removed = remove_sticks(polygons_src); +// bool sticks_removed = + remove_sticks(polygons_src); // if (sticks_removed) printf("Sticks removed!\n"); polygons_outer = offset(polygons_src, aoffset1, ClipperLib::jtMiter, @@ -481,7 +482,7 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical { // This routine will propose a connecting line even if the connecting perimeter segment intersects // iVertical line multiple times before reaching iIntersectionOther. - if (iIntersectionOther == -1) + if (iIntersectionOther == size_t(-1)) return INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED; assert(dir_is_next ? (iVerticalLine + 1 < segs.size()) : (iVerticalLine > 0)); const SegmentedIntersectionLine &il_this = segs[iVerticalLine]; @@ -858,8 +859,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP if (il > ir) // No vertical line intersects this segment. continue; - assert(il >= 0 && il < segs.size()); - assert(ir >= 0 && ir < segs.size()); + assert(il >= 0 && size_t(il) < segs.size()); + assert(ir >= 0 && size_t(ir) < segs.size()); for (int i = il; i <= ir; ++ i) { coord_t this_x = segs[i].pos; assert(this_x == i * line_spacing + x0); @@ -1159,8 +1160,8 @@ bool FillRectilinear2::fill_surface_by_lines(const Surface *surface, const FillP int iSegAbove = -1; int iSegBelow = -1; { - SegmentIntersection::SegmentIntersectionType type_crossing = (intrsctn->type == SegmentIntersection::INNER_LOW) ? - SegmentIntersection::INNER_HIGH : SegmentIntersection::INNER_LOW; +// SegmentIntersection::SegmentIntersectionType type_crossing = (intrsctn->type == SegmentIntersection::INNER_LOW) ? +// SegmentIntersection::INNER_HIGH : SegmentIntersection::INNER_LOW; // Does the perimeter intersect the current vertical line above intrsctn? for (size_t i = i_intersection + 1; i + 1 < seg.intersections.size(); ++ i) // if (seg.intersections[i].iContour == intrsctn->iContour && seg.intersections[i].type == type_crossing) { diff --git a/src/libslic3r/Fill/FillRectilinear3.cpp b/src/libslic3r/Fill/FillRectilinear3.cpp index dab584298..078feeae9 100644 --- a/src/libslic3r/Fill/FillRectilinear3.cpp +++ b/src/libslic3r/Fill/FillRectilinear3.cpp @@ -849,7 +849,7 @@ static inline IntersectionTypeOtherVLine intersection_type_on_prev_next_vertical { // This routine will propose a connecting line even if the connecting perimeter segment intersects // iVertical line multiple times before reaching iIntersectionOther. - if (iIntersectionOther == -1) + if (iIntersectionOther == size_t(-1)) return INTERSECTION_TYPE_OTHER_VLINE_UNDEFINED; assert(dir_is_next ? (iVerticalLine + 1 < segs.size()) : (iVerticalLine > 0)); const SegmentedIntersectionLine &il_this = segs[iVerticalLine]; @@ -1284,8 +1284,8 @@ static bool fill_hatching_segments_legacy( int iSegAbove = -1; int iSegBelow = -1; { - SegmentIntersection::SegmentIntersectionType type_crossing = (intrsctn->type == SegmentIntersection::INNER_LOW) ? - SegmentIntersection::INNER_HIGH : SegmentIntersection::INNER_LOW; +// SegmentIntersection::SegmentIntersectionType type_crossing = (intrsctn->type == SegmentIntersection::INNER_LOW) ? +// SegmentIntersection::INNER_HIGH : SegmentIntersection::INNER_LOW; // Does the perimeter intersect the current vertical line above intrsctn? for (size_t i = i_intersection + 1; i + 1 < seg.intersections.size(); ++ i) // if (seg.intersections[i].iContour == intrsctn->iContour && seg.intersections[i].type == type_crossing) { diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 4793586e3..1807eb54f 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -675,7 +675,7 @@ namespace Slic3r { if (!XML_ParseBuffer(m_xml_parser, (int)stat.m_uncomp_size, 1)) { char error_buf[1024]; - ::sprintf(error_buf, "Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(m_xml_parser)), XML_GetCurrentLineNumber(m_xml_parser)); + ::sprintf(error_buf, "Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(m_xml_parser)), (int)XML_GetCurrentLineNumber(m_xml_parser)); add_error(error_buf); return false; } @@ -895,7 +895,7 @@ namespace Slic3r { if (!XML_ParseBuffer(m_xml_parser, (int)stat.m_uncomp_size, 1)) { char error_buf[1024]; - ::sprintf(error_buf, "Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(m_xml_parser)), XML_GetCurrentLineNumber(m_xml_parser)); + ::sprintf(error_buf, "Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(m_xml_parser)), (int)XML_GetCurrentLineNumber(m_xml_parser)); add_error(error_buf); return false; } @@ -1452,7 +1452,7 @@ namespace Slic3r { object->second.metadata.emplace_back(key, value); else if (type == VOLUME_TYPE) { - if (m_curr_config.volume_id < object->second.volumes.size()) + if (size_t(m_curr_config.volume_id) < object->second.volumes.size()) object->second.volumes[m_curr_config.volume_id].metadata.emplace_back(key, value); } else diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index a33d21c9f..5dced91b8 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -694,8 +694,8 @@ bool load_amf_file(const char *path, DynamicPrintConfig *config, Model *model) } int done = feof(pFile); if (XML_Parse(parser, buff, len, done) == XML_STATUS_ERROR) { - printf("AMF parser: Parse error at line %ul:\n%s\n", - XML_GetCurrentLineNumber(parser), + printf("AMF parser: Parse error at line %d:\n%s\n", + (int)XML_GetCurrentLineNumber(parser), XML_ErrorString(XML_GetErrorCode(parser))); break; } @@ -753,7 +753,7 @@ bool extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_fi if (!XML_ParseBuffer(parser, (int)stat.m_uncomp_size, 1)) { - printf("Error (%s) while parsing xml file at line %d\n", XML_ErrorString(XML_GetErrorCode(parser)), XML_GetCurrentLineNumber(parser)); + printf("Error (%s) while parsing xml file at line %d\n", XML_ErrorString(XML_GetErrorCode(parser)), (int)XML_GetCurrentLineNumber(parser)); close_zip_reader(&archive); return false; } @@ -959,7 +959,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) stream << " 1\n"; stream << " " << ModelVolume::type_to_string(volume->type()) << "\n"; const indexed_triangle_set &its = volume->mesh().its; - for (size_t i = 0; i < (int)its.indices.size(); ++i) { + for (size_t i = 0; i < its.indices.size(); ++i) { stream << " \n"; for (int j = 0; j < 3; ++j) stream << " " << its.indices[i][j] + vertices_offset << "\n"; diff --git a/src/libslic3r/Format/objparser.cpp b/src/libslic3r/Format/objparser.cpp index 88dfae695..62bcd306e 100644 --- a/src/libslic3r/Format/objparser.cpp +++ b/src/libslic3r/Format/objparser.cpp @@ -176,8 +176,7 @@ static bool obj_parseline(const char *line, ObjData &data) EATWS(); if (*line == 0) return false; - // number of vertices of this face - int n = 0; + // current vertex to be parsed ObjVertex vertex; char *endptr = 0; @@ -266,7 +265,6 @@ static bool obj_parseline(const char *line, ObjData &data) { // o [object name] EATWS(); - const char *name = line; while (*line != ' ' && *line != '\t' && *line != 0) ++ line; // copy name to line. diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index c42669de0..23a8999c4 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -316,10 +316,10 @@ std::string WipeTowerIntegration::prime(GCode &gcodegen) std::string WipeTowerIntegration::tool_change(GCode &gcodegen, int extruder_id, bool finish_layer) { std::string gcode; - assert(m_layer_idx >= 0 && m_layer_idx <= m_tool_changes.size()); + assert(m_layer_idx >= 0 && size_t(m_layer_idx) <= m_tool_changes.size()); if (! m_brim_done || gcodegen.writer().need_toolchange(extruder_id) || finish_layer) { - if (m_layer_idx < m_tool_changes.size()) { - assert(m_tool_change_idx < m_tool_changes[m_layer_idx].size()); + if (m_layer_idx < (int)m_tool_changes.size()) { + assert(size_t(m_tool_change_idx) < m_tool_changes[m_layer_idx].size()); gcode += append_tcr(gcodegen, m_tool_changes[m_layer_idx][m_tool_change_idx++], extruder_id); } m_brim_done = true; @@ -1253,7 +1253,7 @@ void GCode::_print_first_layer_extruder_temperatures(FILE *file, Print &print, c int temp = print.config().first_layer_temperature.get_at(first_printing_extruder_id); if (temp_by_gcode >= 0 && temp_by_gcode < 1000) temp = temp_by_gcode; - m_writer.set_temperature(temp_by_gcode, wait, first_printing_extruder_id); + m_writer.set_temperature(temp, wait, first_printing_extruder_id); } else { // Custom G-code does not set the extruder temperature. Do it now. if (print.config().single_extruder_multi_material.value) { @@ -1344,11 +1344,11 @@ void GCode::process_layer( // Check whether it is possible to apply the spiral vase logic for this layer. // Just a reminder: A spiral vase mode is allowed for a single object, single material print only. if (m_spiral_vase && layers.size() == 1 && support_layer == nullptr) { - bool enable = (layer.id() > 0 || print.config().brim_width.value == 0.) && (layer.id() >= print.config().skirt_height.value && ! print.has_infinite_skirt()); + bool enable = (layer.id() > 0 || print.config().brim_width.value == 0.) && (layer.id() >= (size_t)print.config().skirt_height.value && ! print.has_infinite_skirt()); if (enable) { for (const LayerRegion *layer_region : layer.regions()) - if (layer_region->region()->config().bottom_solid_layers.value > layer.id() || - layer_region->perimeters.items_count() > 1 || + if (size_t(layer_region->region()->config().bottom_solid_layers.value) > layer.id() || + layer_region->perimeters.items_count() > 1u || layer_region->fills.items_count() > 0) { enable = false; break; @@ -1414,11 +1414,11 @@ void GCode::process_layer( bool extrude_skirt = ! print.skirt().entities.empty() && // Not enough skirt layers printed yet. - (m_skirt_done.size() < print.config().skirt_height.value || print.has_infinite_skirt()) && + (m_skirt_done.size() < (size_t)print.config().skirt_height.value || print.has_infinite_skirt()) && // This print_z has not been extruded yet (m_skirt_done.empty() ? 0. : m_skirt_done.back()) < print_z - EPSILON && // and this layer is the 1st layer, or it is an object layer, or it is a raft layer. - (first_layer || object_layer != nullptr || support_layer->id() < m_config.raft_layers.value); + (first_layer || object_layer != nullptr || support_layer->id() < (size_t)m_config.raft_layers.value); std::map> skirt_loops_per_extruder; coordf_t skirt_height = 0.; if (extrude_skirt) { @@ -2067,19 +2067,18 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // Retrieve the last start position for this object. float last_pos_weight = 1.f; - switch (seam_position) { - case spAligned: + + if (seam_position == spAligned) { // Seam is aligned to the seam at the preceding layer. if (m_layer != NULL && m_seam_position.count(m_layer->object()) > 0) { last_pos = m_seam_position[m_layer->object()]; last_pos_weight = 1.f; } - break; - case spRear: + } + else if (seam_position == spRear) { last_pos = m_layer->object()->bounding_box().center(); last_pos(1) += coord_t(3. * m_layer->object()->bounding_box().radius()); last_pos_weight = 5.f; - break; } // Insert a projection of last_pos into the polygon. @@ -2088,7 +2087,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou Points::iterator it = project_point_to_polygon_and_insert(polygon, last_pos, 0.1 * nozzle_r); last_pos_proj_idx = it - polygon.points.begin(); } - Point last_pos_proj = polygon.points[last_pos_proj_idx]; + // Parametrize the polygon by its length. std::vector lengths = polygon_parameter_by_length(polygon); @@ -2098,7 +2097,6 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // No penalty for reflex points, slight penalty for convex points, high penalty for flat surfaces. const float penaltyConvexVertex = 1.f; const float penaltyFlatSurface = 5.f; - const float penaltySeam = 1.3f; const float penaltyOverhangHalf = 10.f; // Penalty for visible seams. for (size_t i = 0; i < polygon.points.size(); ++ i) { @@ -2143,10 +2141,14 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // Signed distance is positive outside the object, negative inside the object. // The point is considered at an overhang, if it is more than nozzle radius // outside of the lower layer contour. - bool found = (*lower_layer_edge_grid)->signed_distance(p, search_r, dist); + #ifdef NDEBUG // to suppress unused variable warning in release mode + (*lower_layer_edge_grid)->signed_distance(p, search_r, dist); + #else + bool found = (*lower_layer_edge_grid)->signed_distance(p, search_r, dist); + #endif // If the approximate Signed Distance Field was initialized over lower_layer_edge_grid, // then the signed distnace shall always be known. - assert(found); + assert(found); penalties[i] += extrudate_overlap_penalty(float(nozzle_r), penaltyOverhangHalf, float(dist)); } } diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 552fbf88c..1aa15ef33 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -672,7 +672,7 @@ std::string CoolingBuffer::apply_layer_cooldown( #define EXTRUDER_CONFIG(OPT) config.OPT.get_at(m_current_extruder) int min_fan_speed = EXTRUDER_CONFIG(min_fan_speed); int fan_speed_new = EXTRUDER_CONFIG(fan_always_on) ? min_fan_speed : 0; - if (layer_id >= EXTRUDER_CONFIG(disable_fan_first_layers)) { + if (layer_id >= (size_t)EXTRUDER_CONFIG(disable_fan_first_layers)) { int max_fan_speed = EXTRUDER_CONFIG(max_fan_speed); float slowdown_below_layer_time = float(EXTRUDER_CONFIG(slowdown_below_layer_time)); float fan_below_layer_time = float(EXTRUDER_CONFIG(fan_below_layer_time)); diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp index 8eba6801e..4b9604075 100644 --- a/src/libslic3r/GCode/PreviewData.cpp +++ b/src/libslic3r/GCode/PreviewData.cpp @@ -404,6 +404,8 @@ std::string GCodePreviewData::get_legend_title() const return L("Tool"); case Extrusion::ColorPrint: return L("Color Print"); + case Extrusion::Num_View_Types: + break; // just to supress warning about non-handled value } return ""; @@ -505,6 +507,8 @@ GCodePreviewData::LegendItemsList GCodePreviewData::get_legend_items(const std:: } break; } + case Extrusion::Num_View_Types: + break; // just to supress warning about non-handled value } return items; diff --git a/src/libslic3r/GCode/SpiralVase.cpp b/src/libslic3r/GCode/SpiralVase.cpp index 8e8ae3075..a4ae42b31 100644 --- a/src/libslic3r/GCode/SpiralVase.cpp +++ b/src/libslic3r/GCode/SpiralVase.cpp @@ -24,7 +24,7 @@ std::string SpiralVase::process_layer(const std::string &gcode) // Get total XY length for this layer by summing all extrusion moves. float total_layer_length = 0; float layer_height = 0; - float z; + float z = 0.f; bool set_z = false; { diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index e25ad91fe..f10b45723 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -324,9 +324,8 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ m_layer_tools[j].has_wipe_tower = true; } else { LayerTools <_extra = *m_layer_tools.insert(m_layer_tools.begin() + j, lt_new); - LayerTools <_prev = m_layer_tools[j - 1]; LayerTools <_next = m_layer_tools[j + 1]; - assert(! lt_prev.extruders.empty() && ! lt_next.extruders.empty()); + assert(! m_layer_tools[j - 1].extruders.empty() && ! lt_next.extruders.empty()); // FIXME: Following assert tripped when running combine_infill.t. I decided to comment it out for now. // If it is a bug, it's likely not critical, because this code is unchanged for a long time. It might // still be worth looking into it more and decide if it is a bug or an obsolete assert. @@ -495,9 +494,6 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int if (!is_overriddable(*fill, print.config(), *object, region)) continue; - // What extruder would this normally be printed with? - unsigned int correct_extruder = Print::get_extruder(*fill, region); - if (volume_to_wipe<=0) continue; diff --git a/src/libslic3r/Geometry.cpp b/src/libslic3r/Geometry.cpp index accb4e286..cc8a86a96 100644 --- a/src/libslic3r/Geometry.cpp +++ b/src/libslic3r/Geometry.cpp @@ -912,7 +912,7 @@ MedialAxis::build(ThickPolylines* polylines) } */ - typedef const VD::vertex_type vert_t; + //typedef const VD::vertex_type vert_t; typedef const VD::edge_type edge_t; // collect valid edges (i.e. prune those not belonging to MAT) diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index 19fbb3bb6..caf8dc20f 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -201,7 +201,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) size_t n_groups = 0; for (size_t i = 0; i < bridges.size(); ++ i) { // A grup id for this bridge. - size_t group_id = (bridge_group[i] == -1) ? (n_groups ++) : bridge_group[i]; + size_t group_id = (bridge_group[i] == size_t(-1)) ? (n_groups ++) : bridge_group[i]; bridge_group[i] = group_id; // For all possibly overlaping bridges: for (size_t j = i + 1; j < bridges.size(); ++ j) { @@ -210,7 +210,7 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) if (intersection(bridges_grown[i], bridges_grown[j], false).empty()) continue; // The two bridge regions intersect. Give them the same group id. - if (bridge_group[j] != -1) { + if (bridge_group[j] != size_t(-1)) { // The j'th bridge has been merged with some other bridge before. size_t group_id_new = bridge_group[j]; for (size_t k = 0; k < j; ++ k) diff --git a/src/libslic3r/Line.cpp b/src/libslic3r/Line.cpp index 02f1cb7c2..baa04795a 100644 --- a/src/libslic3r/Line.cpp +++ b/src/libslic3r/Line.cpp @@ -22,7 +22,6 @@ Linef3 transform(const Linef3& line, const Transform3d& t) bool Line::intersection_infinite(const Line &other, Point* point) const { Vec2d a1 = this->a.cast(); - Vec2d a2 = other.a.cast(); Vec2d v12 = (other.a - this->a).cast(); Vec2d v1 = (this->b - this->a).cast(); Vec2d v2 = (other.b - other.a).cast(); diff --git a/src/libslic3r/MotionPlanner.cpp b/src/libslic3r/MotionPlanner.cpp index 198c39f31..dd3443879 100644 --- a/src/libslic3r/MotionPlanner.cpp +++ b/src/libslic3r/MotionPlanner.cpp @@ -332,7 +332,7 @@ Polyline MotionPlannerGraph::shortest_path(size_t node_start, size_t node_end) c queue.pop(); map_node_to_queue_id[u] = size_t(-1); // Stop searching if we reached our destination. - if (u == node_end) + if (size_t(u) == node_end) break; // Visit each edge starting at node u. for (const Neighbor& neighbor : m_adjacency_list[u]) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index de8aeeb2a..4dac192ad 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -175,7 +175,7 @@ void PerimeterGenerator::process() const PerimeterGeneratorLoop &loop = contours_d[i]; // find the contour loop that contains it for (int t = d - 1; t >= 0; -- t) { - for (int j = 0; j < contours[t].size(); ++ j) { + for (size_t j = 0; j < contours[t].size(); ++ j) { PerimeterGeneratorLoop &candidate_parent = contours[t][j]; if (candidate_parent.polygon.contains(loop.polygon.first_point())) { candidate_parent.children.push_back(loop); @@ -397,7 +397,7 @@ static inline ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyli pp.push_back(line.b); width.push_back(line.b_width); - assert(pp.size() == segments + 1); + assert(pp.size() == segments + 1u); assert(width.size() == segments*2); } diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index ae66178aa..2bfe9b745 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -521,7 +521,6 @@ namespace client static void regex_op(expr &lhs, boost::iterator_range &rhs, char op) { const std::string *subject = nullptr; - const std::string *mask = nullptr; if (lhs.type == TYPE_STRING) { // One type is string, the other could be converted to string. subject = &lhs.s(); @@ -563,7 +562,6 @@ namespace client static void ternary_op(expr &lhs, expr &rhs1, expr &rhs2) { - bool value = false; if (lhs.type != TYPE_BOOL) lhs.throw_exception("Not a boolean expression"); if (lhs.b()) @@ -975,7 +973,7 @@ namespace client // depending on the context->just_boolean_expression flag. This way a single static expression parser // could serve both purposes. start = eps[px::bind(&MyContext::evaluate_full_macro, _r1, _a)] > - ( eps(_a==true) > text_block(_r1) [_val=_1] + ( (eps(_a==true) > text_block(_r1) [_val=_1]) | conditional_expression(_r1) [ px::bind(&expr::evaluate_boolean_to_string, _1, _val) ] ) > eoi; start.name("start"); @@ -1245,7 +1243,7 @@ static std::string process_macro(const std::string &templ, client::MyContext &co std::string::const_iterator end = templ.end(); // Accumulator for the processed template. std::string output; - bool res = phrase_parse(iter, end, macro_processor_instance(&context), space, output); + phrase_parse(iter, end, macro_processor_instance(&context), space, output); if (!context.error_message.empty()) { if (context.error_message.back() != '\n' && context.error_message.back() != '\r') context.error_message += '\n'; diff --git a/src/libslic3r/PolylineCollection.cpp b/src/libslic3r/PolylineCollection.cpp index 1304161c3..0c66c371a 100644 --- a/src/libslic3r/PolylineCollection.cpp +++ b/src/libslic3r/PolylineCollection.cpp @@ -61,7 +61,7 @@ Polylines PolylineCollection::_chained_path_from( while (! endpoints.empty()) { // find nearest point int endpoint_index = nearest_point_index(endpoints, start_near, no_reverse); - assert(endpoint_index >= 0 && endpoint_index < endpoints.size() * 2); + assert(endpoint_index >= 0 && size_t(endpoint_index) < endpoints.size() * 2); if (move_from_src) { retval.push_back(std::move(src[endpoints[endpoint_index/2].idx])); } else { diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index f9129f15a..876523bf5 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -18,7 +18,7 @@ #include #include -//! macro used to mark string used at localization, +//! macro used to mark string used at localization, //! return same string #define L(s) Slic3r::I18N::translate(s) @@ -1116,7 +1116,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co region_id = map_volume_to_region[volume_id]; // Assign volume to a region. if (fresh) { - if (region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty()) + if ((size_t)region_id >= print_object.region_volumes.size() || print_object.region_volumes[region_id].empty()) ++ m_regions[region_id]->m_refcnt; print_object.add_region_volume(region_id, volume_id); } @@ -1259,11 +1259,10 @@ std::string Print::validate() const if (has_custom_layering) { const std::vector &layer_height_profile_tallest = layer_height_profiles[tallest_object_idx]; for (size_t idx_object = 0; idx_object < m_objects.size(); ++ idx_object) { - const PrintObject *object = m_objects[idx_object]; const std::vector &layer_height_profile = layer_height_profiles[idx_object]; bool failed = false; if (layer_height_profile_tallest.size() >= layer_height_profile.size()) { - int i = 0; + size_t i = 0; while (i < layer_height_profile.size() && i < layer_height_profile_tallest.size()) { if (std::abs(layer_height_profile_tallest[i] - layer_height_profile[i])) { failed = true; @@ -1628,7 +1627,7 @@ void Print::_make_skirt() } // Number of skirt loops per skirt layer. - int n_skirts = m_config.skirts.value; + size_t n_skirts = m_config.skirts.value; if (this->has_infinite_skirt() && n_skirts == 0) n_skirts = 1; @@ -1640,7 +1639,7 @@ void Print::_make_skirt() // Draw outlines from outside to inside. // Loop while we have less skirts than required or any extruder hasn't reached the min length if any. std::vector extruded_length(extruders.size(), 0.); - for (int i = n_skirts, extruder_idx = 0; i > 0; -- i) { + for (size_t i = n_skirts, extruder_idx = 0; i > 0; -- i) { this->throw_if_canceled(); // Offset the skirt outside. distance += coord_t(scale_(spacing)); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 53d6d692d..42430f59e 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -120,7 +120,7 @@ public: void clear_support_layers(); SupportLayer* get_support_layer(int idx) { return m_support_layers[idx]; } SupportLayer* add_support_layer(int id, coordf_t height, coordf_t print_z); - SupportLayerPtrs::const_iterator insert_support_layer(SupportLayerPtrs::const_iterator pos, int id, coordf_t height, coordf_t print_z, coordf_t slice_z); + SupportLayerPtrs::const_iterator insert_support_layer(SupportLayerPtrs::const_iterator pos, size_t id, coordf_t height, coordf_t print_z, coordf_t slice_z); void delete_support_layer(int idx); // Initialize the layer_height_profile from the model_object's layer_height_profile, from model_object's layer height table, or from slicing parameters. diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 89e21934a..fbf3c7ab6 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -36,7 +36,6 @@ PrintConfigDef::PrintConfigDef() void PrintConfigDef::init_common_params() { - t_optiondef_map &Options = this->options; ConfigOptionDef* def; def = this->add("printer_technology", coEnum); @@ -102,7 +101,6 @@ void PrintConfigDef::init_common_params() void PrintConfigDef::init_fff_params() { - t_optiondef_map &Options = this->options; ConfigOptionDef* def; // Maximum extruder temperature, bumped to 1500 to support printing of glass. @@ -1085,16 +1083,16 @@ void PrintConfigDef::init_fff_params() // Add the machine feedrate limits for XYZE axes. (M203) def = this->add("machine_max_feedrate_" + axis.name, coFloats); def->full_label = (boost::format("Maximum feedrate %1%") % axis_upper).str(); - L("Maximum feedrate X"); - L("Maximum feedrate Y"); - L("Maximum feedrate Z"); - L("Maximum feedrate E"); + (void)L("Maximum feedrate X"); + (void)L("Maximum feedrate Y"); + (void)L("Maximum feedrate Z"); + (void)L("Maximum feedrate E"); def->category = L("Machine limits"); def->tooltip = (boost::format("Maximum feedrate of the %1% axis") % axis_upper).str(); - L("Maximum feedrate of the X axis"); - L("Maximum feedrate of the Y axis"); - L("Maximum feedrate of the Z axis"); - L("Maximum feedrate of the E axis"); + (void)L("Maximum feedrate of the X axis"); + (void)L("Maximum feedrate of the Y axis"); + (void)L("Maximum feedrate of the Z axis"); + (void)L("Maximum feedrate of the E axis"); def->sidetext = L("mm/s"); def->min = 0; def->width = machine_limits_opt_width; @@ -1103,16 +1101,16 @@ void PrintConfigDef::init_fff_params() // Add the machine acceleration limits for XYZE axes (M201) def = this->add("machine_max_acceleration_" + axis.name, coFloats); def->full_label = (boost::format("Maximum acceleration %1%") % axis_upper).str(); - L("Maximum acceleration X"); - L("Maximum acceleration Y"); - L("Maximum acceleration Z"); - L("Maximum acceleration E"); + (void)L("Maximum acceleration X"); + (void)L("Maximum acceleration Y"); + (void)L("Maximum acceleration Z"); + (void)L("Maximum acceleration E"); def->category = L("Machine limits"); def->tooltip = (boost::format("Maximum acceleration of the %1% axis") % axis_upper).str(); - L("Maximum acceleration of the X axis"); - L("Maximum acceleration of the Y axis"); - L("Maximum acceleration of the Z axis"); - L("Maximum acceleration of the E axis"); + (void)L("Maximum acceleration of the X axis"); + (void)L("Maximum acceleration of the Y axis"); + (void)L("Maximum acceleration of the Z axis"); + (void)L("Maximum acceleration of the E axis"); def->sidetext = L("mm/s²"); def->min = 0; def->width = machine_limits_opt_width; @@ -1121,16 +1119,16 @@ void PrintConfigDef::init_fff_params() // Add the machine jerk limits for XYZE axes (M205) def = this->add("machine_max_jerk_" + axis.name, coFloats); def->full_label = (boost::format("Maximum jerk %1%") % axis_upper).str(); - L("Maximum jerk X"); - L("Maximum jerk Y"); - L("Maximum jerk Z"); - L("Maximum jerk E"); + (void)L("Maximum jerk X"); + (void)L("Maximum jerk Y"); + (void)L("Maximum jerk Z"); + (void)L("Maximum jerk E"); def->category = L("Machine limits"); def->tooltip = (boost::format("Maximum jerk of the %1% axis") % axis_upper).str(); - L("Maximum jerk of the X axis"); - L("Maximum jerk of the Y axis"); - L("Maximum jerk of the Z axis"); - L("Maximum jerk of the E axis"); + (void)L("Maximum jerk of the X axis"); + (void)L("Maximum jerk of the Y axis"); + (void)L("Maximum jerk of the Z axis"); + (void)L("Maximum jerk of the E axis"); def->sidetext = L("mm/s"); def->min = 0; def->width = machine_limits_opt_width; @@ -2228,7 +2226,6 @@ void PrintConfigDef::init_fff_params() void PrintConfigDef::init_sla_params() { - t_optiondef_map &Options = this->options; ConfigOptionDef* def; // SLA Printer settings diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index d99aceabf..4b77d0561 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -18,7 +18,7 @@ #include -//! macro used to mark string used at localization, +//! macro used to mark string used at localization, //! return same string #define L(s) Slic3r::I18N::translate(s) @@ -430,7 +430,7 @@ SupportLayer* PrintObject::add_support_layer(int id, coordf_t height, coordf_t p return m_support_layers.back(); } -SupportLayerPtrs::const_iterator PrintObject::insert_support_layer(SupportLayerPtrs::const_iterator pos, int id, coordf_t height, coordf_t print_z, coordf_t slice_z) +SupportLayerPtrs::const_iterator PrintObject::insert_support_layer(SupportLayerPtrs::const_iterator pos, size_t id, coordf_t height, coordf_t print_z, coordf_t slice_z) { return m_support_layers.insert(pos, new SupportLayer(id, this, height, print_z, slice_z)); } @@ -620,7 +620,7 @@ void PrintObject::detect_surfaces_type() // should be visible. bool interface_shells = m_config.interface_shells.value; - for (int idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) { + for (size_t idx_region = 0; idx_region < this->region_volumes.size(); ++ idx_region) { BOOST_LOG_TRIVIAL(debug) << "Detecting solid surfaces for region " << idx_region << " in parallel - start"; #ifdef SLIC3R_DEBUG_SLICE_PROCESSING for (Layer *layer : m_layers) @@ -806,8 +806,6 @@ void PrintObject::process_external_surfaces() BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..." << log_memory_info(); for (size_t region_id = 0; region_id < this->region_volumes.size(); ++region_id) { - const PrintRegion ®ion = *m_print->regions()[region_id]; - BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - start"; tbb::parallel_for( tbb::blocked_range(0, m_layers.size()), @@ -1028,7 +1026,6 @@ void PrintObject::discover_vertical_shells() bool hole_first = true; for (int n = (int)idx_layer - n_extra_bottom_layers; n <= (int)idx_layer + n_extra_top_layers; ++ n) if (n >= 0 && n < (int)m_layers.size()) { - Layer &neighbor_layer = *m_layers[n]; const DiscoverVerticalShellsCacheEntry &cache = cache_top_botom_regions[n]; if (hole_first) { hole_first = false; @@ -2154,7 +2151,7 @@ void PrintObject::discover_horizontal_shells() BOOST_LOG_TRIVIAL(trace) << "discover_horizontal_shells()"; for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { - for (int i = 0; i < int(m_layers.size()); ++ i) { + for (size_t i = 0; i < m_layers.size(); ++ i) { m_print->throw_if_canceled(); LayerRegion *layerm = m_layers[i]->regions()[region_id]; const PrintRegionConfig ®ion_config = layerm->region()->config(); @@ -2171,7 +2168,7 @@ void PrintObject::discover_horizontal_shells() if (region_config.ensure_vertical_shell_thickness.value) continue; - for (int idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) { + for (size_t idx_surface_type = 0; idx_surface_type < 3; ++ idx_surface_type) { m_print->throw_if_canceled(); SurfaceType type = (idx_surface_type == 0) ? stTop : (idx_surface_type == 1) ? stBottom : stBottomBridge; // Find slices of current type for current layer. @@ -2345,7 +2342,7 @@ void PrintObject::combine_infill() // Work on each region separately. for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) { const PrintRegion *region = this->print()->regions()[region_id]; - const int every = region->config().infill_every_layers.value; + const size_t every = region->config().infill_every_layers.value; if (every < 2 || region->config().fill_density == 0.) continue; // Limit the number of combined layers to the maximum height allowed by this regions' nozzle. diff --git a/src/libslic3r/SLA/SLAAutoSupports.cpp b/src/libslic3r/SLA/SLAAutoSupports.cpp index 0a2537b91..91b7dfe5d 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.cpp +++ b/src/libslic3r/SLA/SLAAutoSupports.cpp @@ -59,8 +59,6 @@ SLAAutoSupports::SLAAutoSupports(const TriangleMesh& mesh, const sla::EigenMesh3 void SLAAutoSupports::project_onto_mesh(std::vector& points) const { // The function makes sure that all the points are really exactly placed on the mesh. - igl::Hit hit_up{0, 0, 0.f, 0.f, 0.f}; - igl::Hit hit_down{0, 0, 0.f, 0.f, 0.f}; // Use a reasonable granularity to account for the worker thread synchronization cost. tbb::parallel_for(tbb::blocked_range(0, points.size(), 64), @@ -140,7 +138,6 @@ static std::vector make_layers( SLAAutoSupports::MyLayer &layer_above = layers[layer_id]; SLAAutoSupports::MyLayer &layer_below = layers[layer_id - 1]; //FIXME WTF? - const float height = (layer_id>2 ? heights[layer_id-3] : heights[0]-(heights[1]-heights[0])); const float layer_height = (layer_id!=0 ? heights[layer_id]-heights[layer_id-1] : heights[0]); const float safe_angle = 5.f * (float(M_PI)/180.f); // smaller number - less supports const float between_layers_offset = float(scale_(layer_height / std::tan(safe_angle))); @@ -212,7 +209,7 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: for (Structure &top : layer_top->islands) for (Structure::Link &bottom_link : top.islands_below) { Structure &bottom = *bottom_link.island; - float centroids_dist = (bottom.centroid - top.centroid).norm(); + //float centroids_dist = (bottom.centroid - top.centroid).norm(); // Penalization resulting from centroid offset: // bottom.supports_force *= std::min(1.f, 1.f - std::min(1.f, (1600.f * layer_height) * centroids_dist * centroids_dist / bottom.area)); float &support_force = support_force_bottom[&bottom - layer_bottom->islands.data()]; @@ -239,7 +236,7 @@ void SLAAutoSupports::process(const std::vector& slices, const std:: // s.supports_force_inherited /= std::max(1.f, (layer_height / 0.3f) * e_area / s.area); s.supports_force_inherited /= std::max(1.f, 0.17f * (s.overhangs_area) / s.area); - float force_deficit = s.support_force_deficit(m_config.tear_pressure()); + //float force_deficit = s.support_force_deficit(m_config.tear_pressure()); if (s.islands_below.empty()) { // completely new island - needs support no doubt uniformly_cover({ *s.polygon }, s, point_grid, true); } else if (! s.dangling_areas.empty()) { @@ -380,7 +377,7 @@ static inline std::vector poisson_disk_from_samples(const std::vectorinvalidate_all_steps()); m_objects = print_objects_new; // Delete the PrintObjects marked as Unknown or Deleted. - bool deleted_objects = false; for (auto &pos : print_object_status) if (pos.status == PrintObjectStatus::Unknown || pos.status == PrintObjectStatus::Deleted) { update_apply_status(pos.print_object->invalidate_all_steps()); delete pos.print_object; - deleted_objects = true; } if (new_objects) update_apply_status(false); diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index e1bb4b313..5e73c8811 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -187,7 +187,6 @@ std::vector layer_height_profile_from_ranges( coordf_t hi = it_range->first.second; coordf_t height = it_range->second; coordf_t last_z = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2]; - coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1]; if (lo > last_z + EPSILON) { // Insert a step of normal layer height. layer_height_profile.push_back(last_z); @@ -203,7 +202,6 @@ std::vector layer_height_profile_from_ranges( } coordf_t last_z = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 2]; - coordf_t last_height = layer_height_profile.empty() ? 0. : layer_height_profile[layer_height_profile.size() - 1]; if (last_z < slicing_params.object_print_z_height()) { // Insert a step of normal layer height up to the object top. layer_height_profile.push_back(last_z); @@ -245,7 +243,6 @@ std::vector layer_height_profile_adaptive( } coordf_t slice_z = slicing_params.first_object_layer_height; coordf_t height = slicing_params.first_object_layer_height; - coordf_t cusp_height = 0.; int current_facet = 0; while ((slice_z - height) <= slicing_params.object_print_z_height()) { height = 999; @@ -401,7 +398,6 @@ void adjust_layer_height_profile( } // Adjust height by layer_thickness_delta. coordf_t weight = std::abs(zz - z) < 0.5 * band_width ? (0.5 + 0.5 * cos(2. * M_PI * (zz - z) / band_width)) : 0.; - coordf_t height_new = height; switch (action) { case LAYER_HEIGHT_EDIT_ACTION_INCREASE: case LAYER_HEIGHT_EDIT_ACTION_DECREASE: diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index fde35ca1e..c1eb2f4d8 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -361,17 +361,17 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) std::sort(layers_sorted.begin(), layers_sorted.end(), MyLayersPtrCompare()); int layer_id = 0; assert(object.support_layers().empty()); - for (int i = 0; i < int(layers_sorted.size());) { + for (size_t i = 0; i < layers_sorted.size();) { // Find the last layer with roughly the same print_z, find the minimum layer height of all. // Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should. - int j = i + 1; + size_t j = i + 1; coordf_t zmax = layers_sorted[i]->print_z + EPSILON; for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j) ; // Assign an average print_z to the set of layers with nearly equal print_z. coordf_t zavg = 0.5 * (layers_sorted[i]->print_z + layers_sorted[j - 1]->print_z); coordf_t height_min = layers_sorted[i]->height; bool empty = true; - for (int u = i; u < j; ++u) { + for (size_t u = i; u < j; ++u) { MyLayer &layer = *layers_sorted[u]; if (! layer.polygons.empty()) empty = false; @@ -1042,7 +1042,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ float fw = float(layerm->flow(frExternalPerimeter).scaled_width()); no_interface_offset = (no_interface_offset == 0.f) ? fw : std::min(no_interface_offset, fw); float lower_layer_offset = - (layer_id < m_object_config->support_material_enforce_layers.value) ? + (layer_id < (size_t)m_object_config->support_material_enforce_layers.value) ? // Enforce a full possible support, ignore the overhang angle. 0.f : (threshold_rad > 0. ? @@ -1352,7 +1352,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ { // Find the span of layers, which are to be printed at the first layer height. int j = 0; - for (; j < contact_out.size() && contact_out[j]->print_z < m_slicing_params.first_print_layer_height + this->m_support_layer_height_min - EPSILON; ++ j); + for (; j < (int)contact_out.size() && contact_out[j]->print_z < m_slicing_params.first_print_layer_height + this->m_support_layer_height_min - EPSILON; ++ j); if (j > 0) { // Merge the contact_out layers (0) to (j - 1) into the contact_out[0]. MyLayer &dst = *contact_out.front(); @@ -1377,7 +1377,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // Find the span of layers closer than m_support_layer_height_min. int j = i + 1; coordf_t zmax = contact_out[i]->print_z + m_support_layer_height_min + EPSILON; - for (; j < contact_out.size() && contact_out[j]->print_z < zmax; ++ j) ; + for (; j < (int)contact_out.size() && contact_out[j]->print_z < zmax; ++ j) ; if (i + 1 < j) { // Merge the contact_out layers (i + 1) to (j - 1) into the contact_out[i]. MyLayer &dst = *contact_out[i]; @@ -1395,7 +1395,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ contact_out[k] = contact_out[i]; i = j; } - if (k < contact_out.size()) + if (k < (int)contact_out.size()) contact_out.erase(contact_out.begin() + k, contact_out.end()); } @@ -2566,11 +2566,11 @@ void LoopInterfaceProcessor::generate(MyLayerExtruded &top_contact_layer, const { // make more loops Polygons loop_polygons = loops0; - for (size_t i = 1; i < n_contact_loops; ++ i) + for (int i = 1; i < n_contact_loops; ++ i) polygons_append(loop_polygons, offset2( loops0, - - int(i) * flow.scaled_spacing() - 0.5f * flow.scaled_spacing(), + - i * flow.scaled_spacing() - 0.5f * flow.scaled_spacing(), 0.5f * flow.scaled_spacing())); // Clip such loops to the side oriented towards the object. // Collect split points, so they will be recognized after the clipping. diff --git a/src/libslic3r/utils.cpp b/src/libslic3r/utils.cpp index 3482f708a..519763731 100644 --- a/src/libslic3r/utils.cpp +++ b/src/libslic3r/utils.cpp @@ -414,13 +414,13 @@ std::string format_memsize_MB(size_t n) scale *= 1000; } char buf[8]; - sprintf(buf, "%d", n); + sprintf(buf, "%d", (int)n); out = buf; while (scale != 1) { scale /= 1000; n = n2 / scale; n2 = n2 % scale; - sprintf(buf, ",%03d", n); + sprintf(buf, ",%03d", (int)n); out += buf; } return out + "MB"; From a710e7e7e4392e1be26db508784b2a35c1e75743 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 26 Jun 2019 13:26:49 +0200 Subject: [PATCH 17/72] WIP: Undo / Redo stack. Integration of the "cereal" serialization library. Serialization / deserialization of the DynamicConfig / DynamicPrintConfig. DynamicPrintConfig serializes ordinal identifiers instead of the option key strings to conserve space. --- CMakeLists.txt | 4 + deps/CMakeLists.txt | 2 + deps/deps-unix-common.cmake | 10 ++ deps/deps-windows.cmake | 14 +++ src/libslic3r/CMakeLists.txt | 1 + src/libslic3r/Config.cpp | 153 ++++++++++++++++++------- src/libslic3r/Config.hpp | 191 ++++++++++++++++++++++++++------ src/libslic3r/Format/3mf.cpp | 6 +- src/libslic3r/Format/AMF.cpp | 8 +- src/libslic3r/GCode.cpp | 2 +- src/libslic3r/Layer.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 8 +- src/libslic3r/PrintConfig.hpp | 36 ++++++ src/slic3r/CMakeLists.txt | 2 +- src/slic3r/GUI/PresetBundle.cpp | 2 +- 15 files changed, 361 insertions(+), 80 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ba264c8c3..9d6d754e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -344,6 +344,10 @@ if (NOT GLEW_FOUND) endif () include_directories(${GLEW_INCLUDE_DIRS}) +# Find the Cereal serialization library +add_library(cereal INTERFACE) +target_include_directories(cereal INTERFACE include) + # l10n set(L10N_DIR "${SLIC3R_RESOURCES_DIR}/localization") add_custom_target(pot diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 5bc33c896..1c468607e 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -88,6 +88,7 @@ if (MSVC) dep_libcurl dep_wxwidgets dep_gtest + dep_cereal dep_nlopt # dep_qhull # Experimental dep_zlib # on Windows we still need zlib @@ -102,6 +103,7 @@ else() dep_libcurl dep_wxwidgets dep_gtest + dep_cereal dep_nlopt dep_qhull dep_libigl diff --git a/deps/deps-unix-common.cmake b/deps/deps-unix-common.cmake index c44a6ec20..6d9d6fd75 100644 --- a/deps/deps-unix-common.cmake +++ b/deps/deps-unix-common.cmake @@ -19,6 +19,16 @@ ExternalProject_Add(dep_gtest CMAKE_ARGS -DBUILD_GMOCK=OFF ${DEP_CMAKE_OPTS} -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local ) +ExternalProject_Add(dep_cereal + EXCLUDE_FROM_ALL 1 + URL "https://github.com/USCiLab/cereal/archive/v1.2.2.tar.gz" +# URL_HASH SHA256=c6dd7a5701fff8ad5ebb45a3dc8e757e61d52658de3918e38bab233e7fd3b4ae + CMAKE_ARGS + -DJUST_INSTALL_CEREAL=on + -DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local + ${DEP_CMAKE_OPTS} +) + ExternalProject_Add(dep_nlopt EXCLUDE_FROM_ALL 1 URL "https://github.com/stevengj/nlopt/archive/v2.5.0.tar.gz" diff --git a/deps/deps-windows.cmake b/deps/deps-windows.cmake index d7daf8425..2595f94d8 100644 --- a/deps/deps-windows.cmake +++ b/deps/deps-windows.cmake @@ -115,6 +115,20 @@ if (${DEP_DEBUG}) endif () +ExternalProject_Add(dep_cereal + EXCLUDE_FROM_ALL 1 + URL "https://github.com/USCiLab/cereal/archive/v1.2.2.tar.gz" +# URL_HASH SHA256=c6dd7a5701fff8ad5ebb45a3dc8e757e61d52658de3918e38bab233e7fd3b4ae + CMAKE_GENERATOR "${DEP_MSVC_GEN}" + CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}" + CMAKE_ARGS + -DJUST_INSTALL_CEREAL=on + "-DCMAKE_INSTALL_PREFIX:PATH=${DESTDIR}\\usr\\local" + BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj + INSTALL_COMMAND "" +) + + ExternalProject_Add(dep_nlopt EXCLUDE_FROM_ALL 1 URL "https://github.com/stevengj/nlopt/archive/v2.5.0.tar.gz" diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index dc52257aa..e1423b1e1 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -189,6 +189,7 @@ target_include_directories(libslic3r PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${LIBNE target_link_libraries(libslic3r libnest2d admesh + cereal libigl miniz boost_libs diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 0738b77c6..794beff21 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -209,6 +209,51 @@ std::vector ConfigOptionDef::cli_args(const std::string &key) const return args; } +ConfigOption* ConfigOptionDef::create_empty_option() const +{ + switch (this->type) { + case coFloat: return new ConfigOptionFloat(); + case coFloats: return new ConfigOptionFloats(); + case coInt: return new ConfigOptionInt(); + case coInts: return new ConfigOptionInts(); + case coString: return new ConfigOptionString(); + case coStrings: return new ConfigOptionStrings(); + case coPercent: return new ConfigOptionPercent(); + case coPercents: return new ConfigOptionPercents(); + case coFloatOrPercent: return new ConfigOptionFloatOrPercent(); + case coPoint: return new ConfigOptionPoint(); + case coPoints: return new ConfigOptionPoints(); + case coPoint3: return new ConfigOptionPoint3(); +// case coPoint3s: return new ConfigOptionPoint3s(); + case coBool: return new ConfigOptionBool(); + case coBools: return new ConfigOptionBools(); + case coEnum: return new ConfigOptionEnumGeneric(this->enum_keys_map); + default: throw std::runtime_error(std::string("Unknown option type for option ") + this->label); + } +} + +ConfigOption* ConfigOptionDef::create_default_option() const +{ + if (this->default_value) + return (this->default_value->type() == coEnum) ? + // Special case: For a DynamicConfig, convert a templated enum to a generic enum. + new ConfigOptionEnumGeneric(this->enum_keys_map, this->default_value->getInt()) : + this->default_value->clone(); + return this->create_empty_option(); +} + +// Assignment of the serialization IDs is not thread safe. The Defs shall be initialized from the main thread! +ConfigOptionDef* ConfigDef::add(const t_config_option_key &opt_key, ConfigOptionType type) +{ + static size_t serialization_key_ordinal_last = 0; + ConfigOptionDef *opt = &this->options[opt_key]; + opt->opt_key = opt_key; + opt->type = type; + opt->serialization_key_ordinal = ++ serialization_key_ordinal_last; + this->by_serialization_key_ordinal[opt->serialization_key_ordinal] = opt; + return opt; +} + std::string ConfigOptionDef::nocli = "~~~noCLI"; std::ostream& ConfigDef::print_cli_help(std::ostream& out, bool show_defaults, std::function filter) const @@ -358,7 +403,7 @@ t_config_option_keys ConfigBase::equal(const ConfigBase &other) const return equal; } -std::string ConfigBase::serialize(const t_config_option_key &opt_key) const +std::string ConfigBase::opt_serialize(const t_config_option_key &opt_key) const { const ConfigOption* opt = this->option(opt_key); assert(opt != nullptr); @@ -469,7 +514,7 @@ void ConfigBase::setenv_() const for (size_t i = 0; i < envname.size(); ++i) envname[i] = (envname[i] <= 'z' && envname[i] >= 'a') ? envname[i]-('a'-'A') : envname[i]; - boost::nowide::setenv(envname.c_str(), this->serialize(*it).c_str(), 1); + boost::nowide::setenv(envname.c_str(), this->opt_serialize(*it).c_str(), 1); } } @@ -593,16 +638,16 @@ void ConfigBase::save(const std::string &file) const c.open(file, std::ios::out | std::ios::trunc); c << "# " << Slic3r::header_slic3r_generated() << std::endl; for (const std::string &opt_key : this->keys()) - c << opt_key << " = " << this->serialize(opt_key) << std::endl; + c << opt_key << " = " << this->opt_serialize(opt_key) << std::endl; c.close(); } bool DynamicConfig::operator==(const DynamicConfig &rhs) const { - t_options_map::const_iterator it1 = this->options.begin(); - t_options_map::const_iterator it1_end = this->options.end(); - t_options_map::const_iterator it2 = rhs.options.begin(); - t_options_map::const_iterator it2_end = rhs.options.end(); + auto it1 = this->options.begin(); + auto it1_end = this->options.end(); + auto it2 = rhs.options.begin(); + auto it2_end = rhs.options.end(); for (; it1 != it1_end && it2 != it2_end; ++ it1, ++ it2) if (it1->first != it2->first || *it1->second != *it2->second) // key or value differ @@ -612,10 +657,10 @@ bool DynamicConfig::operator==(const DynamicConfig &rhs) const ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool create) { - t_options_map::iterator it = options.find(opt_key); + auto it = options.find(opt_key); if (it != options.end()) // Option was found. - return it->second; + return it->second.get(); if (! create) // Option was not found and a new option shall not be created. return nullptr; @@ -628,34 +673,8 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre // throw std::runtime_error(std::string("Invalid option name: ") + opt_key); // Let the parent decide what to do if the opt_key is not defined by this->def(). return nullptr; - ConfigOption *opt = nullptr; - if (optdef->default_value) { - opt = (optdef->default_value->type() == coEnum) ? - // Special case: For a DynamicConfig, convert a templated enum to a generic enum. - new ConfigOptionEnumGeneric(optdef->enum_keys_map, optdef->default_value->getInt()) : - optdef->default_value->clone(); - } else { - switch (optdef->type) { - case coFloat: opt = new ConfigOptionFloat(); break; - case coFloats: opt = new ConfigOptionFloats(); break; - case coInt: opt = new ConfigOptionInt(); break; - case coInts: opt = new ConfigOptionInts(); break; - case coString: opt = new ConfigOptionString(); break; - case coStrings: opt = new ConfigOptionStrings(); break; - case coPercent: opt = new ConfigOptionPercent(); break; - case coPercents: opt = new ConfigOptionPercents(); break; - case coFloatOrPercent: opt = new ConfigOptionFloatOrPercent(); break; - case coPoint: opt = new ConfigOptionPoint(); break; - case coPoints: opt = new ConfigOptionPoints(); break; - case coPoint3: opt = new ConfigOptionPoint3(); break; - // case coPoint3s: opt = new ConfigOptionPoint3s(); break; - case coBool: opt = new ConfigOptionBool(); break; - case coBools: opt = new ConfigOptionBools(); break; - case coEnum: opt = new ConfigOptionEnumGeneric(optdef->enum_keys_map); break; - default: throw std::runtime_error(std::string("Unknown option type for option ") + opt_key); - } - } - this->options[opt_key] = opt; + ConfigOption *opt = optdef->create_default_option(); + this->options.insert(it, std::make_pair(opt_key, opt)); return opt; } @@ -802,3 +821,63 @@ t_config_option_keys StaticConfig::keys() const } } + +CEREAL_REGISTER_TYPE(Slic3r::ConfigOption) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVectorBase) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionVector) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloat) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloats) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionInt) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionInts) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionString) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionStrings) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercent) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPercents) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionFloatOrPercent) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoint) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoints) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionPoint3) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionBool) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionBools) +CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionEnumGeneric) +CEREAL_REGISTER_TYPE(Slic3r::ConfigBase) +CEREAL_REGISTER_TYPE(Slic3r::DynamicConfig) + +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionSingle) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOption, Slic3r::ConfigOptionVectorBase) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVectorBase, Slic3r::ConfigOptionVector) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle, Slic3r::ConfigOptionFloat) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionFloats) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle, Slic3r::ConfigOptionInt) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionInts) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle, Slic3r::ConfigOptionString) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionStrings) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionFloat, Slic3r::ConfigOptionPercent) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionFloats, Slic3r::ConfigOptionPercents) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionPercent, Slic3r::ConfigOptionFloatOrPercent) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle, Slic3r::ConfigOptionPoint) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionPoints) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle, Slic3r::ConfigOptionPoint3) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionSingle, Slic3r::ConfigOptionBool) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionVector, Slic3r::ConfigOptionBools) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigOptionInt, Slic3r::ConfigOptionEnumGeneric) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ConfigBase, Slic3r::DynamicConfig) diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index ee4bc4e46..7b3c5c73a 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -18,6 +18,12 @@ #include #include +#include +#include +#include +#include +#include + namespace Slic3r { // Name of the configuration option. @@ -152,6 +158,10 @@ public: bool operator==(const T &rhs) const { return this->value == rhs; } bool operator!=(const T &rhs) const { return this->value != rhs; } + +private: + friend class cereal::access; + template void serialize(Archive & ar) { ar(this->value); } }; // Value of a vector valued option (bools, ints, floats, strings, points) @@ -290,6 +300,10 @@ public: bool operator==(const std::vector &rhs) const { return this->values == rhs; } bool operator!=(const std::vector &rhs) const { return this->values != rhs; } + +private: + friend class cereal::access; + template void serialize(Archive & ar) { ar(this->values); } }; class ConfigOptionFloat : public ConfigOptionSingle @@ -324,6 +338,10 @@ public: this->set(opt); return *this; } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; class ConfigOptionFloats : public ConfigOptionVector @@ -382,6 +400,10 @@ public: this->set(opt); return *this; } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; class ConfigOptionInt : public ConfigOptionSingle @@ -418,6 +440,10 @@ public: this->set(opt); return *this; } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; class ConfigOptionInts : public ConfigOptionVector @@ -468,6 +494,10 @@ public: } return true; } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; class ConfigOptionString : public ConfigOptionSingle @@ -492,6 +522,10 @@ public: UNUSED(append); return unescape_string_cstyle(str, this->value); } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; // semicolon-separated strings @@ -526,6 +560,10 @@ public: this->values.clear(); return unescape_strings_cstyle(str, this->values); } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; class ConfigOptionPercent : public ConfigOptionFloat @@ -558,6 +596,10 @@ public: iss >> this->value; return !iss.fail(); } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class(this)); } }; class ConfigOptionPercents : public ConfigOptionFloats @@ -612,6 +654,10 @@ public: } return true; } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class(this)); } }; class ConfigOptionFloatOrPercent : public ConfigOptionPercent @@ -661,6 +707,10 @@ public: iss >> this->value; return !iss.fail(); } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class(this), percent); } }; class ConfigOptionPoint : public ConfigOptionSingle @@ -691,6 +741,10 @@ public: return sscanf(str.data(), " %lf , %lf %c", &this->value(0), &this->value(1), &dummy) == 2 || sscanf(str.data(), " %lf x %lf %c", &this->value(0), &this->value(1), &dummy) == 2; } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(this->value.x(), this->value.y()); } }; class ConfigOptionPoints : public ConfigOptionVector @@ -750,8 +804,21 @@ public: } return true; } -}; +private: + friend class cereal::access; + template void save(Archive& archive) const { + size_t cnt = this->values.size(); + archive(cnt); + archive.saveBinary((const char*)this->values.data(), sizeof(Vec2d) * cnt); + } + template void load(Archive& archive) { + size_t cnt; + archive(cnt); + this->values.assign(cnt, Vec2d()); + archive.loadBinary((char*)this->values.data(), sizeof(Vec2d) * cnt); + } +}; class ConfigOptionPoint3 : public ConfigOptionSingle { @@ -783,6 +850,10 @@ public: return sscanf(str.data(), " %lf , %lf , %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 2 || sscanf(str.data(), " %lf x %lf x %lf %c", &this->value(0), &this->value(1), &this->value(2), &dummy) == 2; } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(this->value.x(), this->value.y(), this->value.z()); } }; class ConfigOptionBool : public ConfigOptionSingle @@ -809,6 +880,10 @@ public: this->value = (str.compare("1") == 0); return true; } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; class ConfigOptionBools : public ConfigOptionVector @@ -864,6 +939,10 @@ public: } return true; } + +private: + friend class cereal::access; + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; // Map from an enum integer value to an enum name. @@ -1002,19 +1081,73 @@ public: this->value = it->second; return true; } + +private: + friend class cereal::access; + template void serialize(Archive& ar) { ar(cereal::base_class(this)); } }; // Definition of a configuration value for the purpose of GUI presentation, editing, value mapping and config file handling. class ConfigOptionDef { public: + // Identifier of this option. It is stored here so that it is accessible through the by_serialization_key_ordinal map. + t_config_option_key opt_key; // What type? bool, int, string etc. ConfigOptionType type = coNone; // Default value of this option. The default value object is owned by ConfigDef, it is released in its destructor. Slic3r::clonable_ptr default_value; - void set_default_value(const ConfigOption* ptr) { this->default_value = Slic3r::clonable_ptr(ptr); } - template - const T* get_default_value() const { return static_cast(this->default_value.get()); } + void set_default_value(const ConfigOption* ptr) { this->default_value = Slic3r::clonable_ptr(ptr); } + template const T* get_default_value() const { return static_cast(this->default_value.get()); } + + // Create an empty option to be used as a base for deserialization of DynamicConfig. + ConfigOption* create_empty_option() const; + // Create a default option to be inserted into a DynamicConfig. + ConfigOption* create_default_option() const; + + template ConfigOption* load_option_from_archive(Archive &archive) const { + switch (this->type) { + case coFloat: { auto opt = new ConfigOptionFloat(); archive(*opt); return opt; } + case coFloats: { auto opt = new ConfigOptionFloats(); archive(*opt); return opt; } + case coInt: { auto opt = new ConfigOptionInt(); archive(*opt); return opt; } + case coInts: { auto opt = new ConfigOptionInts(); archive(*opt); return opt; } + case coString: { auto opt = new ConfigOptionString(); archive(*opt); return opt; } + case coStrings: { auto opt = new ConfigOptionStrings(); archive(*opt); return opt; } + case coPercent: { auto opt = new ConfigOptionPercent(); archive(*opt); return opt; } + case coPercents: { auto opt = new ConfigOptionPercents(); archive(*opt); return opt; } + case coFloatOrPercent: { auto opt = new ConfigOptionFloatOrPercent(); archive(*opt); return opt; } + case coPoint: { auto opt = new ConfigOptionPoint(); archive(*opt); return opt; } + case coPoints: { auto opt = new ConfigOptionPoints(); archive(*opt); return opt; } + case coPoint3: { auto opt = new ConfigOptionPoint3(); archive(*opt); return opt; } + case coBool: { auto opt = new ConfigOptionBool(); archive(*opt); return opt; } + case coBools: { auto opt = new ConfigOptionBools(); archive(*opt); return opt; } + case coEnum: { auto opt = new ConfigOptionEnumGeneric(this->enum_keys_map); archive(*opt); return opt; } + default: throw std::runtime_error(std::string("ConfigOptionDef::load_option_from_archive(): Unknown option type for option ") + this->opt_key); + } + } + + template ConfigOption* save_option_to_archive(Archive &archive, const ConfigOption *opt) const { + switch (this->type) { + case coFloat: archive(*static_cast(opt)); break; + case coFloats: archive(*static_cast(opt)); break; + case coInt: archive(*static_cast(opt)); break; + case coInts: archive(*static_cast(opt)); break; + case coString: archive(*static_cast(opt)); break; + case coStrings: archive(*static_cast(opt)); break; + case coPercent: archive(*static_cast(opt)); break; + case coPercents: archive(*static_cast(opt)); break; + case coFloatOrPercent: archive(*static_cast(opt)); break; + case coPoint: archive(*static_cast(opt)); break; + case coPoints: archive(*static_cast(opt)); break; + case coPoint3: archive(*static_cast(opt)); break; + case coBool: archive(*static_cast(opt)); break; + case coBools: archive(*static_cast(opt)); break; + case coEnum: archive(*static_cast(opt)); break; + default: throw std::runtime_error(std::string("ConfigOptionDef::save_option_to_archive(): Unknown option type for option ") + this->opt_key); + } + // Make the compiler happy, shut up the warnings. + return nullptr; + } // Usually empty. // Special values - "i_enum_open", "f_enum_open" to provide combo box for int or float selection, @@ -1084,6 +1217,9 @@ public: return false; } + // 0 is an invalid key. + size_t serialization_key_ordinal = 0; + // Returns the alternative CLI arguments for the given option. // If there are no cli arguments defined, use the key and replace underscores with dashes. std::vector cli_args(const std::string &key) const; @@ -1103,7 +1239,8 @@ typedef std::map t_optiondef_map; class ConfigDef { public: - t_optiondef_map options; + t_optiondef_map options; + std::map by_serialization_key_ordinal; bool has(const t_config_option_key &opt_key) const { return this->options.count(opt_key) > 0; } const ConfigOptionDef* get(const t_config_option_key &opt_key) const { @@ -1124,11 +1261,7 @@ public: std::function filter = [](const ConfigOptionDef &){ return true; }) const; protected: - ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type) { - ConfigOptionDef* opt = &this->options[opt_key]; - opt->type = type; - return opt; - } + ConfigOptionDef* add(const t_config_option_key &opt_key, ConfigOptionType type); }; // An abstract configuration store. @@ -1197,7 +1330,7 @@ public: bool equals(const ConfigBase &other) const { return this->diff(other).empty(); } t_config_option_keys diff(const ConfigBase &other) const; t_config_option_keys equal(const ConfigBase &other) const; - std::string serialize(const t_config_option_key &opt_key) const; + std::string opt_serialize(const t_config_option_key &opt_key) const; // Set a configuration value from a string, it will call an overridable handle_legacy() // to resolve renamed and removed configuration keys. bool set_deserialize(const t_config_option_key &opt_key, const std::string &str, bool append = false); @@ -1235,7 +1368,7 @@ public: assert(this->def() == nullptr || this->def() == rhs.def()); this->clear(); for (const auto &kvp : rhs.options) - this->options[kvp.first] = kvp.second->clone(); + this->options[kvp.first].reset(kvp.second->clone()); return *this; } @@ -1258,15 +1391,13 @@ public: for (const auto &kvp : rhs.options) { auto it = this->options.find(kvp.first); if (it == this->options.end()) - this->options[kvp.first] = kvp.second->clone(); + this->options[kvp.first].reset(kvp.second->clone()); else { assert(it->second->type() == kvp.second->type()); if (it->second->type() == kvp.second->type()) *it->second = *kvp.second; - else { - delete it->second; - it->second = kvp.second->clone(); - } + else + it->second.reset(kvp.second->clone()); } } return *this; @@ -1277,14 +1408,13 @@ public: DynamicConfig& operator+=(DynamicConfig &&rhs) { assert(this->def() == nullptr || this->def() == rhs.def()); - for (const auto &kvp : rhs.options) { + for (auto &kvp : rhs.options) { auto it = this->options.find(kvp.first); if (it == this->options.end()) { - this->options[kvp.first] = kvp.second; + this->options.insert(std::make_pair(kvp.first, std::move(kvp.second))); } else { assert(it->second->type() == kvp.second->type()); - delete it->second; - it->second = kvp.second; + it->second = std::move(kvp.second); } } rhs.options.clear(); @@ -1301,8 +1431,6 @@ public: void clear() { - for (auto &opt : this->options) - delete opt.second; this->options.clear(); } @@ -1311,7 +1439,6 @@ public: auto it = this->options.find(opt_key); if (it == this->options.end()) return false; - delete it->second; this->options.erase(it); return true; } @@ -1336,11 +1463,10 @@ public: { auto it = this->options.find(opt_key); if (it == this->options.end()) { - this->options[opt_key] = opt; + this->options[opt_key].reset(opt); return true; } else { - delete it->second; - it->second = opt; + it->second.reset(opt); return false; } } @@ -1370,12 +1496,15 @@ public: void read_cli(const std::vector &tokens, t_config_option_keys* extra, t_config_option_keys* keys = nullptr); bool read_cli(int argc, char** argv, t_config_option_keys* extra, t_config_option_keys* keys = nullptr); - typedef std::map t_options_map; - t_options_map::const_iterator cbegin() const { return options.cbegin(); } - t_options_map::const_iterator cend() const { return options.cend(); } + std::map>::const_iterator cbegin() const { return options.cbegin(); } + std::map>::const_iterator cend() const { return options.cend(); } + size_t size() const { return options.size(); } private: - t_options_map options; + std::map> options; + + friend class cereal::access; + template void serialize(Archive &ar) { ar(options); } }; /// Configuration store with a static definition of configuration values. diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 4793586e3..0cb0af119 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -2060,7 +2060,7 @@ namespace Slic3r { for (const std::string &key : config.keys()) if (key != "compatible_printers") - out += "; " + key + " = " + config.serialize(key) + "\n"; + out += "; " + key + " = " + config.opt_serialize(key) + "\n"; if (!out.empty()) { @@ -2094,7 +2094,7 @@ namespace Slic3r { // stores object's config data for (const std::string& key : obj->config.keys()) { - stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << obj->config.serialize(key) << "\"/>\n"; + stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << OBJECT_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << obj->config.opt_serialize(key) << "\"/>\n"; } for (const ModelVolume* volume : obj_metadata.second.object->volumes) @@ -2124,7 +2124,7 @@ namespace Slic3r { // stores volume's config data for (const std::string& key : volume->config.keys()) { - stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.serialize(key) << "\"/>\n"; + stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << key << "\" " << VALUE_ATTR << "=\"" << volume->config.opt_serialize(key) << "\"/>\n"; } stream << " \n"; diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index a33d21c9f..0228bd906 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -873,7 +873,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) std::string str_config = "\n"; for (const std::string &key : config->keys()) if (key != "compatible_printers") - str_config += "; " + key + " = " + config->serialize(key) + "\n"; + str_config += "; " + key + " = " + config->opt_serialize(key) + "\n"; stream << "" << xml_escape(str_config) << "\n"; } @@ -885,7 +885,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) for (const auto &attr : material.second->attributes) stream << " " << attr.second << "\n"; for (const std::string &key : material.second->config.keys()) - stream << " " << material.second->config.serialize(key) << "\n"; + stream << " " << material.second->config.opt_serialize(key) << "\n"; stream << " \n"; } std::string instances; @@ -893,7 +893,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) ModelObject *object = model->objects[object_id]; stream << " \n"; for (const std::string &key : object->config.keys()) - stream << " " << object->config.serialize(key) << "\n"; + stream << " " << object->config.opt_serialize(key) << "\n"; if (!object->name.empty()) stream << " " << xml_escape(object->name) << "\n"; const std::vector &layer_height_profile = object->layer_height_profile; @@ -952,7 +952,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) else stream << " material_id() << "\">\n"; for (const std::string &key : volume->config.keys()) - stream << " " << volume->config.serialize(key) << "\n"; + stream << " " << volume->config.opt_serialize(key) << "\n"; if (!volume->name.empty()) stream << " " << xml_escape(volume->name) << "\n"; if (volume->is_modifier()) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index c42669de0..f868aa079 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1749,7 +1749,7 @@ void GCode::append_full_config(const Print& print, std::string& str) const StaticPrintConfig *cfg = configs[i]; for (const std::string &key : cfg->keys()) if (key != "compatible_printers") - str += "; " + key + " = " + cfg->serialize(key) + "\n"; + str += "; " + key + " = " + cfg->opt_serialize(key) + "\n"; } const DynamicConfig &full_config = print.placeholder_parser().config(); for (const char *key : { diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index c1d92c6bb..a8160867a 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -128,7 +128,7 @@ void Layer::make_perimeters() && config.external_perimeter_speed == other_config.external_perimeter_speed && config.gap_fill_speed == other_config.gap_fill_speed && config.overhangs == other_config.overhangs - && config.serialize("perimeter_extrusion_width").compare(other_config.serialize("perimeter_extrusion_width")) == 0 + && config.opt_serialize("perimeter_extrusion_width") == other_config.opt_serialize("perimeter_extrusion_width") && config.thin_walls == other_config.thin_walls && config.external_perimeters_first == other_config.external_perimeters_first) { layerms.push_back(other_layerm); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 89e21934a..97787fff6 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -406,10 +406,13 @@ void PrintConfigDef::init_fff_params() def->set_default_value(new ConfigOptionEnum(ipRectilinear)); def = this->add("bottom_fill_pattern", coEnum); - *def = *def_top_fill_pattern; def->label = L("Bottom fill pattern"); + def->category = L("Infill"); def->tooltip = L("Fill pattern for bottom infill. This only affects the bottom external visible layer, and not its adjacent solid shells."); def->cli = "bottom-fill-pattern|external-fill-pattern|solid-fill-pattern"; + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values = def_top_fill_pattern->enum_values; + def->aliases = def_top_fill_pattern->aliases; def->set_default_value(new ConfigOptionEnum(ipRectilinear)); def = this->add("external_perimeter_extrusion_width", coFloatOrPercent); @@ -3194,3 +3197,6 @@ void DynamicPrintAndCLIConfig::handle_legacy(t_config_option_key &opt_key, std:: } } + +CEREAL_REGISTER_TYPE(Slic3r::DynamicPrintConfig) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::DynamicConfig, Slic3r::DynamicPrintConfig) diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 248b89e32..8dbce9ea3 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1190,6 +1190,8 @@ private: this->options.insert(cli_actions_config_def.options.begin(), cli_actions_config_def.options.end()); this->options.insert(cli_transform_config_def.options.begin(), cli_transform_config_def.options.end()); this->options.insert(cli_misc_config_def.options.begin(), cli_misc_config_def.options.end()); + for (const auto &kvp : this->options) + this->by_serialization_key_ordinal[kvp.second.serialization_key_ordinal] = &kvp.second; } // Do not release the default values, they are handled by print_config_def & cli_actions_config_def / cli_transform_config_def / cli_misc_config_def. ~PrintAndCLIConfigDef() { this->options.clear(); } @@ -1199,4 +1201,38 @@ private: } // namespace Slic3r +// Serialization through the Cereal library +namespace cereal { + // Let cereal know that there are load / save non-member functions declared for DynamicPrintConfig, ignore serialize / load / save from parent class DynamicConfig. + template struct specialize {}; + + template void load(Archive& archive, Slic3r::DynamicPrintConfig &config) + { + size_t cnt; + archive(cnt); + config.clear(); + for (size_t i = 0; i < cnt; ++ i) { + size_t serialization_key_ordinal; + archive(serialization_key_ordinal); + assert(serialization_key_ordinal > 0); + auto it = Slic3r::print_config_def.by_serialization_key_ordinal.find(serialization_key_ordinal); + assert(it != Slic3r::print_config_def.by_serialization_key_ordinal.end()); + config.set_key_value(it->second->opt_key, it->second->load_option_from_archive(archive)); + } + } + + template void save(Archive& archive, const Slic3r::DynamicPrintConfig &config) + { + size_t cnt = config.size(); + archive(cnt); + for (auto it = config.cbegin(); it != config.cend(); ++it) { + const Slic3r::ConfigOptionDef* optdef = Slic3r::print_config_def.get(it->first); + assert(optdef != nullptr); + assert(optdef->serialization_key_ordinal > 0); + archive(optdef->serialization_key_ordinal); + optdef->save_option_to_archive(archive, it->second.get()); + } + } +} + #endif diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 570e23baa..1867a8186 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -159,7 +159,7 @@ endif () add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES}) -target_link_libraries(libslic3r_gui libslic3r avrdude imgui ${GLEW_LIBRARIES}) +target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES}) if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY) add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE) endif () diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index b28cb2eda..00c1f8168 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -1366,7 +1366,7 @@ void PresetBundle::export_configbundle(const std::string &path, bool export_syst continue; c << std::endl << "[" << presets->section_name() << ":" << preset.name << "]" << std::endl; for (const std::string &opt_key : preset.config.keys()) - c << opt_key << " = " << preset.config.serialize(opt_key) << std::endl; + c << opt_key << " = " << preset.config.opt_serialize(opt_key) << std::endl; } } From 4b9e366f00ad07aaba6fabf4c759e1dccc090f7e Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Wed, 26 Jun 2019 14:50:12 +0200 Subject: [PATCH 18/72] Multimaterial print - making sure that temperatures will be changed with SE printer without the wipe tower --- src/libslic3r/GCode.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index dadf9f26e..0628e4022 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2821,6 +2821,14 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) // user provided his own toolchange gcode, no need to do anything } + // Set the temperature if the wipe tower didn't (not needed for non-single extruder MM) + if (m_config.single_extruder_multi_material && !m_config.wipe_tower) { + int temp = (m_layer_index == 0 ? m_config.first_layer_temperature.get_at(extruder_id) : + m_config.temperature.get_at(extruder_id)); + + gcode += m_writer.set_temperature(temp, false); + } + m_placeholder_parser.set("current_extruder", extruder_id); // Append the filament start G-code. From d99e932ee807afd053bec0d3bffd3c934f19a9dd Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 26 Jun 2019 16:29:12 +0200 Subject: [PATCH 19/72] WIP Undo / Redo: Serialization of the Model / ModelObject / Model instance using the cereal framework. --- src/libslic3r/BoundingBox.hpp | 11 +++++ src/libslic3r/Config.hpp | 4 +- src/libslic3r/Geometry.hpp | 23 +++++++++++ src/libslic3r/Model.cpp | 13 ++++++ src/libslic3r/Model.hpp | 77 +++++++++++++++++++++++++++-------- src/libslic3r/Point.hpp | 24 ++++++++++- src/libslic3r/pchheader.hpp | 9 +++- 7 files changed, 139 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp index 26302f702..b12ed5551 100644 --- a/src/libslic3r/BoundingBox.hpp +++ b/src/libslic3r/BoundingBox.hpp @@ -161,4 +161,15 @@ inline bool empty(const BoundingBox3Base &bb) } // namespace Slic3r +#include +#include + +// Serialization through the Cereal library +namespace cereal { + template void serialize(Archive& archive, Slic3r::BoundingBox &bb) { archive(bb.min, bb.max, bb.defined); } + template void serialize(Archive& archive, Slic3r::BoundingBox3 &bb) { archive(bb.min, bb.max, bb.defined); } + template void serialize(Archive& archive, Slic3r::BoundingBoxf &bb) { archive(bb.min, bb.max, bb.defined); } + template void serialize(Archive& archive, Slic3r::BoundingBoxf3 &bb) { archive(bb.min, bb.max, bb.defined); } +} + #endif diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 7b3c5c73a..d1fb9b7f4 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -744,7 +744,7 @@ public: private: friend class cereal::access; - template void serialize(Archive &ar) { ar(this->value.x(), this->value.y()); } + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; class ConfigOptionPoints : public ConfigOptionVector @@ -853,7 +853,7 @@ public: private: friend class cereal::access; - template void serialize(Archive &ar) { ar(this->value.x(), this->value.y(), this->value.z()); } + template void serialize(Archive &ar) { ar(cereal::base_class>(this)); } }; class ConfigOptionBool : public ConfigOptionSingle diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index bcbe80a0d..f1987734f 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -7,6 +7,10 @@ #include "Polygon.hpp" #include "Polyline.hpp" +// Serialization through the Cereal library +#include +#include + #include "boost/polygon/voronoi.hpp" using boost::polygon::voronoi_builder; using boost::polygon::voronoi_diagram; @@ -263,6 +267,25 @@ public: // as possible in least squares norm in regard to the 8 corners of bbox. // Bounding box is expected to be centered around zero in all axes. static Transformation volume_to_bed_transformation(const Transformation& instance_transformation, const BoundingBoxf3& bbox); + +private: + template void load(Archive& archive, Slic3r::Geometry::Transformation &t) { + archive.loadBinary((char*)m.data(), sizeof(float) * 4); + } + template void save(Archive& archive, const Slic3r::Geometry::Transformation &t) const { + archive.saveBinary((char*)m.data(), sizeof(float) * 4); + } + +private: + friend class cereal::access; + template void serialize(Archive & ar) { ar(m_offset, m_rotation, m_scaling_factor, m_mirror); } + explicit Transformation(int) : m_dirty(true) {} + template static void load_and_construct(Archive & ar, cereal::construct &construct) + { + // Calling a private constructor with special "int" parameter to indicate that no construction is necessary. + construct(1); + ar(construct.ptr()->m_offset, construct.ptr()->m_rotation, construct.ptr()->m_scaling_factor, m_mirror); + } }; // Rotation when going from the first coordinate system with rotation rot_xyz_from applied diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 596f80671..da7d9e4d3 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1910,3 +1910,16 @@ void check_model_ids_equal(const Model &model1, const Model &model2) #endif /* NDEBUG */ } + +#if 0 +CEREAL_REGISTER_TYPE(Slic3r::ModelBase) +CEREAL_REGISTER_TYPE(Slic3r::ModelObject) +CEREAL_REGISTER_TYPE(Slic3r::ModelVolume) +CEREAL_REGISTER_TYPE(Slic3r::ModelInstance) +CEREAL_REGISTER_TYPE(Slic3r::Model) + +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ModelBase, Slic3r::ModelObject) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ModelBase, Slic3r::ModelVolume) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ModelBase, Slic3r::ModelInstance) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ModelBase, Slic3r::Model) +#endif \ No newline at end of file diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 0fd1140f0..3330f140d 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -40,8 +40,9 @@ typedef std::vector ModelInstancePtrs; // Valid IDs are strictly positive (non zero). // It is declared as an object, as some compilers (notably msvcc) consider a typedef size_t equivalent to size_t // for parameter overload. -struct ModelID +class ModelID { +public: ModelID(size_t id) : id(id) {} bool operator==(const ModelID &rhs) const { return this->id == rhs.id; } @@ -54,6 +55,12 @@ struct ModelID bool valid() const { return id != 0; } size_t id; + +private: + ModelID() {} + + friend class cereal::access; + template void serialize(Archive &ar) { ar(id); } }; // Unique object / instance ID for the wipe tower. @@ -76,6 +83,8 @@ protected: // Constructor with ignored int parameter to assign an invalid ID, to be replaced // by an existing ID copied from elsewhere. ModelBase(int) : m_id(ModelID(0)) {} + // The class tree will have virtual tables and type information. + virtual ~ModelBase() {} // Use with caution! void set_new_unique_id() { m_id = generate_new_id(); } @@ -94,6 +103,11 @@ private: friend ModelID wipe_tower_object_id(); friend ModelID wipe_tower_instance_id(); + + friend class cereal::access; + template void serialize(Archive &ar) { ar(m_id); } + ModelBase(const ModelID id) : m_id(id) {} + template static void load_and_construct(Archive & ar, cereal::construct &construct) { ModelID id; ar(id); construct(id); } }; #define MODELBASE_DERIVED_COPY_MOVE_CLONE(TYPE) \ @@ -155,10 +169,13 @@ private: // Parent, owning this material. Model *m_model; - ModelMaterial() = delete; ModelMaterial(ModelMaterial &&rhs) = delete; ModelMaterial& operator=(const ModelMaterial &rhs) = delete; ModelMaterial& operator=(ModelMaterial &&rhs) = delete; + + friend class cereal::access; + ModelMaterial() : m_model(nullptr) {} + template void serialize(Archive &ar) { ar(cereal::base_class(this)); ar(attributes, config); } }; // A printable object, possibly having multiple print volumes (each with its own set of parameters and materials), @@ -323,7 +340,15 @@ private: mutable BoundingBoxf3 m_raw_bounding_box; mutable bool m_raw_bounding_box_valid; mutable BoundingBoxf3 m_raw_mesh_bounding_box; - mutable bool m_raw_mesh_bounding_box_valid; + mutable bool m_raw_mesh_bounding_box_valid; + + friend class cereal::access; + ModelObject() : m_model(nullptr), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {} + template void serialize(Archive &ar) { + ar(cereal::base_class(this)); + ar(name, input_file, instances, volumes, config, layer_height_ranges, layer_height_profile, sla_support_points, sla_points_status, origin_translation, + m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid); + } }; // Declared outside of ModelVolume, so it could be forward declared. @@ -459,7 +484,7 @@ private: // -1 -> is unknown value (before first cheking) // 0 -> is not splittable // 1 -> is splittable - mutable int m_is_splittable{ -1 }; + mutable int m_is_splittable{ -1 }; ModelVolume(ModelObject *object, const TriangleMesh &mesh) : m_mesh(new TriangleMesh(mesh)), m_type(ModelVolumeType::MODEL_PART), object(object) { @@ -486,6 +511,13 @@ private: } ModelVolume& operator=(ModelVolume &rhs) = delete; + + friend class cereal::access; + ModelVolume() : object(nullptr) {} + template void serialize(Archive &ar) { + ar(cereal::base_class(this)); + ar(name, config, m_mesh, m_type, m_material_id, m_convex_hull, m_transformation, m_is_splittable); + } }; // A single instance of a ModelObject. @@ -571,10 +603,16 @@ private: explicit ModelInstance(ModelObject *object, const ModelInstance &other) : m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) {} - ModelInstance() = delete; explicit ModelInstance(ModelInstance &&rhs) = delete; ModelInstance& operator=(const ModelInstance &rhs) = delete; ModelInstance& operator=(ModelInstance &&rhs) = delete; + + friend class cereal::access; + ModelInstance() : object(nullptr) {} + template void serialize(Archive &ar) { + ar(cereal::base_class(this)); + ar(m_transformation, print_volume_state); + } }; // The print bed content. @@ -633,24 +671,24 @@ public: 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); + unsigned int update_print_volume_state(const BoundingBoxf3 &print_volume); // Returns true if any ModelObject was modified. - bool center_instances_around_point(const Vec2d &point); - void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); } - TriangleMesh mesh() const; - bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL); + bool center_instances_around_point(const Vec2d &point); + void translate(coordf_t x, coordf_t y, coordf_t z) { for (ModelObject *o : this->objects) o->translate(x, y, z); } + TriangleMesh mesh() const; + bool arrange_objects(coordf_t dist, const BoundingBoxf* bb = NULL); // Croaks if the duplicated objects do not fit the print bed. - void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL); - void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL); - void duplicate_objects_grid(size_t x, size_t y, coordf_t dist); + void duplicate(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL); + void duplicate_objects(size_t copies_num, coordf_t dist, const BoundingBoxf* bb = NULL); + void duplicate_objects_grid(size_t x, size_t y, coordf_t dist); - bool looks_like_multipart_object() const; - void convert_multipart_object(unsigned int max_extruders); + bool looks_like_multipart_object() const; + void convert_multipart_object(unsigned int max_extruders); // Ensures that the min z of the model is not negative - void adjust_min_z(); + void adjust_min_z(); - void print_info() const { for (const ModelObject *o : this->objects) o->print_info(); } + void print_info() const { for (const ModelObject *o : this->objects) o->print_info(); } static unsigned int get_auto_extruder_id(unsigned int max_extruders); static std::string get_auto_extruder_id_as_string(unsigned int max_extruders); @@ -663,6 +701,11 @@ public: private: MODELBASE_DERIVED_PRIVATE_COPY_MOVE(Model) + + friend class cereal::access; + template void serialize(Archive &ar) { + ar(cereal::base_class(this), materials, objects); + } }; #undef MODELBASE_DERIVED_COPY_MOVE_CLONE diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index b02ead299..8979523b7 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -62,8 +62,8 @@ inline Vec2i64 to_2d(const Vec3i64 &pt3) { return Vec2i64(pt3(0), pt3(1)); } inline Vec2f to_2d(const Vec3f &pt3) { return Vec2f (pt3(0), pt3(1)); } inline Vec2d to_2d(const Vec3d &pt3) { return Vec2d (pt3(0), pt3(1)); } -inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); } -inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); } +inline Vec3d to_3d(const Vec2d &v, double z) { return Vec3d(v(0), v(1), z); } +inline Vec3f to_3d(const Vec2f &v, float z) { return Vec3f(v(0), v(1), z); } inline Vec3i64 to_3d(const Vec2i64 &v, float z) { return Vec3i64(int64_t(v(0)), int64_t(v(1)), int64_t(z)); } inline Vec3crd to_3d(const Vec3crd &p, coord_t z) { return Vec3crd(p(0), p(1), z); } @@ -291,4 +291,24 @@ namespace boost { namespace polygon { } } // end Boost +#include +#include + +// Serialization through the Cereal library +namespace cereal { +// template void serialize(Archive& archive, Slic3r::Vec2crd &v) { archive(v.x(), v.y()); } +// template void serialize(Archive& archive, Slic3r::Vec3crd &v) { archive(v.x(), v.y(), v.z()); } + template void serialize(Archive& archive, Slic3r::Vec2i &v) { archive(v.x(), v.y()); } + template void serialize(Archive& archive, Slic3r::Vec3i &v) { archive(v.x(), v.y(), v.z()); } +// template void serialize(Archive& archive, Slic3r::Vec2i64 &v) { archive(v.x(), v.y()); } +// template void serialize(Archive& archive, Slic3r::Vec3i64 &v) { archive(v.x(), v.y(), v.z()); } + template void serialize(Archive& archive, Slic3r::Vec2f &v) { archive(v.x(), v.y()); } + template void serialize(Archive& archive, Slic3r::Vec3f &v) { archive(v.x(), v.y(), v.z()); } + template void serialize(Archive& archive, Slic3r::Vec2d &v) { archive(v.x(), v.y()); } + template void serialize(Archive& archive, Slic3r::Vec3d &v) { archive(v.x(), v.y(), v.z()); } + + template void load(Archive& archive, Slic3r::Matrix2f &m) { archive.loadBinary((char*)m.data(), sizeof(float) * 4); } + template void save(Archive& archive, Slic3r::Matrix2f &m) { archive.saveBinary((char*)m.data(), sizeof(float) * 4); } +} + #endif diff --git a/src/libslic3r/pchheader.hpp b/src/libslic3r/pchheader.hpp index b27dfe6a2..54b563def 100644 --- a/src/libslic3r/pchheader.hpp +++ b/src/libslic3r/pchheader.hpp @@ -100,7 +100,14 @@ #include #include -#include +#include + +#include +#include +#include +#include +#include +#include #include "BoundingBox.hpp" #include "ClipperUtils.hpp" From 27ee68d2f9666f5ad48d5ffacf1d6ea404156dda Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 27 Jun 2019 11:02:45 +0200 Subject: [PATCH 20/72] WIP Undo / Redo: ModelID / ModelBase renamed to ObjectID / ObjectBase --- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/Model.cpp | 25 ++-- src/libslic3r/Model.hpp | 140 +++++-------------- src/libslic3r/ObjectID.cpp | 9 ++ src/libslic3r/ObjectID.hpp | 87 ++++++++++++ src/libslic3r/Print.cpp | 8 +- src/libslic3r/PrintBase.hpp | 2 +- src/libslic3r/SLAPrint.cpp | 8 +- src/libslic3r/SLAPrint.hpp | 4 +- src/slic3r/GUI/GLCanvas3D.cpp | 8 +- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 4 +- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 2 +- src/slic3r/GUI/Selection.hpp | 2 +- xs/t/15_config.t | 29 ++-- xs/xsp/Config.xsp | 4 +- 15 files changed, 179 insertions(+), 155 deletions(-) create mode 100644 src/libslic3r/ObjectID.cpp create mode 100644 src/libslic3r/ObjectID.hpp diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index e1423b1e1..1bb9401f5 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -114,6 +114,8 @@ add_library(libslic3r STATIC MultiPoint.cpp MultiPoint.hpp MutablePriorityQueue.hpp + ObjectID.cpp + ObjectID.hpp PerimeterGenerator.cpp PerimeterGenerator.hpp PlaceholderParser.cpp diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index da7d9e4d3..2533f288b 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -22,18 +22,16 @@ namespace Slic3r { unsigned int Model::s_auto_extruder_id = 1; -size_t ModelBase::s_last_id = 0; - // Unique object / instance ID for the wipe tower. -ModelID wipe_tower_object_id() +ObjectID wipe_tower_object_id() { - static ModelBase mine; + static ObjectBase mine; return mine.id(); } -ModelID wipe_tower_instance_id() +ObjectID wipe_tower_instance_id() { - static ModelBase mine; + static ObjectBase mine; return mine.id(); } @@ -221,7 +219,7 @@ bool Model::delete_object(ModelObject* object) return false; } -bool Model::delete_object(ModelID id) +bool Model::delete_object(ObjectID id) { if (id.id != 0) { size_t idx = 0; @@ -1865,8 +1863,8 @@ bool model_volume_list_changed(const ModelObject &model_object_old, const ModelO // Verify whether the IDs of Model / ModelObject / ModelVolume / ModelInstance / ModelMaterial are valid and unique. void check_model_ids_validity(const Model &model) { - std::set ids; - auto check = [&ids](ModelID id) { + std::set ids; + auto check = [&ids](ObjectID id) { assert(id.id > 0); assert(ids.find(id) == ids.end()); ids.insert(id); @@ -1912,14 +1910,13 @@ void check_model_ids_equal(const Model &model1, const Model &model2) } #if 0 -CEREAL_REGISTER_TYPE(Slic3r::ModelBase) CEREAL_REGISTER_TYPE(Slic3r::ModelObject) CEREAL_REGISTER_TYPE(Slic3r::ModelVolume) CEREAL_REGISTER_TYPE(Slic3r::ModelInstance) CEREAL_REGISTER_TYPE(Slic3r::Model) -CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ModelBase, Slic3r::ModelObject) -CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ModelBase, Slic3r::ModelVolume) -CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ModelBase, Slic3r::ModelInstance) -CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ModelBase, Slic3r::Model) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelObject) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelVolume) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::ModelInstance) +CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::ObjectBase, Slic3r::Model) #endif \ No newline at end of file diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 3330f140d..850ea4202 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -2,19 +2,20 @@ #define slic3r_Model_hpp_ #include "libslic3r.h" -#include "PrintConfig.hpp" +#include "Geometry.hpp" #include "Layer.hpp" +#include "ObjectID.hpp" #include "Point.hpp" -#include "TriangleMesh.hpp" +#include "PrintConfig.hpp" #include "Slicing.hpp" +#include "SLA/SLACommon.hpp" +#include "TriangleMesh.hpp" #include #include #include #include #include -#include "Geometry.hpp" -#include namespace Slic3r { @@ -35,82 +36,11 @@ typedef std::vector ModelObjectPtrs; typedef std::vector ModelVolumePtrs; typedef std::vector ModelInstancePtrs; -// Unique identifier of a Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial. -// Used to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject) -// Valid IDs are strictly positive (non zero). -// It is declared as an object, as some compilers (notably msvcc) consider a typedef size_t equivalent to size_t -// for parameter overload. -class ModelID -{ -public: - ModelID(size_t id) : id(id) {} - - bool operator==(const ModelID &rhs) const { return this->id == rhs.id; } - bool operator!=(const ModelID &rhs) const { return this->id != rhs.id; } - bool operator< (const ModelID &rhs) const { return this->id < rhs.id; } - bool operator> (const ModelID &rhs) const { return this->id > rhs.id; } - bool operator<=(const ModelID &rhs) const { return this->id <= rhs.id; } - bool operator>=(const ModelID &rhs) const { return this->id >= rhs.id; } - - bool valid() const { return id != 0; } - - size_t id; - -private: - ModelID() {} - - friend class cereal::access; - template void serialize(Archive &ar) { ar(id); } -}; - // Unique object / instance ID for the wipe tower. -extern ModelID wipe_tower_object_id(); -extern ModelID wipe_tower_instance_id(); +extern ObjectID wipe_tower_object_id(); +extern ObjectID wipe_tower_instance_id(); -// Base for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial to provide a unique ID -// to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject). -// Achtung! The s_last_id counter is not thread safe, so it is expected, that the ModelBase derived instances -// are only instantiated from the main thread. -class ModelBase -{ -public: - ModelID id() const { return m_id; } - -protected: - // Constructors to be only called by derived classes. - // Default constructor to assign a unique ID. - ModelBase() : m_id(generate_new_id()) {} - // Constructor with ignored int parameter to assign an invalid ID, to be replaced - // by an existing ID copied from elsewhere. - ModelBase(int) : m_id(ModelID(0)) {} - // The class tree will have virtual tables and type information. - virtual ~ModelBase() {} - - // Use with caution! - void set_new_unique_id() { m_id = generate_new_id(); } - void set_invalid_id() { m_id = 0; } - // Use with caution! - void copy_id(const ModelBase &rhs) { m_id = rhs.id(); } - - // Override this method if a ModelBase derived class owns other ModelBase derived instances. - void assign_new_unique_ids_recursive() { this->set_new_unique_id(); } - -private: - ModelID m_id; - - static inline ModelID generate_new_id() { return ModelID(++ s_last_id); } - static size_t s_last_id; - - friend ModelID wipe_tower_object_id(); - friend ModelID wipe_tower_instance_id(); - - friend class cereal::access; - template void serialize(Archive &ar) { ar(m_id); } - ModelBase(const ModelID id) : m_id(id) {} - template static void load_and_construct(Archive & ar, cereal::construct &construct) { ModelID id; ar(id); construct(id); } -}; - -#define MODELBASE_DERIVED_COPY_MOVE_CLONE(TYPE) \ +#define OBJECTBASE_DERIVED_COPY_MOVE_CLONE(TYPE) \ /* Copy a model, copy the IDs. The Print::apply() will call the TYPE::copy() method */ \ /* to make a private copy for background processing. */ \ static TYPE* new_copy(const TYPE &rhs) { return new TYPE(rhs); } \ @@ -138,14 +68,14 @@ private: return *this; \ } -#define MODELBASE_DERIVED_PRIVATE_COPY_MOVE(TYPE) \ +#define OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE(TYPE) \ private: \ /* Private constructor with an unused int parameter will create a TYPE instance with an invalid ID. */ \ - explicit TYPE(int) : ModelBase(-1) {}; \ + explicit TYPE(int) : ObjectBase(-1) {}; \ void assign_new_unique_ids_recursive(); // Material, which may be shared across multiple ModelObjects of a single Model. -class ModelMaterial : public ModelBase +class ModelMaterial : public ObjectBase { public: // Attributes are defined by the AMF file format, but they don't seem to be used by Slic3r for any purpose. @@ -175,14 +105,14 @@ private: friend class cereal::access; ModelMaterial() : m_model(nullptr) {} - template void serialize(Archive &ar) { ar(cereal::base_class(this)); ar(attributes, config); } + template void serialize(Archive &ar) { ar(cereal::base_class(this)); ar(attributes, config); } }; // A printable object, possibly having multiple print volumes (each with its own set of parameters and materials), // and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials. // Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed, // different rotation and different uniform scaling. -class ModelObject : public ModelBase +class ModelObject : public ObjectBase { friend class Model; public: @@ -323,13 +253,13 @@ private: /* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */ /* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */ - ModelObject(const ModelObject &rhs) : ModelBase(-1), m_model(rhs.m_model) { this->assign_copy(rhs); } - explicit ModelObject(ModelObject &&rhs) : ModelBase(-1) { this->assign_copy(std::move(rhs)); } + ModelObject(const ModelObject &rhs) : ObjectBase(-1), m_model(rhs.m_model) { this->assign_copy(rhs); } + explicit ModelObject(ModelObject &&rhs) : ObjectBase(-1) { this->assign_copy(std::move(rhs)); } ModelObject& operator=(const ModelObject &rhs) { this->assign_copy(rhs); m_model = rhs.m_model; return *this; } ModelObject& operator=(ModelObject &&rhs) { this->assign_copy(std::move(rhs)); m_model = rhs.m_model; return *this; } - MODELBASE_DERIVED_COPY_MOVE_CLONE(ModelObject) - MODELBASE_DERIVED_PRIVATE_COPY_MOVE(ModelObject) + OBJECTBASE_DERIVED_COPY_MOVE_CLONE(ModelObject) + OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE(ModelObject) // Parent object, owning this ModelObject. Set to nullptr here, so the macros above will have it initialized. Model *m_model = nullptr; @@ -345,7 +275,7 @@ private: friend class cereal::access; ModelObject() : m_model(nullptr), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {} template void serialize(Archive &ar) { - ar(cereal::base_class(this)); + ar(cereal::base_class(this)); ar(name, input_file, instances, volumes, config, layer_height_ranges, layer_height_profile, sla_support_points, sla_points_status, origin_translation, m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid); } @@ -362,7 +292,7 @@ enum class ModelVolumeType : int { // An object STL, or a modifier volume, over which a different set of parameters shall be applied. // ModelVolume instances are owned by a ModelObject. -class ModelVolume : public ModelBase +class ModelVolume : public ObjectBase { public: std::string name; @@ -456,7 +386,7 @@ public: const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); } - using ModelBase::set_new_unique_id; + using ObjectBase::set_new_unique_id; protected: friend class Print; @@ -496,7 +426,7 @@ private: // Copying an existing volume, therefore this volume will get a copy of the ID assigned. ModelVolume(ModelObject *object, const ModelVolume &other) : - ModelBase(other), // copy the ID + ObjectBase(other), // copy the ID name(other.name), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) { this->set_material_id(other.material_id()); @@ -515,14 +445,14 @@ private: friend class cereal::access; ModelVolume() : object(nullptr) {} template void serialize(Archive &ar) { - ar(cereal::base_class(this)); + ar(cereal::base_class(this)); ar(name, config, m_mesh, m_type, m_material_id, m_convex_hull, m_transformation, m_is_splittable); } }; // A single instance of a ModelObject. // Knows the affine transformation of an object. -class ModelInstance : public ModelBase +class ModelInstance : public ObjectBase { public: enum EPrintVolumeState : unsigned char @@ -610,7 +540,7 @@ private: friend class cereal::access; ModelInstance() : object(nullptr) {} template void serialize(Archive &ar) { - ar(cereal::base_class(this)); + ar(cereal::base_class(this)); ar(m_transformation, print_volume_state); } }; @@ -620,7 +550,7 @@ private: // and with multiple modifier meshes. // A model groups multiple objects, each object having possibly multiple instances, // all objects may share mutliple materials. -class Model : public ModelBase +class Model : public ObjectBase { static unsigned int s_auto_extruder_id; @@ -637,12 +567,12 @@ public: /* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */ /* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */ - Model(const Model &rhs) : ModelBase(-1) { this->assign_copy(rhs); } - explicit Model(Model &&rhs) : ModelBase(-1) { this->assign_copy(std::move(rhs)); } + Model(const Model &rhs) : ObjectBase(-1) { this->assign_copy(rhs); } + explicit Model(Model &&rhs) : ObjectBase(-1) { this->assign_copy(std::move(rhs)); } Model& operator=(const Model &rhs) { this->assign_copy(rhs); return *this; } Model& operator=(Model &&rhs) { this->assign_copy(std::move(rhs)); return *this; } - MODELBASE_DERIVED_COPY_MOVE_CLONE(Model) + OBJECTBASE_DERIVED_COPY_MOVE_CLONE(Model) static Model read_from_file(const std::string &input_file, DynamicPrintConfig *config = nullptr, bool add_default_instances = true); static Model read_from_archive(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances = true); @@ -653,7 +583,7 @@ public: ModelObject* add_object(const char *name, const char *path, TriangleMesh &&mesh); ModelObject* add_object(const ModelObject &other); void delete_object(size_t idx); - bool delete_object(ModelID id); + bool delete_object(ObjectID id); bool delete_object(ModelObject* object); void clear_objects(); @@ -700,16 +630,16 @@ public: std::string propose_export_file_name_and_path(const std::string &new_extension) const; private: - MODELBASE_DERIVED_PRIVATE_COPY_MOVE(Model) + OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE(Model) friend class cereal::access; template void serialize(Archive &ar) { - ar(cereal::base_class(this), materials, objects); + ar(cereal::base_class(this), materials, objects); } }; -#undef MODELBASE_DERIVED_COPY_MOVE_CLONE -#undef MODELBASE_DERIVED_PRIVATE_COPY_MOVE +#undef OBJECTBASE_DERIVED_COPY_MOVE_CLONE +#undef OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE // Test whether the two models contain the same number of ModelObjects with the same set of IDs // ordered in the same order. In that case it is not necessary to kill the background processing. @@ -729,6 +659,6 @@ void check_model_ids_validity(const Model &model); void check_model_ids_equal(const Model &model1, const Model &model2); #endif /* NDEBUG */ -} +} // namespace Slic3r -#endif +#endif /* slic3r_Model_hpp_ */ diff --git a/src/libslic3r/ObjectID.cpp b/src/libslic3r/ObjectID.cpp new file mode 100644 index 000000000..90681a5a6 --- /dev/null +++ b/src/libslic3r/ObjectID.cpp @@ -0,0 +1,9 @@ +#include "ObjectID.hpp" + +namespace Slic3r { + +size_t ObjectBase::s_last_id = 0; + +} // namespace Slic3r + +// CEREAL_REGISTER_TYPE(Slic3r::ObjectBase) diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp new file mode 100644 index 000000000..6f9c3fcff --- /dev/null +++ b/src/libslic3r/ObjectID.hpp @@ -0,0 +1,87 @@ +#ifndef slic3r_ObjectID_hpp_ +#define slic3r_ObjectID_hpp_ + +#include +#include +#include +#include +#include + +namespace Slic3r { + +// Unique identifier of a mutable object accross the application. +// Used to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject) +// (for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial classes) +// and to serialize / deserialize an object onto the Undo / Redo stack. +// Valid IDs are strictly positive (non zero). +// It is declared as an object, as some compilers (notably msvcc) consider a typedef size_t equivalent to size_t +// for parameter overload. +class ObjectID +{ +public: + ObjectID(size_t id) : id(id) {} + + bool operator==(const ObjectID &rhs) const { return this->id == rhs.id; } + bool operator!=(const ObjectID &rhs) const { return this->id != rhs.id; } + bool operator< (const ObjectID &rhs) const { return this->id < rhs.id; } + bool operator> (const ObjectID &rhs) const { return this->id > rhs.id; } + bool operator<=(const ObjectID &rhs) const { return this->id <= rhs.id; } + bool operator>=(const ObjectID &rhs) const { return this->id >= rhs.id; } + + bool valid() const { return id != 0; } + + size_t id; + +private: + ObjectID() {} + + friend class cereal::access; + template void serialize(Archive &ar) { ar(id); } +}; + +// Base for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial to provide a unique ID +// to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject). +// Achtung! The s_last_id counter is not thread safe, so it is expected, that the ObjectBase derived instances +// are only instantiated from the main thread. +class ObjectBase +{ +public: + ObjectID id() const { return m_id; } + +protected: + // Constructors to be only called by derived classes. + // Default constructor to assign a unique ID. + ObjectBase() : m_id(generate_new_id()) {} + // Constructor with ignored int parameter to assign an invalid ID, to be replaced + // by an existing ID copied from elsewhere. + ObjectBase(int) : m_id(ObjectID(0)) {} + // The class tree will have virtual tables and type information. + virtual ~ObjectBase() {} + + // Use with caution! + void set_new_unique_id() { m_id = generate_new_id(); } + void set_invalid_id() { m_id = 0; } + // Use with caution! + void copy_id(const ObjectBase &rhs) { m_id = rhs.id(); } + + // Override this method if a ObjectBase derived class owns other ObjectBase derived instances. + void assign_new_unique_ids_recursive() { this->set_new_unique_id(); } + +private: + ObjectID m_id; + + static inline ObjectID generate_new_id() { return ObjectID(++ s_last_id); } + static size_t s_last_id; + + friend ObjectID wipe_tower_object_id(); + friend ObjectID wipe_tower_instance_id(); + + friend class cereal::access; + template void serialize(Archive &ar) { ar(m_id); } + ObjectBase(const ObjectID id) : m_id(id) {} + template static void load_and_construct(Archive & ar, cereal::construct &construct) { ObjectID id; ar(id); construct(id); } +}; + +} // namespace Slic3r + +#endif /* slic3r_ObjectID_hpp_ */ diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index f9129f15a..6d8c6e2bb 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -732,8 +732,8 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co Moved, Deleted, }; - ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {} - ModelID id; + ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {} + ObjectID id; Status status; // Search by id. bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; } @@ -839,9 +839,9 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co print_object(print_object), trafo(print_object->trafo()), status(status) {} - PrintObjectStatus(ModelID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {} + PrintObjectStatus(ObjectID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {} // ID of the ModelObject & PrintObject - ModelID id; + ObjectID id; // Pointer to the old PrintObject PrintObject *print_object; // Trafo generated with model_object->world_matrix(true) diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index d4c39499c..d84d492a0 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -246,7 +246,7 @@ public: struct TaskParams { TaskParams() : single_model_object(0), single_model_instance_only(false), to_object_step(-1), to_print_step(-1) {} // If non-empty, limit the processing to this ModelObject. - ModelID single_model_object; + ObjectID single_model_object; // If set, only process single_model_object. Otherwise process everything, but single_model_object first. bool single_model_instance_only; // If non-negative, stop processing at the successive object step. diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index c73ae5650..aa7cf58b5 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -212,8 +212,8 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf Moved, Deleted, }; - ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {} - ModelID id; + ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {} + ObjectID id; Status status; // Search by id. bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; } @@ -316,9 +316,9 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf print_object(print_object), trafo(print_object->trafo()), status(status) {} - PrintObjectStatus(ModelID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {} + PrintObjectStatus(ObjectID id) : id(id), print_object(nullptr), trafo(Transform3d::Identity()), status(Unknown) {} // ID of the ModelObject & PrintObject - ModelID id; + ObjectID id; // Pointer to the old PrintObject SLAPrintObject *print_object; // Trafo generated with model_object->world_matrix(true) diff --git a/src/libslic3r/SLAPrint.hpp b/src/libslic3r/SLAPrint.hpp index b0c5a8fdc..9620e9b68 100644 --- a/src/libslic3r/SLAPrint.hpp +++ b/src/libslic3r/SLAPrint.hpp @@ -54,10 +54,10 @@ public: bool is_left_handed() const { return m_left_handed; } struct Instance { - Instance(ModelID instance_id, const Point &shift, float rotation) : instance_id(instance_id), shift(shift), rotation(rotation) {} + Instance(ObjectID instance_id, const Point &shift, float rotation) : instance_id(instance_id), shift(shift), rotation(rotation) {} bool operator==(const Instance &rhs) const { return this->instance_id == rhs.instance_id && this->shift == rhs.shift && this->rotation == rhs.rotation; } // ID of the corresponding ModelInstance. - ModelID instance_id; + ObjectID instance_id; // Slic3r::Point objects in scaled G-code coordinates Point shift; // Rotation along the Z axis, in radians. diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index cf8a49ea2..4116ac34b 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1812,14 +1812,14 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re struct ModelVolumeState { ModelVolumeState(const GLVolume *volume) : model_volume(nullptr), geometry_id(volume->geometry_id), volume_idx(-1) {} - ModelVolumeState(const ModelVolume *model_volume, const ModelID &instance_id, const GLVolume::CompositeID &composite_id) : + ModelVolumeState(const ModelVolume *model_volume, const ObjectID &instance_id, const GLVolume::CompositeID &composite_id) : model_volume(model_volume), geometry_id(std::make_pair(model_volume->id().id, instance_id.id)), composite_id(composite_id), volume_idx(-1) {} - ModelVolumeState(const ModelID &volume_id, const ModelID &instance_id) : + ModelVolumeState(const ObjectID &volume_id, const ObjectID &instance_id) : model_volume(nullptr), geometry_id(std::make_pair(volume_id.id, instance_id.id)), volume_idx(-1) {} bool new_geometry() const { return this->volume_idx == size_t(-1); } const ModelVolume *model_volume; - // ModelID of ModelVolume + ModelID of ModelInstance - // or timestamp of an SLAPrintObjectStep + ModelID of ModelInstance + // ObjectID of ModelVolume + ObjectID of ModelInstance + // or timestamp of an SLAPrintObjectStep + ObjectID of ModelInstance std::pair geometry_id; GLVolume::CompositeID composite_id; // Volume index in the new GLVolume vector. diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index cfe07a61c..9e703caaa 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -380,7 +380,7 @@ bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point) const bool GLGizmoSlaSupports::is_mesh_update_necessary() const { return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty()) - && ((m_model_object->id() != m_current_mesh_model_id) || m_its == nullptr); + && ((m_model_object->id() != m_current_mesh_object_id) || m_its == nullptr); } void GLGizmoSlaSupports::update_mesh() @@ -390,7 +390,7 @@ void GLGizmoSlaSupports::update_mesh() // This mesh does not account for the possible Z up SLA offset. m_mesh = &m_model_object->volumes.front()->mesh(); m_its = &m_mesh->its; - m_current_mesh_model_id = m_model_object->id(); + m_current_mesh_object_id = m_model_object->id(); m_editing_mode = false; m_AABB.deinit(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index 30238cc9d..8a5cdf3ef 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -26,7 +26,7 @@ class GLGizmoSlaSupports : public GLGizmoBase { private: ModelObject* m_model_object = nullptr; - ModelID m_current_mesh_model_id = 0; + ObjectID m_current_mesh_object_id = 0; int m_active_instance = -1; float m_active_instance_bb_radius; // to cache the bb mutable float m_z_shift = 0.f; diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 802f8d284..c23f23e6b 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -171,7 +171,7 @@ private: Vec3d dragging_center; // Map from indices of ModelObject instances in Model::objects // to a set of indices of ModelVolume instances in ModelObject::instances - // Here the index means a position inside the respective std::vector, not ModelID. + // Here the index means a position inside the respective std::vector, not ObjectID. ObjectIdxsToInstanceIdxsMap content; }; diff --git a/xs/t/15_config.t b/xs/t/15_config.t index 397f7e479..1f9fc939b 100644 --- a/xs/t/15_config.t +++ b/xs/t/15_config.t @@ -9,19 +9,19 @@ use Test::More tests => 147; foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintConfig) { $config->set('layer_height', 0.3); ok abs($config->get('layer_height') - 0.3) < 1e-4, 'set/get float'; - is $config->serialize('layer_height'), '0.3', 'serialize float'; + is $config->opt_serialize('layer_height'), '0.3', 'serialize float'; $config->set('perimeters', 2); is $config->get('perimeters'), 2, 'set/get int'; - is $config->serialize('perimeters'), '2', 'serialize int'; + is $config->opt_serialize('perimeters'), '2', 'serialize int'; $config->set('extrusion_axis', 'A'); is $config->get('extrusion_axis'), 'A', 'set/get string'; - is $config->serialize('extrusion_axis'), 'A', 'serialize string'; + is $config->opt_serialize('extrusion_axis'), 'A', 'serialize string'; $config->set('notes', "foo\nbar"); is $config->get('notes'), "foo\nbar", 'set/get string with newline'; - is $config->serialize('notes'), 'foo\nbar', 'serialize string with newline'; + is $config->opt_serialize('notes'), 'foo\nbar', 'serialize string with newline'; $config->set_deserialize('notes', 'bar\nbaz'); is $config->get('notes'), "bar\nbaz", 'deserialize string with newline'; @@ -59,7 +59,7 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo ) { $config->set('filament_notes', $test_data->{values}); - is $config->serialize('filament_notes'), $test_data->{serialized}, 'serialize multi-string value ' . $test_data->{name}; + is $config->opt_serialize('filament_notes'), $test_data->{serialized}, 'serialize multi-string value ' . $test_data->{name}; $config->set_deserialize('filament_notes', ''); is_deeply $config->get('filament_notes'), [], 'deserialize multi-string value - empty ' . $test_data->{name}; $config->set_deserialize('filament_notes', $test_data->{serialized}); @@ -68,12 +68,12 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo $config->set('first_layer_height', 0.3); ok abs($config->get('first_layer_height') - 0.3) < 1e-4, 'set/get absolute floatOrPercent'; - is $config->serialize('first_layer_height'), '0.3', 'serialize absolute floatOrPercent'; + is $config->opt_serialize('first_layer_height'), '0.3', 'serialize absolute floatOrPercent'; $config->set('first_layer_height', '50%'); $config->get_abs_value('first_layer_height'); ok abs($config->get_abs_value('first_layer_height') - 0.15) < 1e-4, 'set/get relative floatOrPercent'; - is $config->serialize('first_layer_height'), '50%', 'serialize relative floatOrPercent'; + is $config->opt_serialize('first_layer_height'), '50%', 'serialize relative floatOrPercent'; # Uh-oh, we have no point option to test at the moment #ok $config->set('print_center', [50,80]), 'valid point coordinates'; @@ -86,11 +86,10 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo $config->set('use_relative_e_distances', 1); is $config->get('use_relative_e_distances'), 1, 'set/get bool'; - is $config->serialize('use_relative_e_distances'), '1', 'serialize bool'; - + is $config->opt_serialize('use_relative_e_distances'), '1', 'serialize bool'; $config->set('gcode_flavor', 'teacup'); is $config->get('gcode_flavor'), 'teacup', 'set/get enum'; - is $config->serialize('gcode_flavor'), 'teacup', 'serialize enum'; + is $config->opt_serialize('gcode_flavor'), 'teacup', 'serialize enum'; $config->set_deserialize('gcode_flavor', 'mach3'); is $config->get('gcode_flavor'), 'mach3', 'deserialize enum (gcode_flavor)'; $config->set_deserialize('gcode_flavor', 'machinekit'); @@ -106,7 +105,7 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo is_deeply [ map $_->pp, @{$config->get('extruder_offset')} ], [[10,20],[30,45]], 'set/get points'; $config->set('extruder_offset', [Slic3r::Pointf->new(10,20),Slic3r::Pointf->new(30,45)]); is_deeply [ map $_->pp, @{$config->get('extruder_offset')} ], [[10,20],[30,45]], 'set/get points'; - is $config->serialize('extruder_offset'), '10x20,30x45', 'serialize points'; + is $config->opt_serialize('extruder_offset'), '10x20,30x45', 'serialize points'; $config->set_deserialize('extruder_offset', '20x10'); is_deeply [ map $_->pp, @{$config->get('extruder_offset')} ], [[20,10]], 'deserialize points'; $config->set_deserialize('extruder_offset', '0x0'); @@ -120,7 +119,7 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo # truncate ->get() to first decimal digit $config->set('nozzle_diameter', [0.2,3]); is_deeply [ map int($_*10)/10, @{$config->get('nozzle_diameter')} ], [0.2,3], 'set/get floats'; - is $config->serialize('nozzle_diameter'), '0.2,3', 'serialize floats'; + is $config->opt_serialize('nozzle_diameter'), '0.2,3', 'serialize floats'; $config->set_deserialize('nozzle_diameter', '0.1,0.4'); is_deeply [ map int($_*10)/10, @{$config->get('nozzle_diameter')} ], [0.1,0.4], 'deserialize floats'; $config->set_deserialize('nozzle_diameter', '3'); @@ -133,7 +132,7 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo $config->set('temperature', [180,210]); is_deeply $config->get('temperature'), [180,210], 'set/get ints'; - is $config->serialize('temperature'), '180,210', 'serialize ints'; + is $config->opt_serialize('temperature'), '180,210', 'serialize ints'; $config->set_deserialize('temperature', '195,220'); is_deeply $config->get('temperature'), [195,220], 'deserialize ints'; { @@ -147,7 +146,7 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo is $config->get_at('wipe', 0), 1, 'get_at bools'; is $config->get_at('wipe', 1), 0, 'get_at bools'; is $config->get_at('wipe', 9), 1, 'get_at bools'; - is $config->serialize('wipe'), '1,0', 'serialize bools'; + is $config->opt_serialize('wipe'), '1,0', 'serialize bools'; $config->set_deserialize('wipe', '0,1,1'); is_deeply $config->get('wipe'), [0,1,1], 'deserialize bools'; $config->set_deserialize('wipe', ''); @@ -162,7 +161,7 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo $config->set('post_process', ['foo','bar']); is_deeply $config->get('post_process'), ['foo','bar'], 'set/get strings'; - is $config->serialize('post_process'), 'foo;bar', 'serialize strings'; + is $config->opt_serialize('post_process'), 'foo;bar', 'serialize strings'; $config->set_deserialize('post_process', 'bar;baz'); is_deeply $config->get('post_process'), ['bar','baz'], 'deserialize strings'; { diff --git a/xs/xsp/Config.xsp b/xs/xsp/Config.xsp index 4d48a2c6f..c374880a1 100644 --- a/xs/xsp/Config.xsp +++ b/xs/xsp/Config.xsp @@ -33,7 +33,7 @@ %code{% RETVAL = ConfigBase__set_deserialize(THIS, opt_key, str); %}; void set_ifndef(t_config_option_key opt_key, SV* value, bool deserialize = false) %code{% ConfigBase__set_ifndef(THIS, opt_key, value, deserialize); %}; - std::string serialize(t_config_option_key opt_key); + std::string opt_serialize(t_config_option_key opt_key); double get_abs_value(t_config_option_key opt_key); %name{get_abs_value_over} double get_abs_value(t_config_option_key opt_key, double ratio_over); @@ -95,7 +95,7 @@ %code{% RETVAL = ConfigBase__set_deserialize(THIS, opt_key, str); %}; void set_ifndef(t_config_option_key opt_key, SV* value, bool deserialize = false) %code{% ConfigBase__set_ifndef(THIS, opt_key, value, deserialize); %}; - std::string serialize(t_config_option_key opt_key); + std::string opt_serialize(t_config_option_key opt_key); double get_abs_value(t_config_option_key opt_key); %name{get_abs_value_over} double get_abs_value(t_config_option_key opt_key, double ratio_over); From 5e846112eee7ff881b2fe5754d3136e152fe3220 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 2 Jul 2019 16:42:23 +0200 Subject: [PATCH 21/72] WIP UndoRedo: Added Undo/Redo stack, added Platter::take_snapshot(), experimental snapshots on loading STLs and increasing / decreasing model instances. --- src/libslic3r/Model.cpp | 27 +- src/libslic3r/Model.hpp | 62 ++-- src/libslic3r/ObjectID.cpp | 13 + src/libslic3r/ObjectID.hpp | 9 + src/libslic3r/SLA/SLACommon.hpp | 2 + src/libslic3r/Slicing.hpp | 5 + src/libslic3r/TriangleMesh.hpp | 19 ++ src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/Plater.cpp | 31 +- src/slic3r/GUI/Plater.hpp | 3 + src/slic3r/GUI/Selection.hpp | 3 +- src/slic3r/Utils/UndoRedo.cpp | 559 ++++++++++++++++++++++++++++++++ src/slic3r/Utils/UndoRedo.hpp | 58 ++++ 13 files changed, 742 insertions(+), 51 deletions(-) create mode 100644 src/slic3r/Utils/UndoRedo.cpp create mode 100644 src/slic3r/Utils/UndoRedo.hpp diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 2533f288b..5e166b44f 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -22,19 +22,6 @@ namespace Slic3r { unsigned int Model::s_auto_extruder_id = 1; -// Unique object / instance ID for the wipe tower. -ObjectID wipe_tower_object_id() -{ - static ObjectBase mine; - return mine.id(); -} - -ObjectID wipe_tower_instance_id() -{ - static ObjectBase mine; - return mine.id(); -} - Model& Model::assign_copy(const Model &rhs) { this->copy_id(rhs); @@ -1068,11 +1055,11 @@ void ModelObject::mirror(Axis axis) } // This method could only be called before the meshes of this ModelVolumes are not shared! -void ModelObject::scale_mesh(const Vec3d &versor) +void ModelObject::scale_mesh_after_creation(const Vec3d &versor) { for (ModelVolume *v : this->volumes) { - v->scale_geometry(versor); + v->scale_geometry_after_creation(versor); v->set_offset(versor.cwiseProduct(v->get_offset())); } this->invalidate_bounding_box(); @@ -1562,8 +1549,8 @@ void ModelVolume::center_geometry_after_creation() Vec3d shift = this->mesh().bounding_box().center(); if (!shift.isApprox(Vec3d::Zero())) { - m_mesh->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); - m_convex_hull->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); + const_cast(m_mesh.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); + const_cast(m_convex_hull.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); translate(shift); } } @@ -1716,10 +1703,10 @@ void ModelVolume::mirror(Axis axis) } // This method could only be called before the meshes of this ModelVolumes are not shared! -void ModelVolume::scale_geometry(const Vec3d& versor) +void ModelVolume::scale_geometry_after_creation(const Vec3d& versor) { - m_mesh->scale(versor); - m_convex_hull->scale(versor); + const_cast(m_mesh.get())->scale(versor); + const_cast(m_convex_hull.get())->scale(versor); } void ModelVolume::transform_this_mesh(const Transform3d &mesh_trafo, bool fix_left_handed) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 850ea4202..8e9f7ecaa 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -27,6 +27,10 @@ class ModelVolume; class Print; class SLAPrint; +namespace UndoRedo { + class StackImpl; +} + typedef std::string t_model_material_id; typedef std::string t_model_material_attribute; typedef std::map t_model_material_attributes; @@ -36,10 +40,6 @@ typedef std::vector ModelObjectPtrs; typedef std::vector ModelVolumePtrs; typedef std::vector ModelInstancePtrs; -// Unique object / instance ID for the wipe tower. -extern ObjectID wipe_tower_object_id(); -extern ObjectID wipe_tower_instance_id(); - #define OBJECTBASE_DERIVED_COPY_MOVE_CLONE(TYPE) \ /* Copy a model, copy the IDs. The Print::apply() will call the TYPE::copy() method */ \ /* to make a private copy for background processing. */ \ @@ -75,7 +75,7 @@ private: \ void assign_new_unique_ids_recursive(); // Material, which may be shared across multiple ModelObjects of a single Model. -class ModelMaterial : public ObjectBase +class ModelMaterial final : public ObjectBase { public: // Attributes are defined by the AMF file format, but they don't seem to be used by Slic3r for any purpose. @@ -104,6 +104,7 @@ private: ModelMaterial& operator=(ModelMaterial &&rhs) = delete; friend class cereal::access; + friend class UndoRedo::StackImpl; ModelMaterial() : m_model(nullptr) {} template void serialize(Archive &ar) { ar(cereal::base_class(this)); ar(attributes, config); } }; @@ -112,7 +113,7 @@ private: // and possibly having multiple modifier volumes, each modifier volume with its set of parameters and materials. // Each ModelObject may be instantiated mutliple times, each instance having different placement on the print bed, // different rotation and different uniform scaling. -class ModelObject : public ObjectBase +class ModelObject final : public ObjectBase { friend class Model; public: @@ -211,7 +212,7 @@ public: void mirror(Axis axis); // This method could only be called before the meshes of this ModelVolumes are not shared! - void scale_mesh(const Vec3d& versor); + void scale_mesh_after_creation(const Vec3d& versor); size_t materials_count() const; size_t facets_count() const; @@ -240,12 +241,6 @@ public: // Get count of errors in the mesh( or all object's meshes, if volume index isn't defined) int get_mesh_errors_count(const int vol_idx = -1) const; -protected: - friend class Print; - friend class SLAPrint; - // Called by Print::apply() to set the model pointer after making a copy. - void set_model(Model *model) { m_model = model; } - private: ModelObject(Model *model) : m_model(model), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {} @@ -272,7 +267,14 @@ private: mutable BoundingBoxf3 m_raw_mesh_bounding_box; mutable bool m_raw_mesh_bounding_box_valid; + // Called by Print::apply() to set the model pointer after making a copy. + friend class Print; + friend class SLAPrint; + void set_model(Model *model) { m_model = model; } + + // Undo / Redo through the cereal serialization library friend class cereal::access; + friend class UndoRedo::StackImpl; ModelObject() : m_model(nullptr), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {} template void serialize(Archive &ar) { ar(cereal::base_class(this)); @@ -292,17 +294,17 @@ enum class ModelVolumeType : int { // An object STL, or a modifier volume, over which a different set of parameters shall be applied. // ModelVolume instances are owned by a ModelObject. -class ModelVolume : public ObjectBase +class ModelVolume final : public ObjectBase { public: std::string name; // The triangular model. const TriangleMesh& mesh() const { return *m_mesh.get(); } - void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared(mesh); } - void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared(std::move(mesh)); } - void set_mesh(std::shared_ptr &mesh) { m_mesh = mesh; } - void set_mesh(std::unique_ptr &&mesh) { m_mesh = std::move(mesh); } - void reset_mesh() { m_mesh = std::make_shared(); } + void set_mesh(const TriangleMesh &mesh) { m_mesh = std::make_shared(mesh); } + void set_mesh(TriangleMesh &&mesh) { m_mesh = std::make_shared(std::move(mesh)); } + void set_mesh(std::shared_ptr &mesh) { m_mesh = mesh; } + void set_mesh(std::unique_ptr &&mesh) { m_mesh = std::move(mesh); } + void reset_mesh() { m_mesh = std::make_shared(); } // Configuration parameters specific to an object model geometry or a modifier volume, // overriding the global Slic3r settings and the ModelObject settings. DynamicPrintConfig config; @@ -340,7 +342,7 @@ public: void mirror(Axis axis); // This method could only be called before the meshes of this ModelVolumes are not shared! - void scale_geometry(const Vec3d& versor); + void scale_geometry_after_creation(const Vec3d& versor); // Translates the mesh and the convex hull so that the origin of their vertices is in the center of this volume's bounding box. // Attention! This method may only be called just after ModelVolume creation! It must not be called once the TriangleMesh of this ModelVolume is shared! @@ -400,15 +402,15 @@ protected: private: // Parent object owning this ModelVolume. - ModelObject* object; + ModelObject* object; // The triangular model. - std::shared_ptr m_mesh; + std::shared_ptr m_mesh; // Is it an object to be printed, or a modifier volume? - ModelVolumeType m_type; - t_model_material_id m_material_id; + ModelVolumeType m_type; + t_model_material_id m_material_id; // The convex hull of this model's mesh. - std::shared_ptr m_convex_hull; - Geometry::Transformation m_transformation; + std::shared_ptr m_convex_hull; + Geometry::Transformation m_transformation; // flag to optimize the checking if the volume is splittable // -1 -> is unknown value (before first cheking) @@ -443,16 +445,16 @@ private: ModelVolume& operator=(ModelVolume &rhs) = delete; friend class cereal::access; + friend class UndoRedo::StackImpl; ModelVolume() : object(nullptr) {} template void serialize(Archive &ar) { - ar(cereal::base_class(this)); ar(name, config, m_mesh, m_type, m_material_id, m_convex_hull, m_transformation, m_is_splittable); } }; // A single instance of a ModelObject. // Knows the affine transformation of an object. -class ModelInstance : public ObjectBase +class ModelInstance final : public ObjectBase { public: enum EPrintVolumeState : unsigned char @@ -538,6 +540,7 @@ private: ModelInstance& operator=(ModelInstance &&rhs) = delete; friend class cereal::access; + friend class UndoRedo::StackImpl; ModelInstance() : object(nullptr) {} template void serialize(Archive &ar) { ar(cereal::base_class(this)); @@ -550,7 +553,7 @@ private: // and with multiple modifier meshes. // A model groups multiple objects, each object having possibly multiple instances, // all objects may share mutliple materials. -class Model : public ObjectBase +class Model final : public ObjectBase { static unsigned int s_auto_extruder_id; @@ -633,6 +636,7 @@ private: OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE(Model) friend class cereal::access; + friend class UndoRedo::StackImpl; template void serialize(Archive &ar) { ar(cereal::base_class(this), materials, objects); } diff --git a/src/libslic3r/ObjectID.cpp b/src/libslic3r/ObjectID.cpp index 90681a5a6..b188d84c0 100644 --- a/src/libslic3r/ObjectID.cpp +++ b/src/libslic3r/ObjectID.cpp @@ -4,6 +4,19 @@ namespace Slic3r { size_t ObjectBase::s_last_id = 0; +// Unique object / instance ID for the wipe tower. +ObjectID wipe_tower_object_id() +{ + static ObjectBase mine; + return mine.id(); +} + +ObjectID wipe_tower_instance_id() +{ + static ObjectBase mine; + return mine.id(); +} + } // namespace Slic3r // CEREAL_REGISTER_TYPE(Slic3r::ObjectBase) diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp index 6f9c3fcff..f00d6f61e 100644 --- a/src/libslic3r/ObjectID.hpp +++ b/src/libslic3r/ObjectID.hpp @@ -9,6 +9,10 @@ namespace Slic3r { +namespace UndoRedo { + class StackImpl; +}; + // Unique identifier of a mutable object accross the application. // Used to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject) // (for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial classes) @@ -75,6 +79,7 @@ private: friend ObjectID wipe_tower_object_id(); friend ObjectID wipe_tower_instance_id(); + friend class Slic3r::UndoRedo::StackImpl; friend class cereal::access; template void serialize(Archive &ar) { ar(m_id); } @@ -82,6 +87,10 @@ private: template static void load_and_construct(Archive & ar, cereal::construct &construct) { ObjectID id; ar(id); construct(id); } }; +// Unique object / instance ID for the wipe tower. +extern ObjectID wipe_tower_object_id(); +extern ObjectID wipe_tower_instance_id(); + } // namespace Slic3r #endif /* slic3r_ObjectID_hpp_ */ diff --git a/src/libslic3r/SLA/SLACommon.hpp b/src/libslic3r/SLA/SLACommon.hpp index 855802759..247e89dbb 100644 --- a/src/libslic3r/SLA/SLACommon.hpp +++ b/src/libslic3r/SLA/SLACommon.hpp @@ -43,6 +43,8 @@ struct SupportPoint { bool operator==(const SupportPoint& sp) const { return (pos==sp.pos) && head_front_radius==sp.head_front_radius && is_new_island==sp.is_new_island; } bool operator!=(const SupportPoint& sp) const { return !(sp == (*this)); } + + template void serialize(Archive &ar) { ar(pos, head_front_radius, is_new_island); } }; /// An index-triangle structure for libIGL functions. Also serves as an diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index fa5a12f9c..cd6affdeb 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -171,4 +171,9 @@ extern int generate_layer_height_texture( }; // namespace Slic3r +namespace cereal +{ + template void serialize(Archive& archive, Slic3r::t_layer_height_range &lhr) { archive(lhr.first, lhr.second); } +} + #endif /* slic3r_Slicing_hpp_ */ diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 054a98935..1fc512893 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -195,4 +195,23 @@ TriangleMesh make_sphere(double rho, double fa=(2*PI/360)); } +// Serialization through the Cereal library +namespace cereal { + template struct specialize {}; + template void load(Archive &archive, Slic3r::TriangleMesh &mesh) { + stl_file &stl = mesh.stl; + stl.stats.type = inmemory; + archive(stl.stats.number_of_facets, stl.stats.original_num_facets); + stl_allocate(&stl); + archive.loadBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50); + stl_get_size(&stl); + mesh.repair(); + } + template void save(Archive &archive, const Slic3r::TriangleMesh &mesh) { + const stl_file& stl = mesh.stl; + archive(stl.stats.number_of_facets, stl.stats.original_num_facets); + archive.saveBinary((char*)stl.facet_start.data(), stl.facet_start.size() * 50); + } +} + #endif diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 1867a8186..da1afdfee 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -146,6 +146,8 @@ set(SLIC3R_GUI_SOURCES Utils/PresetUpdater.hpp Utils/Time.cpp Utils/Time.hpp + Utils/UndoRedo.cpp + Utils/UndoRedo.hpp Utils/HexFile.cpp Utils/HexFile.hpp ) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 9d0c979b6..f08480559 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -69,6 +69,7 @@ #include "../Utils/ASCIIFolding.hpp" #include "../Utils/PrintHost.hpp" #include "../Utils/FixModelByWin10.hpp" +#include "../Utils/UndoRedo.hpp" #include // Needs to be last because reasons :-/ #include "WipeTowerDialog.hpp" @@ -1182,6 +1183,8 @@ const std::regex PlaterDropTarget::pattern_drop(".*[.](stl|obj|amf|3mf|prusa)", bool PlaterDropTarget::OnDropFiles(wxCoord x, wxCoord y, const wxArrayString &filenames) { + plater->take_snapshot(_(L("Load Files"))); + std::vector paths; for (const auto &filename : filenames) { @@ -1247,6 +1250,7 @@ struct Plater::priv Slic3r::Model model; PrinterTechnology printer_technology = ptFFF; Slic3r::GCodePreviewData gcode_preview_data; + Slic3r::UndoRedo::Stack undo_redo_stack; // GUI elements wxSizer* panel_sizer{ nullptr }; @@ -1545,6 +1549,10 @@ struct Plater::priv void split_object(); void split_volume(); void scale_selection_to_fit_print_volume(); + + void take_snapshot(const std::string& snapshot_name) { this->undo_redo_stack.take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection()); } + void take_snapshot(const wxString& snapshot_name) { this->take_snapshot(std::string(snapshot_name.ToUTF8().data())); } + bool background_processing_enabled() const { return this->get_config("background_processing") == "1"; } void update_print_volume_state(); void schedule_background_process(); @@ -1775,6 +1783,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // updates camera type from .ini file camera.set_type(get_config("use_perspective_camera")); + + this->undo_redo_stack.initialize(model, view3D->get_canvas3d()->get_selection()); } void Plater::priv::update(bool force_full_scene_refresh) @@ -2139,7 +2149,7 @@ std::vector Plater::priv::load_model_objects(const ModelObjectPtrs &mode // the size of the object is too big -> this could lead to overflow when moving to clipper coordinates, // so scale down the mesh double inv = 1. / max_ratio; - object->scale_mesh(Vec3d(inv, inv, inv)); + object->scale_mesh_after_creation(Vec3d(inv, inv, inv)); object->origin_translation = Vec3d::Zero(); object->center_around_origin(); scaled_down = true; @@ -2355,6 +2365,8 @@ void Plater::priv::delete_object_from_model(size_t obj_idx) void Plater::priv::reset() { + this->take_snapshot(_(L("Reset Project"))); + set_project_filename(wxEmptyString); // Prevent toolpaths preview from rendering while we modify the Print object @@ -3515,6 +3527,8 @@ void Plater::new_project() void Plater::load_project() { + this->take_snapshot(_(L("Load Project"))); + wxString input_file; wxGetApp().load_project(this, input_file); @@ -3593,6 +3607,7 @@ void Plater::delete_object_from_model(size_t obj_idx) { p->delete_object_from_mo void Plater::remove_selected() { + this->take_snapshot(_(L("Delete Selected Objects"))); this->p->view3D->delete_selected(); } @@ -3600,6 +3615,8 @@ void Plater::increase_instances(size_t num) { if (! can_increase_instances()) { return; } + this->take_snapshot(_(L("Increase Instances"))); + int obj_idx = p->get_selected_object_idx(); ModelObject* model_object = p->model.objects[obj_idx]; @@ -3634,6 +3651,8 @@ void Plater::decrease_instances(size_t num) { if (! can_decrease_instances()) { return; } + this->take_snapshot(_(L("Decrease Instances"))); + int obj_idx = p->get_selected_object_idx(); ModelObject* model_object = p->model.objects[obj_idx]; @@ -3982,6 +4001,16 @@ void Plater::send_gcode() } } +void Plater::take_snapshot(const std::string &snapshot_name) +{ + p->take_snapshot(snapshot_name); +} + +void Plater::take_snapshot(const wxString &snapshot_name) +{ + p->take_snapshot(snapshot_name); +} + void Plater::on_extruders_change(int num_extruders) { auto& choices = sidebar().combos_filament(); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 2851af654..9a6bcda7b 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -179,6 +179,9 @@ public: void fix_through_netfabb(const int obj_idx, const int vol_idx = -1); void send_gcode(); + void take_snapshot(const std::string &snapshot_name); + void take_snapshot(const wxString &snapshot_name); + void on_extruders_change(int extruders_count); void on_config_change(const DynamicPrintConfig &config); // On activating the parent window. diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index c23f23e6b..17ae72356 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -3,6 +3,7 @@ #include #include "libslic3r/Geometry.hpp" +#include "libslic3r/ObjectID.hpp" #include "3DScene.hpp" #if ENABLE_RENDER_SELECTION_CENTER @@ -66,7 +67,7 @@ private: Enum m_value; }; -class Selection +class Selection : public Slic3r::ObjectBase { public: typedef std::set IndicesList; diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp new file mode 100644 index 000000000..9263db65e --- /dev/null +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -0,0 +1,559 @@ +#include "UndoRedo.hpp" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#define CEREAL_FUTURE_EXPERIMENTAL +#include + +#include + +#include + +namespace Slic3r { +namespace UndoRedo { + +// Time interval, start is closed, end is open. +struct Interval +{ +public: + Interval(size_t begin, size_t end) : m_begin(begin), m_end(end) {} + + size_t begin() const { return m_begin; } + size_t end() const { return m_end; } + + bool is_valid() const { return m_begin >= 0 && m_begin < m_end; } + // This interval comes strictly before the rhs interval. + bool strictly_before(const Interval &rhs) const { return this->is_valid() && rhs.is_valid() && m_end <= rhs.m_begin; } + // This interval comes strictly after the rhs interval. + bool strictly_after(const Interval &rhs) const { return this->is_valid() && rhs.is_valid() && rhs.m_end <= m_begin; } + + bool operator<(const Interval &rhs) const { return (m_begin < rhs.m_begin) || (m_begin == rhs.m_begin && m_end < rhs.m_end); } + bool operator==(const Interval &rhs) const { return m_begin == rhs.m_begin && m_end == rhs.m_end; } + + void trim_end(size_t new_end) { m_end = std::min(m_end, new_end); } + void extend_end(size_t new_end) { assert(new_end >= m_end); m_end = new_end; } + +private: + size_t m_begin; + size_t m_end; +}; + +// History of a single object tracked by the Undo / Redo stack. The object may be mutable or immutable. +class ObjectHistoryBase +{ +public: + virtual ~ObjectHistoryBase() {} + + // If the history is empty, the ObjectHistory object could be released. + virtual bool empty() = 0; + + // Release all data after the given timestamp. For the ImmutableObjectHistory, the shared pointer is NOT released. + virtual void relese_after_timestamp(size_t timestamp) = 0; + +#ifndef NDEBUG + virtual bool validate() = 0; +#endif /* NDEBUG */ +}; + +template class ObjectHistory : public ObjectHistoryBase +{ +public: + ~ObjectHistory() override {} + + // If the history is empty, the ObjectHistory object could be released. + bool empty() override { return m_history.empty(); } + + // Release all data after the given timestamp. The shared pointer is NOT released. + void relese_after_timestamp(size_t timestamp) override { + assert(! m_history.empty()); + assert(this->validate()); + // it points to an interval which either starts with timestamp, or follows the timestamp. + auto it = std::lower_bound(m_history.begin(), m_history.end(), T(timestamp, timestamp)); + if (it == m_history.end()) { + auto it_prev = it; + -- it_prev; + assert(it_prev->begin() < timestamp); + // Trim the last interval with timestamp. + it_prev->trim_end(timestamp); + } + m_history.erase(it, m_history.end()); + assert(this->validate()); + } + +protected: + std::vector m_history; +}; + +// Big objects (mainly the triangle meshes) are tracked by Slicer using the shared pointers +// and they are immutable. +// The Undo / Redo stack therefore may keep a shared pointer to these immutable objects +// and as long as the ref counter of these objects is higher than 1 (1 reference is held +// by the Undo / Redo stack), there is no cost associated to holding the object +// at the Undo / Redo stack. Once the reference counter drops to 1 (only the Undo / Redo +// stack holds the reference), the shared pointer may get serialized (and possibly compressed) +// and the shared pointer may be released. +// The history of a single immutable object may not be continuous, as an immutable object may +// be removed from the scene while being kept at the Copy / Paste stack. +template +class ImmutableObjectHistory : public ObjectHistory +{ +public: + ImmutableObjectHistory(std::shared_ptr shared_object) : m_shared_object(shared_object) {} + ~ImmutableObjectHistory() override {} + + void save(size_t active_snapshot_time, size_t current_time) { + assert(m_history.empty() || m_history.back().end() <= active_snapshot_time); + if (m_history.empty() || m_history.back().end() < active_snapshot_time) + m_history.emplace_back(active_snapshot_time, current_time + 1); + else + m_history.back().extend_end(current_time + 1); + } + + bool has_snapshot(size_t timestamp) { + if (m_history.empty()) + return false; + auto it = std::lower_bound(m_history.begin(), m_history.end(), Interval(timestamp, timestamp)); + if (it == m_history.end() || it->begin() >= timestamp) { + if (it == m_history.begin()) + return false; + -- it; + } + return timestamp >= it->begin() && timestamp < it->end(); + } + + bool is_serialized() const { return m_shared_object.get() == nullptr; } + const std::string& serialized_data() const { return m_serialized; } + std::shared_ptr& shared_ptr(StackImpl &stack) { + if (m_shared_object.get() == nullptr && ! this->m_serialized.empty()) { + // Deserialize the object. + std::istringstream iss(m_serialized); + { + Slic3r::UndoRedo::InputArchive archive(stack, iss); + std::unique_ptr::type> mesh(new std::remove_const::type()); + archive(*mesh.get()); + m_shared_object = std::move(mesh); + } + } + return m_shared_object; + } + +#ifndef NDEBUG + bool validate() override; +#endif /* NDEBUG */ + +private: + // Either the source object is held by a shared pointer and the m_serialized field is empty, + // or the shared pointer is null and the object is being serialized into m_serialized. + std::shared_ptr m_shared_object; + std::string m_serialized; +}; + +struct MutableHistoryInterval +{ +private: + struct Data + { + // Reference counter of this data chunk. We may have used shared_ptr, but the shared_ptr is thread safe + // with the associated cost of CPU cache invalidation on refcount change. + size_t refcnt; + size_t size; + char data[1]; + + bool matches(const std::string& rhs) { return this->size == rhs.size() && memcmp(this->data, rhs.data(), this->size) == 0; } + }; + + Interval m_interval; + Data *m_data; + +public: + MutableHistoryInterval(const Interval &interval, const std::string &input_data) : m_interval(interval), m_data(nullptr) { + m_data = (Data*)new char[offsetof(Data, data) + input_data.size()]; + m_data->refcnt = 1; + m_data->size = input_data.size(); + memcpy(m_data->data, input_data.data(), input_data.size()); + } + + MutableHistoryInterval(const Interval &interval, MutableHistoryInterval &other) : m_interval(interval), m_data(other.m_data) { + ++ m_data->refcnt; + } + + // as a key for std::lower_bound + MutableHistoryInterval(const size_t begin, const size_t end) : m_interval(begin, end), m_data(nullptr) {} + + MutableHistoryInterval(MutableHistoryInterval&& rhs) : m_interval(rhs.m_interval), m_data(rhs.m_data) { rhs.m_data = nullptr; } + MutableHistoryInterval& operator=(MutableHistoryInterval&& rhs) { m_interval = rhs.m_interval; m_data = rhs.m_data; rhs.m_data = nullptr; return *this; } + + ~MutableHistoryInterval() { + if (m_data != nullptr && -- m_data->refcnt == 0) + delete[] (char*)m_data; + } + + const Interval& interval() const { return m_interval; } + size_t begin() const { return m_interval.begin(); } + size_t end() const { return m_interval.end(); } + void trim_end(size_t timestamp) { m_interval.trim_end(timestamp); } + void extend_end(size_t timestamp) { m_interval.extend_end(timestamp); } + + bool operator<(const MutableHistoryInterval& rhs) const { return m_interval < rhs.m_interval; } + bool operator==(const MutableHistoryInterval& rhs) const { return m_interval == rhs.m_interval; } + + const char* data() const { return m_data->data; } + size_t size() const { return m_data->size; } + size_t refcnt() const { return m_data->refcnt; } + bool matches(const std::string& data) { return m_data->matches(data); } + +private: + MutableHistoryInterval(const MutableHistoryInterval &rhs); + MutableHistoryInterval& operator=(const MutableHistoryInterval &rhs); +}; + +// Smaller objects (Model, ModelObject, ModelInstance, ModelVolume, DynamicPrintConfig) +// are mutable and there is not tracking of the changes, therefore a snapshot needs to be +// taken every time and compared to the previous data at the Undo / Redo stack. +// The serialized data is stored if it is different from the last value on the stack, otherwise +// the serialized data is discarded. +// The history of a single mutable object may not be continuous, as an mutable object may +// be removed from the scene while being kept at the Copy / Paste stack, therefore an object snapshot +// with the same serialized object data may be shared by multiple history intervals. +template +class MutableObjectHistory : public ObjectHistory +{ +public: + ~MutableObjectHistory() override {} + + void save(size_t active_snapshot_time, size_t current_time, const std::string &data) { + assert(m_history.empty() || m_history.back().end() <= active_snapshot_time); + if (m_history.empty() || m_history.back().end() < active_snapshot_time) { + if (! m_history.empty() && m_history.back().matches(data)) + // Share the previous data by reference counting. + m_history.emplace_back(Interval(current_time, current_time + 1), m_history.back()); + else + // Allocate new data. + m_history.emplace_back(Interval(current_time, current_time + 1), data); + } else { + assert(! m_history.empty()); + assert(m_history.back().end() == active_snapshot_time); + if (m_history.back().matches(data)) + // Just extend the last interval using the old data. + m_history.back().extend_end(current_time + 1); + else + // Allocate new data time continuous with the previous data. + m_history.emplace_back(Interval(active_snapshot_time, current_time + 1), data); + } + } + + std::string load(size_t timestamp) const { + assert(! m_history.empty()); + auto it = std::lower_bound(m_history.begin(), m_history.end(), MutableHistoryInterval(timestamp, timestamp)); + if (it == m_history.end() || it->begin() >= timestamp) { + assert(it != m_history.begin()); + -- it; + } + assert(timestamp >= it->begin() && timestamp < it->end()); + return std::string(it->data(), it->data() + it->size()); + } + +#ifndef NDEBUG + bool validate() override; +#endif /* NDEBUG */ +}; + +#ifndef NDEBUG +template +bool ImmutableObjectHistory::validate() +{ + // The immutable object content is captured either by a shared object, or by its serialization, but not both. + assert(! m_shared_object == ! m_serialized.empty()); + // Verify that the history intervals are sorted and do not overlap. + if (! m_history.empty()) + for (size_t i = 1; i < m_history.size(); ++ i) + assert(m_history[i - 1].strictly_before(m_history[i])); + return true; +} +#endif /* NDEBUG */ + +#ifndef NDEBUG +template +bool MutableObjectHistory::validate() +{ + // Verify that the history intervals are sorted and do not overlap, and that the data reference counters are correct. + if (! m_history.empty()) { + std::map refcntrs; + assert(m_history.front().data() != nullptr); + ++ refcntrs[m_history.front().data()]; + for (size_t i = 1; i < m_history.size(); ++ i) { + assert(m_history[i - 1].interval().strictly_before(m_history[i].interval())); + ++ refcntrs[m_history[i].data()]; + } + for (const auto &hi : m_history) { + assert(hi.data() != nullptr); + assert(refcntrs[hi.data()] == hi.refcnt()); + } + } + return true; +} +#endif /* NDEBUG */ + +class StackImpl +{ +public: + // Stack needs to be initialized. An empty stack is not valid, there must be a "New Project" status stored at the beginning. + StackImpl() : m_active_snapshot_time(0), m_current_time(0) {} + + // The Undo / Redo stack is being initialized with an empty model and an empty selection. + // The first snapshot cannot be removed. + void initialize(const Slic3r::Model &model, const Slic3r::GUI::Selection &selection); + + // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time. + void take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection); + void load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GUI::Selection &selection); + + // Snapshot history (names with timestamps). + const std::vector& snapshots() const { return m_snapshots; } + +//protected: + void save_model(const Slic3r::Model &model, size_t snapshot_time); + void save_selection(const Slic3r::Model& model, const Slic3r::GUI::Selection &selection, size_t snapshot_time); + void load_model(const Slic3r::Model &model, size_t snapshot_time); + void load_selection(const Slic3r::GUI::Selection &selection, size_t snapshot_time); + + template ObjectID save_mutable_object(const T &object); + template ObjectID save_immutable_object(std::shared_ptr &object); + template T* load_mutable_object(const Slic3r::ObjectID id); + template std::shared_ptr load_immutable_object(const Slic3r::ObjectID id); + template void load_mutable_object(const Slic3r::ObjectID id, T &target); + +private: + template ObjectID immutable_object_id(const std::shared_ptr &ptr) { + return this->immutable_object_id_impl((const void*)ptr.get()); + } + ObjectID immutable_object_id_impl(const void *ptr) { + auto it = m_shared_ptr_to_object_id.find(ptr); + if (it == m_shared_ptr_to_object_id.end()) { + // Allocate a new temporary ObjectID for this shared pointer. + ObjectBase object_with_id; + it = m_shared_ptr_to_object_id.insert(it, std::make_pair(ptr, object_with_id.id())); + } + return it->second; + } + + // Each individual object (Model, ModelObject, ModelInstance, ModelVolume, Selection, TriangleMesh) + // is stored with its own history, referenced by the ObjectID. Immutable objects do not provide + // their own IDs, therefore there are temporary IDs generated for them and stored to m_shared_ptr_to_object_id. + std::map> m_objects; + std::map m_shared_ptr_to_object_id; + // Snapshot history (names with timestamps). + std::vector m_snapshots; + // Timestamp of the active snapshot. + size_t m_active_snapshot_time; + // Logical time counter. m_current_time is being incremented with each snapshot taken. + size_t m_current_time; +}; + +using InputArchive = cereal::UserDataAdapter; +using OutputArchive = cereal::UserDataAdapter; + +} // namespace UndoRedo + +class Model; +class ModelObject; +class ModelVolume; +class ModelInstance; +class ModelMaterial; + +} // namespace Slic3r + +namespace cereal +{ + // Let cereal know that there are load / save non-member functions declared for ModelObject*, ignore serialization of pointers triggering + // static assert, that cereal does not support serialization of raw pointers. + template struct specialize {}; + template struct specialize {}; + template struct specialize {}; + template struct specialize {}; + template struct specialize {}; + + // Store ObjectBase derived class onto the Undo / Redo stack as a separate object, + // store just the ObjectID to this stream. + template void save(BinaryOutputArchive& ar, T* const& ptr) + { + ar(cereal::get_user_data(ar).save_mutable_object(*ptr)); + } + + // Load ObjectBase derived class from the Undo / Redo stack as a separate object + // based on the ObjectID loaded from this stream. + template void load(BinaryInputArchive& ar, T*& ptr) + { + Slic3r::UndoRedo::StackImpl& stack = cereal::get_user_data(ar); + size_t id; + ar(id); + ptr = stack.load_mutable_object(Slic3r::ObjectID(id)); + } + + // Store ObjectBase derived class onto the Undo / Redo stack as a separate object, + // store just the ObjectID to this stream. + template void save(BinaryOutputArchive& ar, std::shared_ptr& ptr) + { + ar(cereal::get_user_data(ar).save_immutable_object(ptr)); + } + + // Load ObjectBase derived class from the Undo / Redo stack as a separate object + // based on the ObjectID loaded from this stream. + template void load(BinaryInputArchive& ar, std::shared_ptr& ptr) + { + Slic3r::UndoRedo::StackImpl& stack = cereal::get_user_data(ar); + size_t id; + ar(id); + ptr = std::const_pointer_cast(stack.load_immutable_object(Slic3r::ObjectID(id))); + } + +#if 0 + void save(BinaryOutputArchive &ar, const Slic3r::GUI::Selection &selection) + { + size_t num = selection.get_volume_idxs().size(); + ar(num); + for (unsigned int volume_idx : selection.get_volume_idxs()) { + const Slic3r::GLVolume::CompositeID &id = selection.get_volume(volume_idx)->composite_id; + ar(id.object_id, id.volume_id, id.instance_id); + } + } + + template void load(BinaryInputArchive &ar, Slic3r::GUI::Selection &selection) + { + size_t num; + ar(num); + for (size_t i = 0; i < num; ++ i) { + Slic3r::GLVolume::CompositeID id; + ar(id.object_id, id.volume_id, id.instance_id); + } + } +#endif +} + +#include +#include +#include + +namespace Slic3r { +namespace UndoRedo { + +template ObjectID StackImpl::save_mutable_object(const T &object) +{ + // First find or allocate a history stack for the ObjectID of this object instance. + auto it_object_history = m_objects.find(object.id()); + if (it_object_history == m_objects.end()) + it_object_history = m_objects.insert(it_object_history, std::make_pair(object.id(), std::unique_ptr>(new MutableObjectHistory()))); + auto *object_history = static_cast*>(it_object_history->second.get()); + // Then serialize the object into a string. + std::ostringstream oss; + { + Slic3r::UndoRedo::OutputArchive archive(*this, oss); + archive(object); + } + object_history->save(m_active_snapshot_time, m_current_time, oss.str()); + return object.id(); +} + +template ObjectID StackImpl::save_immutable_object(std::shared_ptr &object) +{ + // First allocate a temporary ObjectID for this pointer. + ObjectID object_id = this->immutable_object_id(object); + // and find or allocate a history stack for the ObjectID associated to this shared_ptr. + auto it_object_history = m_objects.find(object_id); + if (it_object_history == m_objects.end()) + it_object_history = m_objects.insert(it_object_history, ObjectID, std::unique_ptr>(new ImmutableObjectHistory(object))); + auto *object_history = ; + // Then save the interval. + static_cast*>(it_object_history->second.get())->save(m_active_snapshot_time, m_current_time); + return object_id; +} + +template T* StackImpl::load_mutable_object(const Slic3r::ObjectID id) +{ + T *target = new T(); + this->load_mutable_object(id, *target); + return target; +} + +template std::shared_ptr StackImpl::load_immutable_object(const Slic3r::ObjectID id) +{ + // First find a history stack for the ObjectID of this object instance. + auto it_object_history = m_objects.find(id); + assert(it_object_history != m_objects.end()); + auto *object_history = static_cast*>(it_object_history->second.get()); + assert(object_history->has_snapshot(m_active_snapshot_time)); + return object_history->shared_ptr(*this); +} + +template void StackImpl::load_mutable_object(const Slic3r::ObjectID id, T &target) +{ + // First find a history stack for the ObjectID of this object instance. + auto it_object_history = m_objects.find(id); + assert(it_object_history != m_objects.end()); + auto *object_history = static_cast*>(it_object_history->second.get()); + // Then get the data associated with the object history and m_active_snapshot_time. + std::istringstream iss(object_history->load(m_active_snapshot_time)); + Slic3r::UndoRedo::InputArchive archive(*this, iss); + archive(target); +} + +// The Undo / Redo stack is being initialized with an empty model and an empty selection. +// The first snapshot cannot be removed. +void StackImpl::initialize(const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) +{ + assert(m_active_snapshot_time == 0); + assert(m_current_time == 0); + // The initial time interval will be <0, 1) + m_active_snapshot_time = SIZE_MAX; // let it overflow to zero in take_snapshot + m_current_time = 0; + this->take_snapshot("New Project", model, selection); +} + +// Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time. +void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) +{ + // Release old snapshot data. + ++ m_active_snapshot_time; + for (auto &kvp : m_objects) + kvp.second->relese_after_timestamp(m_active_snapshot_time); + { + auto it = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); + m_snapshots.erase(it, m_snapshots.end()); + } + // Take new snapshots. + this->save_mutable_object(model); +// this->save_mutable_object(selection); + // Save the snapshot info + m_snapshots.emplace_back(snapshot_name, m_current_time ++, model.id().id); +} + +void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GUI::Selection &selection) +{ + // Find the snapshot by time. It must exist. + const auto it_snapshot = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(timestamp)); + if (it_snapshot == m_snapshots.end() || it_snapshot->timestamp != timestamp) + throw std::runtime_error((boost::format("Snapshot with timestamp %1% does not exist") % timestamp).str()); + + this->load_mutable_object(ObjectID(it_snapshot->model_object_id), model); + this->load_mutable_object(selection.id(), selection); + this->m_active_snapshot_time = timestamp; +} + +// Wrappers of the private implementation. +Stack::Stack() : pimpl(new StackImpl()) {} +Stack::~Stack() {} +void Stack::initialize(const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) { pimpl->initialize(model, selection); } +void Stack::take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) { pimpl->take_snapshot(snapshot_name, model, selection); } +void Stack::load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GUI::Selection &selection) { pimpl->load_snapshot(timestamp, model, selection); } +const std::vector& Stack::snapshots() const { return pimpl->snapshots(); } + +} // namespace UndoRedo +} // namespace Slic3r diff --git a/src/slic3r/Utils/UndoRedo.hpp b/src/slic3r/Utils/UndoRedo.hpp new file mode 100644 index 000000000..d452e777c --- /dev/null +++ b/src/slic3r/Utils/UndoRedo.hpp @@ -0,0 +1,58 @@ +#ifndef slic3r_Utils_UndoRedo_hpp_ +#define slic3r_Utils_UndoRedo_hpp_ + +#include +#include + +namespace Slic3r { + +class Model; + +namespace GUI { + class Selection; +} // namespace GUI + +namespace UndoRedo { + +struct Snapshot +{ + Snapshot(size_t timestamp) : timestamp(timestamp) {} + Snapshot(const std::string &name, size_t timestamp, size_t model_object_id) : name(name), timestamp(timestamp), model_object_id(model_object_id) {} + + std::string name; + size_t timestamp; + size_t model_object_id; + + bool operator< (const Snapshot &rhs) const { return this->timestamp < rhs.timestamp; } + bool operator==(const Snapshot &rhs) const { return this->timestamp == rhs.timestamp; } +}; + +class StackImpl; + +class Stack +{ +public: + // Stack needs to be initialized. An empty stack is not valid, there must be a "New Project" status stored at the beginning. + Stack(); + ~Stack(); + + // The Undo / Redo stack is being initialized with an empty model and an empty selection. + // The first snapshot cannot be removed. + void initialize(const Slic3r::Model &model, const Slic3r::GUI::Selection &selection); + + // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time. + void take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection); + void load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GUI::Selection &selection); + + // Snapshot history (names with timestamps). + const std::vector& snapshots() const; + +private: + friend class StackImpl; + std::unique_ptr pimpl; +}; + +}; // namespace UndoRedo +}; // namespace Slic3r + +#endif /* slic3r_Utils_UndoRedo_hpp_ */ From 41255198634067481bc8671e08c819544adaba0e Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 2 Jul 2019 17:56:38 +0200 Subject: [PATCH 22/72] WIP Undo / Redo: Capturing of the triangle meshes. --- src/slic3r/Utils/UndoRedo.cpp | 44 ++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 9263db65e..1e49c1336 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -52,6 +52,10 @@ class ObjectHistoryBase public: virtual ~ObjectHistoryBase() {} + // Is the object captured by this history mutable or immutable? + virtual bool is_mutable() const = 0; + virtual bool is_immutable() const = 0; + // If the history is empty, the ObjectHistory object could be released. virtual bool empty() = 0; @@ -109,6 +113,9 @@ public: ImmutableObjectHistory(std::shared_ptr shared_object) : m_shared_object(shared_object) {} ~ImmutableObjectHistory() override {} + bool is_mutable() const override { return false; } + bool is_immutable() const override { return true; } + void save(size_t active_snapshot_time, size_t current_time) { assert(m_history.empty() || m_history.back().end() <= active_snapshot_time); if (m_history.empty() || m_history.back().end() < active_snapshot_time) @@ -229,6 +236,9 @@ class MutableObjectHistory : public ObjectHistory public: ~MutableObjectHistory() override {} + bool is_mutable() const override { return true; } + bool is_immutable() const override { return false; } + void save(size_t active_snapshot_time, size_t current_time, const std::string &data) { assert(m_history.empty() || m_history.back().end() <= active_snapshot_time); if (m_history.empty() || m_history.back().end() < active_snapshot_time) { @@ -344,6 +354,7 @@ private: } return it->second; } + void collect_garbage(); // Each individual object (Model, ModelObject, ModelInstance, ModelVolume, Selection, TriangleMesh) // is stored with its own history, referenced by the ObjectID. Immutable objects do not provide @@ -368,6 +379,7 @@ class ModelObject; class ModelVolume; class ModelInstance; class ModelMaterial; +class TriangleMesh; } // namespace Slic3r @@ -380,6 +392,7 @@ namespace cereal template struct specialize {}; template struct specialize {}; template struct specialize {}; + template struct specialize, cereal::specialization::non_member_load_save> {}; // Store ObjectBase derived class onto the Undo / Redo stack as a separate object, // store just the ObjectID to this stream. @@ -400,19 +413,19 @@ namespace cereal // Store ObjectBase derived class onto the Undo / Redo stack as a separate object, // store just the ObjectID to this stream. - template void save(BinaryOutputArchive& ar, std::shared_ptr& ptr) + template void save(BinaryOutputArchive &ar, const std::shared_ptr &ptr) { - ar(cereal::get_user_data(ar).save_immutable_object(ptr)); + ar(cereal::get_user_data(ar).save_immutable_object(const_cast&>(ptr))); } // Load ObjectBase derived class from the Undo / Redo stack as a separate object // based on the ObjectID loaded from this stream. - template void load(BinaryInputArchive& ar, std::shared_ptr& ptr) + template void load(BinaryInputArchive &ar, std::shared_ptr &ptr) { - Slic3r::UndoRedo::StackImpl& stack = cereal::get_user_data(ar); + Slic3r::UndoRedo::StackImpl &stack = cereal::get_user_data(ar); size_t id; ar(id); - ptr = std::const_pointer_cast(stack.load_immutable_object(Slic3r::ObjectID(id))); + ptr = stack.load_immutable_object(Slic3r::ObjectID(id)); } #if 0 @@ -469,10 +482,9 @@ template ObjectID StackImpl::save_immutable_object(std::shared_ptr>(new ImmutableObjectHistory(object))); - auto *object_history = ; + it_object_history = m_objects.emplace_hint(it_object_history, object_id, std::unique_ptr>(new ImmutableObjectHistory(object))); // Then save the interval. - static_cast*>(it_object_history->second.get())->save(m_active_snapshot_time, m_current_time); + static_cast*>(it_object_history->second.get())->save(m_active_snapshot_time, m_current_time); return object_id; } @@ -533,6 +545,8 @@ void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Mo // this->save_mutable_object(selection); // Save the snapshot info m_snapshots.emplace_back(snapshot_name, m_current_time ++, model.id().id); + // Release empty objects from the history. + this->collect_garbage(); } void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GUI::Selection &selection) @@ -547,6 +561,20 @@ void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GU this->m_active_snapshot_time = timestamp; } +void StackImpl::collect_garbage() +{ + // Purge objects with empty histories. + for (auto it = m_objects.begin(); it != m_objects.end();) { + if (it->second->empty()) { + if (it->second->is_immutable()) + // Release the immutable object from the ptr to ObjectID map. + this->m_objects.erase(it->first); + it = m_objects.erase(it); + } else + ++ it; + } +} + // Wrappers of the private implementation. Stack::Stack() : pimpl(new StackImpl()) {} Stack::~Stack() {} From e2a670218bd5a47c09595b7bc60e2fd7ee4c7858 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Wed, 3 Jul 2019 13:43:54 +0200 Subject: [PATCH 23/72] WIP Undo / Redo: Serializing the configs of ModelObject / ModelVolume / ModelMaterial as separate objects to conserve memory. --- src/libslic3r/Model.cpp | 41 ++++++-- src/libslic3r/Model.hpp | 193 ++++++++++++++++++++++++---------- src/libslic3r/ObjectID.hpp | 2 + src/libslic3r/Print.cpp | 4 +- src/libslic3r/SLAPrint.cpp | 2 +- src/slic3r/GUI/Selection.cpp | 2 +- src/slic3r/Utils/UndoRedo.cpp | 66 +++++++++--- 7 files changed, 229 insertions(+), 81 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 5e166b44f..16f8fec4e 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -607,11 +607,15 @@ ModelObject::~ModelObject() // maintains the m_model pointer ModelObject& ModelObject::assign_copy(const ModelObject &rhs) { - this->copy_id(rhs); + assert(this->id().invalid() || this->id() == rhs.id()); + assert(this->config.id().invalid() || this->config.id() == rhs.config.id()); + this->copy_id(rhs); this->name = rhs.name; this->input_file = rhs.input_file; + // Copies the config's ID this->config = rhs.config; + assert(this->config.id() == rhs.config.id()); this->sla_support_points = rhs.sla_support_points; this->sla_points_status = rhs.sla_points_status; this->layer_height_ranges = rhs.layer_height_ranges; @@ -643,11 +647,14 @@ ModelObject& ModelObject::assign_copy(const ModelObject &rhs) // maintains the m_model pointer ModelObject& ModelObject::assign_copy(ModelObject &&rhs) { + assert(this->id().invalid()); this->copy_id(rhs); this->name = std::move(rhs.name); this->input_file = std::move(rhs.input_file); + // Moves the config's ID this->config = std::move(rhs.config); + assert(this->config.id() == rhs.config.id()); this->sla_support_points = std::move(rhs.sla_support_points); this->sla_points_status = std::move(rhs.sla_points_status); this->layer_height_ranges = std::move(rhs.layer_height_ranges); @@ -1176,13 +1183,19 @@ ModelObjectPtrs ModelObject::cut(size_t instance, coordf_t z, bool keep_upper, b if (keep_upper && upper_mesh.facets_count() > 0) { ModelVolume* vol = upper->add_volume(upper_mesh); vol->name = volume->name; - vol->config = volume->config; + // Don't copy the config's ID. + static_cast(vol->config) = static_cast(volume->config); + assert(vol->config.id().valid()); + assert(vol->config.id() != volume->config.id()); vol->set_material(volume->material_id(), *volume->material()); } if (keep_lower && lower_mesh.facets_count() > 0) { ModelVolume* vol = lower->add_volume(lower_mesh); vol->name = volume->name; - vol->config = volume->config; + // Don't copy the config's ID. + static_cast(vol->config) = static_cast(volume->config); + assert(vol->config.id().valid()); + assert(vol->config.id() != volume->config.id()); vol->set_material(volume->material_id(), *volume->material()); // Compute the lower part instances' bounding boxes to figure out where to place @@ -1257,7 +1270,10 @@ void ModelObject::split(ModelObjectPtrs* new_objects) // XXX: this seems to be the only real usage of m_model, maybe refactor this so that it's not needed? ModelObject* new_object = m_model->add_object(); new_object->name = this->name; - new_object->config = this->config; + // Don't copy the config's ID. + static_cast(new_object->config) = static_cast(this->config); + assert(new_object->config.id().valid()); + assert(new_object->config.id() != this->config.id()); new_object->instances.reserve(this->instances.size()); for (const ModelInstance *model_instance : this->instances) new_object->add_instance(*model_instance); @@ -1852,19 +1868,24 @@ void check_model_ids_validity(const Model &model) { std::set ids; auto check = [&ids](ObjectID id) { - assert(id.id > 0); + assert(id.valid()); assert(ids.find(id) == ids.end()); ids.insert(id); }; for (const ModelObject *model_object : model.objects) { check(model_object->id()); - for (const ModelVolume *model_volume : model_object->volumes) + check(model_object->config.id()); + for (const ModelVolume *model_volume : model_object->volumes) { check(model_volume->id()); + check(model_volume->config.id()); + } for (const ModelInstance *model_instance : model_object->instances) check(model_instance->id()); } - for (const auto mm : model.materials) + for (const auto mm : model.materials) { check(mm.second->id()); + check(mm.second->config.id()); + } } void check_model_ids_equal(const Model &model1, const Model &model2) @@ -1875,10 +1896,13 @@ void check_model_ids_equal(const Model &model1, const Model &model2) const ModelObject &model_object1 = *model1.objects[idx_model]; const ModelObject &model_object2 = * model2.objects[idx_model]; assert(model_object1.id() == model_object2.id()); + assert(model_object1.config.id() == model_object2.config.id()); assert(model_object1.volumes.size() == model_object2.volumes.size()); assert(model_object1.instances.size() == model_object2.instances.size()); - for (size_t i = 0; i < model_object1.volumes.size(); ++ i) + for (size_t i = 0; i < model_object1.volumes.size(); ++ i) { assert(model_object1.volumes[i]->id() == model_object2.volumes[i]->id()); + assert(model_object1.volumes[i]->config.id() == model_object2.volumes[i]->config.id()); + } for (size_t i = 0; i < model_object1.instances.size(); ++ i) assert(model_object1.instances[i]->id() == model_object2.instances[i]->id()); } @@ -1889,6 +1913,7 @@ void check_model_ids_equal(const Model &model1, const Model &model2) for (; it1 != model1.materials.end(); ++ it1, ++ it2) { assert(it1->first == it2->first); // compare keys assert(it1->second->id() == it2->second->id()); + assert(it1->second->config.id() == it2->second->config.id()); } } } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 8e9f7ecaa..5fd958f86 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -31,6 +31,34 @@ namespace UndoRedo { class StackImpl; } +class ModelConfig : public ObjectBase, public DynamicPrintConfig +{ +private: + friend class cereal::access; + friend class UndoRedo::StackImpl; + friend class ModelObject; + friend class ModelVolume; + friend class ModelMaterial; + + // Constructors to be only called by derived classes. + // Default constructor to assign a unique ID. + explicit ModelConfig() {} + // Constructor with ignored int parameter to assign an invalid ID, to be replaced + // by an existing ID copied from elsewhere. + explicit ModelConfig(int) : ObjectBase(-1) {} + // Copy constructor copies the ID. + explicit ModelConfig(const ModelConfig &cfg) : ObjectBase(-1), DynamicPrintConfig(cfg) { this->copy_id(cfg); } + // Move constructor copies the ID. + explicit ModelConfig(ModelConfig &&cfg) : ObjectBase(-1), DynamicPrintConfig(std::move(cfg)) { this->copy_id(cfg); } + + ModelConfig& operator=(const ModelConfig &rhs) = default; + ModelConfig& operator=(ModelConfig &&rhs) = default; + + template void serialize(Archive &ar) { + ar(cereal::base_class(this)); + } +}; + typedef std::string t_model_material_id; typedef std::string t_model_material_attribute; typedef std::map t_model_material_attributes; @@ -43,10 +71,10 @@ typedef std::vector ModelInstancePtrs; #define OBJECTBASE_DERIVED_COPY_MOVE_CLONE(TYPE) \ /* Copy a model, copy the IDs. The Print::apply() will call the TYPE::copy() method */ \ /* to make a private copy for background processing. */ \ - static TYPE* new_copy(const TYPE &rhs) { return new TYPE(rhs); } \ - static TYPE* new_copy(TYPE &&rhs) { return new TYPE(std::move(rhs)); } \ - static TYPE make_copy(const TYPE &rhs) { return TYPE(rhs); } \ - static TYPE make_copy(TYPE &&rhs) { return TYPE(std::move(rhs)); } \ + static TYPE* new_copy(const TYPE &rhs) { auto *ret = new TYPE(rhs); assert(ret->id() == rhs.id()); return ret; } \ + static TYPE* new_copy(TYPE &&rhs) { auto *ret = new TYPE(std::move(rhs)); assert(ret->id() == rhs.id()); return ret; } \ + static TYPE make_copy(const TYPE &rhs) { TYPE ret(rhs); assert(ret.id() == rhs.id()); return ret; } \ + static TYPE make_copy(TYPE &&rhs) { TYPE ret(std::move(rhs)); assert(ret.id() == rhs.id()); return ret; } \ TYPE& assign_copy(const TYPE &rhs); \ TYPE& assign_copy(TYPE &&rhs); \ /* Copy a TYPE, generate new IDs. The front end will use this call. */ \ @@ -54,26 +82,24 @@ typedef std::vector ModelInstancePtrs; /* Default constructor assigning an invalid ID. */ \ auto obj = new TYPE(-1); \ obj->assign_clone(rhs); \ + assert(obj->id().valid() && obj->id() != rhs.id()); \ return obj; \ } \ TYPE make_clone(const TYPE &rhs) { \ /* Default constructor assigning an invalid ID. */ \ TYPE obj(-1); \ obj.assign_clone(rhs); \ + assert(obj.id().valid() && obj.id() != rhs.id()); \ return obj; \ } \ TYPE& assign_clone(const TYPE &rhs) { \ this->assign_copy(rhs); \ + assert(this->id().valid() && this->id() == rhs.id()); \ this->assign_new_unique_ids_recursive(); \ + assert(this->id().valid() && this->id() != rhs.id()); \ return *this; \ } -#define OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE(TYPE) \ -private: \ - /* Private constructor with an unused int parameter will create a TYPE instance with an invalid ID. */ \ - explicit TYPE(int) : ObjectBase(-1) {}; \ - void assign_new_unique_ids_recursive(); - // Material, which may be shared across multiple ModelObjects of a single Model. class ModelMaterial final : public ObjectBase { @@ -81,32 +107,40 @@ public: // Attributes are defined by the AMF file format, but they don't seem to be used by Slic3r for any purpose. t_model_material_attributes attributes; // Dynamic configuration storage for the object specific configuration values, overriding the global configuration. - DynamicPrintConfig config; + ModelConfig config; Model* get_model() const { return m_model; } void apply(const t_model_material_attributes &attributes) { this->attributes.insert(attributes.begin(), attributes.end()); } -protected: - friend class Model; - // Constructor, which assigns a new unique ID. - ModelMaterial(Model *model) : m_model(model) {} - // Copy constructor copies the ID and m_model! - ModelMaterial(const ModelMaterial &rhs) = default; - void set_model(Model *model) { m_model = model; } - private: // Parent, owning this material. Model *m_model; - + + // To be accessed by the Model. + friend class Model; + // Constructor, which assigns a new unique ID to the material and to its config. + ModelMaterial(Model *model) : m_model(model) { assert(this->id().valid()); } + // Copy constructor copies the IDs of the ModelMaterial and its config, and m_model! + ModelMaterial(const ModelMaterial &rhs) = default; + void set_model(Model *model) { m_model = model; } + void set_new_unique_id() { ObjectBase::set_new_unique_id(); this->config.set_new_unique_id(); } + + // To be accessed by the serialization and Undo/Redo code. + friend class cereal::access; + friend class UndoRedo::StackImpl; + // Create an object for deserialization, don't allocate IDs for ModelMaterial and its config. + ModelMaterial() : ObjectBase(-1), config(-1), m_model(nullptr) { assert(this->id().invalid()); assert(this->config.id().invalid()); } + template void serialize(Archive &ar) { + assert(this->id().invalid()); assert(this->config.id().invalid()); + ar(attributes, config); + // assert(this->id().valid()); assert(this->config.id().valid()); + } + + // Disabled methods. ModelMaterial(ModelMaterial &&rhs) = delete; ModelMaterial& operator=(const ModelMaterial &rhs) = delete; ModelMaterial& operator=(ModelMaterial &&rhs) = delete; - - friend class cereal::access; - friend class UndoRedo::StackImpl; - ModelMaterial() : m_model(nullptr) {} - template void serialize(Archive &ar) { ar(cereal::base_class(this)); ar(attributes, config); } }; // A printable object, possibly having multiple print volumes (each with its own set of parameters and materials), @@ -115,7 +149,6 @@ private: // different rotation and different uniform scaling. class ModelObject final : public ObjectBase { - friend class Model; public: std::string name; std::string input_file; // XXX: consider fs::path @@ -126,7 +159,7 @@ public: // ModelVolumes are owned by this ModelObject. ModelVolumePtrs volumes; // Configuration parameters specific to a single ModelObject, overriding the global Slic3r settings. - DynamicPrintConfig config; + ModelConfig config; // Variation of a layer thickness for spans of Z coordinates. t_layer_height_ranges layer_height_ranges; // Profile of increasing z to a layer height, to be linearly interpolated when calculating the layers. @@ -236,25 +269,53 @@ public: std::string get_export_filename() const; - // Get full stl statistics for all object's meshes + // Get full stl statistics for all object's meshes stl_stats get_object_stl_stats() const; - // Get count of errors in the mesh( or all object's meshes, if volume index isn't defined) + // Get count of errors in the mesh( or all object's meshes, if volume index isn't defined) int get_mesh_errors_count(const int vol_idx = -1) const; private: - ModelObject(Model *model) : m_model(model), origin_translation(Vec3d::Zero()), - m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {} - ~ModelObject(); + friend class Model; + // This constructor assigns new ID to this ModelObject and its config. + explicit ModelObject(Model *model) : m_model(model), origin_translation(Vec3d::Zero()), + m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) + { assert(this->id().valid()); } + explicit ModelObject(int) : ObjectBase(-1), config(-1), m_model(nullptr), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) + { assert(this->id().invalid()); assert(this->config.id().invalid()); } + ~ModelObject(); + void assign_new_unique_ids_recursive(); - /* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */ - /* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */ - ModelObject(const ModelObject &rhs) : ObjectBase(-1), m_model(rhs.m_model) { this->assign_copy(rhs); } - explicit ModelObject(ModelObject &&rhs) : ObjectBase(-1) { this->assign_copy(std::move(rhs)); } - ModelObject& operator=(const ModelObject &rhs) { this->assign_copy(rhs); m_model = rhs.m_model; return *this; } - ModelObject& operator=(ModelObject &&rhs) { this->assign_copy(std::move(rhs)); m_model = rhs.m_model; return *this; } + // To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" + // (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). + ModelObject(const ModelObject &rhs) : ObjectBase(-1), config(-1), m_model(rhs.m_model) { + assert(this->id().invalid()); assert(this->config.id().invalid()); assert(rhs.id() != rhs.config.id()); + this->assign_copy(rhs); + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); + assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id()); + } + explicit ModelObject(ModelObject &&rhs) : ObjectBase(-1), config(-1) { + assert(this->id().invalid()); assert(this->config.id().invalid()); assert(rhs.id() != rhs.config.id()); + this->assign_copy(std::move(rhs)); + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); + assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id()); + } + ModelObject& operator=(const ModelObject &rhs) { + this->assign_copy(rhs); + m_model = rhs.m_model; + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); + assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id()); + return *this; + } + ModelObject& operator=(ModelObject &&rhs) { + this->assign_copy(std::move(rhs)); + m_model = rhs.m_model; + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); + assert(this->id() == rhs.id()); assert(this->config.id() == rhs.config.id()); + return *this; + } + void set_new_unique_id() { ObjectBase::set_new_unique_id(); this->config.set_new_unique_id(); } OBJECTBASE_DERIVED_COPY_MOVE_CLONE(ModelObject) - OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE(ModelObject) // Parent object, owning this ModelObject. Set to nullptr here, so the macros above will have it initialized. Model *m_model = nullptr; @@ -275,8 +336,11 @@ private: // Undo / Redo through the cereal serialization library friend class cereal::access; friend class UndoRedo::StackImpl; - ModelObject() : m_model(nullptr), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) {} - template void serialize(Archive &ar) { + // Used for deserialization -> Don't allocate any IDs for the ModelObject or its config. + ModelObject() : ObjectBase(-1), config(-1), m_model(nullptr), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) { + assert(this->id().invalid()); assert(this->config.id().invalid()); + } + template void serialize(Archive &ar) { ar(cereal::base_class(this)); ar(name, input_file, instances, volumes, config, layer_height_ranges, layer_height_profile, sla_support_points, sla_points_status, origin_translation, m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid); @@ -307,7 +371,7 @@ public: void reset_mesh() { m_mesh = std::make_shared(); } // Configuration parameters specific to an object model geometry or a modifier volume, // overriding the global Slic3r settings and the ModelObject settings. - DynamicPrintConfig config; + ModelConfig config; // A parent object owning this modifier volume. ModelObject* get_object() const { return this->object; }; @@ -388,13 +452,14 @@ public: const Transform3d& get_matrix(bool dont_translate = false, bool dont_rotate = false, bool dont_scale = false, bool dont_mirror = false) const { return m_transformation.get_matrix(dont_translate, dont_rotate, dont_scale, dont_mirror); } - using ObjectBase::set_new_unique_id; + void set_new_unique_id() { ObjectBase::set_new_unique_id(); this->config.set_new_unique_id(); } protected: friend class Print; friend class SLAPrint; friend class ModelObject; + // Copies IDs of both the ModelVolume and its config. explicit ModelVolume(const ModelVolume &rhs) = default; void set_model_object(ModelObject *model_object) { object = model_object; } void transform_this_mesh(const Transform3d& t, bool fix_left_handed); @@ -420,33 +485,46 @@ private: ModelVolume(ModelObject *object, const TriangleMesh &mesh) : m_mesh(new TriangleMesh(mesh)), m_type(ModelVolumeType::MODEL_PART), object(object) { + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); if (mesh.stl.stats.number_of_facets > 1) calculate_convex_hull(); } ModelVolume(ModelObject *object, TriangleMesh &&mesh, TriangleMesh &&convex_hull) : - m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(ModelVolumeType::MODEL_PART), object(object) {} + m_mesh(new TriangleMesh(std::move(mesh))), m_convex_hull(new TriangleMesh(std::move(convex_hull))), m_type(ModelVolumeType::MODEL_PART), object(object) { + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); + } // Copying an existing volume, therefore this volume will get a copy of the ID assigned. ModelVolume(ModelObject *object, const ModelVolume &other) : - ObjectBase(other), // copy the ID + ObjectBase(other), name(other.name), m_mesh(other.m_mesh), m_convex_hull(other.m_convex_hull), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) { + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); + assert(this->id() == other.id() && this->config.id() == other.config.id()); this->set_material_id(other.material_id()); } // Providing a new mesh, therefore this volume will get a new unique ID assigned. ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) : name(other.name), m_mesh(new TriangleMesh(std::move(mesh))), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) { + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); + assert(this->id() == other.id() && this->config.id() == other.config.id()); this->set_material_id(other.material_id()); + this->config.set_new_unique_id(); if (mesh.stl.stats.number_of_facets > 1) calculate_convex_hull(); + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); + assert(this->id() != other.id() && this->config.id() != other.config.id()); } ModelVolume& operator=(ModelVolume &rhs) = delete; friend class cereal::access; friend class UndoRedo::StackImpl; - ModelVolume() : object(nullptr) {} + // Used for deserialization, therefore no IDs are allocated. + ModelVolume() : ObjectBase(-1), config(-1), object(nullptr) { + assert(this->id().invalid()); assert(this->config.id().invalid()); + } template void serialize(Archive &ar) { ar(name, config, m_mesh, m_type, m_material_id, m_convex_hull, m_transformation, m_is_splittable); } @@ -530,10 +608,10 @@ private: ModelObject* object; // Constructor, which assigns a new unique ID. - explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) {} + explicit ModelInstance(ModelObject *object) : object(object), print_volume_state(PVS_Inside) { assert(this->id().valid()); } // Constructor, which assigns a new unique ID. explicit ModelInstance(ModelObject *object, const ModelInstance &other) : - m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) {} + m_transformation(other.m_transformation), object(object), print_volume_state(PVS_Inside) { assert(this->id().valid() && this->id() != other.id()); } explicit ModelInstance(ModelInstance &&rhs) = delete; ModelInstance& operator=(const ModelInstance &rhs) = delete; @@ -541,9 +619,9 @@ private: friend class cereal::access; friend class UndoRedo::StackImpl; - ModelInstance() : object(nullptr) {} + // Used for deserialization, therefore no IDs are allocated. + ModelInstance() : ObjectBase(-1), object(nullptr) { assert(this->id().invalid()); } template void serialize(Archive &ar) { - ar(cereal::base_class(this)); ar(m_transformation, print_volume_state); } }; @@ -565,15 +643,15 @@ public: ModelObjectPtrs objects; // Default constructor assigns a new ID to the model. - Model() {} + Model() { assert(this->id().valid()); } ~Model() { this->clear_objects(); this->clear_materials(); } /* To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" */ /* (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). */ - Model(const Model &rhs) : ObjectBase(-1) { this->assign_copy(rhs); } - explicit Model(Model &&rhs) : ObjectBase(-1) { this->assign_copy(std::move(rhs)); } - Model& operator=(const Model &rhs) { this->assign_copy(rhs); return *this; } - Model& operator=(Model &&rhs) { this->assign_copy(std::move(rhs)); return *this; } + Model(const Model &rhs) : ObjectBase(-1) { assert(this->id().invalid()); this->assign_copy(rhs); assert(this->id().valid()); assert(this->id() == rhs.id()); } + explicit Model(Model &&rhs) : ObjectBase(-1) { assert(this->id().invalid()); this->assign_copy(std::move(rhs)); assert(this->id().valid()); assert(this->id() == rhs.id()); } + Model& operator=(const Model &rhs) { this->assign_copy(rhs); assert(this->id().valid()); assert(this->id() == rhs.id()); return *this; } + Model& operator=(Model &&rhs) { this->assign_copy(std::move(rhs)); assert(this->id().valid()); assert(this->id() == rhs.id()); return *this; } OBJECTBASE_DERIVED_COPY_MOVE_CLONE(Model) @@ -633,12 +711,13 @@ public: std::string propose_export_file_name_and_path(const std::string &new_extension) const; private: - OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE(Model) + explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); }; + void assign_new_unique_ids_recursive(); friend class cereal::access; friend class UndoRedo::StackImpl; template void serialize(Archive &ar) { - ar(cereal::base_class(this), materials, objects); + ar(materials, objects); } }; diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp index f00d6f61e..0988acf5a 100644 --- a/src/libslic3r/ObjectID.hpp +++ b/src/libslic3r/ObjectID.hpp @@ -33,6 +33,7 @@ public: bool operator>=(const ObjectID &rhs) const { return this->id >= rhs.id; } bool valid() const { return id != 0; } + bool invalid() const { return id == 0; } size_t id; @@ -72,6 +73,7 @@ protected: void assign_new_unique_ids_recursive() { this->set_new_unique_id(); } private: + friend class UndoRedo::StackImpl; ObjectID m_id; static inline ObjectID generate_new_id() { return ObjectID(++ s_last_id); } diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 6d8c6e2bb..e545b9b7b 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -612,7 +612,7 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, assert(mv_src.id() == mv_dst.id()); // Copy the ModelVolume data. mv_dst.name = mv_src.name; - mv_dst.config = mv_src.config; + static_cast(mv_dst.config) = static_cast(mv_src.config); //FIXME what to do with the materials? // mv_dst.m_material_id = mv_src.m_material_id; ++ i_src; @@ -899,7 +899,7 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co // Synchronize Object's config. bool object_config_changed = model_object.config != model_object_new.config; if (object_config_changed) - model_object.config = model_object_new.config; + static_cast(model_object.config) = static_cast(model_object_new.config); if (! object_diff.empty() || object_config_changed) { PrintObjectConfig new_config = PrintObject::object_config_from_model_object(m_default_object_config, model_object, num_extruders); auto range = print_object_status.equal_range(PrintObjectStatus(model_object.id())); diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index aa7cf58b5..47b259f64 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -368,7 +368,7 @@ SLAPrint::ApplyStatus SLAPrint::apply(const Model &model, const DynamicPrintConf // Synchronize Object's config. bool object_config_changed = model_object.config != model_object_new.config; if (object_config_changed) - model_object.config = model_object_new.config; + static_cast(model_object.config) = static_cast(model_object_new.config); if (! object_diff.empty() || object_config_changed) { SLAPrintObjectConfig new_config = m_default_object_config; normalize_and_apply_config(new_config, model_object.config); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 97168ee04..0784b70ff 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -1160,7 +1160,7 @@ void Selection::copy_to_clipboard() ModelObject* dst_object = m_clipboard.add_object(); dst_object->name = src_object->name; dst_object->input_file = src_object->input_file; - dst_object->config = src_object->config; + static_cast(dst_object->config) = static_cast(src_object->config); dst_object->sla_support_points = src_object->sla_support_points; dst_object->sla_points_status = src_object->sla_points_status; dst_object->layer_height_ranges = src_object->layer_height_ranges; diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 1e49c1336..74565a301 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -117,7 +117,9 @@ public: bool is_immutable() const override { return true; } void save(size_t active_snapshot_time, size_t current_time) { - assert(m_history.empty() || m_history.back().end() <= active_snapshot_time); + assert(m_history.empty() || m_history.back().end() <= active_snapshot_time || + // The snapshot of an immutable object may have already been taken from another mutable object. + (m_history.back().begin() <= active_snapshot_time && m_history.back().end() == current_time + 1)); if (m_history.empty() || m_history.back().end() < active_snapshot_time) m_history.emplace_back(active_snapshot_time, current_time + 1); else @@ -335,11 +337,11 @@ public: void load_model(const Slic3r::Model &model, size_t snapshot_time); void load_selection(const Slic3r::GUI::Selection &selection, size_t snapshot_time); - template ObjectID save_mutable_object(const T &object); + template ObjectID save_mutable_object(const T &object); template ObjectID save_immutable_object(std::shared_ptr &object); template T* load_mutable_object(const Slic3r::ObjectID id); template std::shared_ptr load_immutable_object(const Slic3r::ObjectID id); - template void load_mutable_object(const Slic3r::ObjectID id, T &target); + template void load_mutable_object(const Slic3r::ObjectID id, T &target); private: template ObjectID immutable_object_id(const std::shared_ptr &ptr) { @@ -379,6 +381,8 @@ class ModelObject; class ModelVolume; class ModelInstance; class ModelMaterial; +class ModelConfig; +class DynamicPrintConfig; class TriangleMesh; } // namespace Slic3r @@ -392,13 +396,14 @@ namespace cereal template struct specialize {}; template struct specialize {}; template struct specialize {}; + template struct specialize {}; template struct specialize, cereal::specialization::non_member_load_save> {}; // Store ObjectBase derived class onto the Undo / Redo stack as a separate object, // store just the ObjectID to this stream. template void save(BinaryOutputArchive& ar, T* const& ptr) { - ar(cereal::get_user_data(ar).save_mutable_object(*ptr)); + ar(cereal::get_user_data(ar).save_mutable_object(*ptr)); } // Load ObjectBase derived class from the Undo / Redo stack as a separate object @@ -411,6 +416,40 @@ namespace cereal ptr = stack.load_mutable_object(Slic3r::ObjectID(id)); } + // Store ObjectBase derived class onto the Undo / Redo stack as a separate object, + // store just the ObjectID to this stream. + template void save(BinaryOutputArchive &ar, const std::unique_ptr &ptr) + { + ar(cereal::get_user_data(ar).save_mutable_object(*ptr.get())); + } + + // Load ObjectBase derived class from the Undo / Redo stack as a separate object + // based on the ObjectID loaded from this stream. + template void load(BinaryInputArchive &ar, std::unique_ptr &ptr) + { + Slic3r::UndoRedo::StackImpl& stack = cereal::get_user_data(ar); + size_t id; + ar(id); + ptr.reset(stack.load_mutable_object(Slic3r::ObjectID(id))); + } + + // Store ObjectBase derived class onto the Undo / Redo stack as a separate object, + // store just the ObjectID to this stream. + void save(BinaryOutputArchive& ar, const Slic3r::ModelConfig &cfg) + { + ar(cereal::get_user_data(ar).save_mutable_object(cfg)); + } + + // Load ObjectBase derived class from the Undo / Redo stack as a separate object + // based on the ObjectID loaded from this stream. + void load(BinaryInputArchive& ar, Slic3r::ModelConfig &cfg) + { + Slic3r::UndoRedo::StackImpl& stack = cereal::get_user_data(ar); + size_t id; + ar(id); + stack.load_mutable_object(Slic3r::ObjectID(id), cfg); + } + // Store ObjectBase derived class onto the Undo / Redo stack as a separate object, // store just the ObjectID to this stream. template void save(BinaryOutputArchive &ar, const std::shared_ptr &ptr) @@ -458,7 +497,7 @@ namespace cereal namespace Slic3r { namespace UndoRedo { -template ObjectID StackImpl::save_mutable_object(const T &object) +template ObjectID StackImpl::save_mutable_object(const T &object) { // First find or allocate a history stack for the ObjectID of this object instance. auto it_object_history = m_objects.find(object.id()); @@ -469,7 +508,7 @@ template ObjectID StackImpl::save_mutable_object(const T &object) std::ostringstream oss; { Slic3r::UndoRedo::OutputArchive archive(*this, oss); - archive(object); + archive(static_cast(object)); } object_history->save(m_active_snapshot_time, m_current_time, oss.str()); return object.id(); @@ -491,7 +530,7 @@ template ObjectID StackImpl::save_immutable_object(std::shared_ptr T* StackImpl::load_mutable_object(const Slic3r::ObjectID id) { T *target = new T(); - this->load_mutable_object(id, *target); + this->load_mutable_object(id, *target); return target; } @@ -505,7 +544,7 @@ template std::shared_ptr StackImpl::load_immutable_object(c return object_history->shared_ptr(*this); } -template void StackImpl::load_mutable_object(const Slic3r::ObjectID id, T &target) +template void StackImpl::load_mutable_object(const Slic3r::ObjectID id, T &target) { // First find a history stack for the ObjectID of this object instance. auto it_object_history = m_objects.find(id); @@ -514,7 +553,8 @@ template void StackImpl::load_mutable_object(const Slic3r::ObjectID // Then get the data associated with the object history and m_active_snapshot_time. std::istringstream iss(object_history->load(m_active_snapshot_time)); Slic3r::UndoRedo::InputArchive archive(*this, iss); - archive(target); + target.m_id = id; + archive(static_cast(target)); } // The Undo / Redo stack is being initialized with an empty model and an empty selection. @@ -541,7 +581,7 @@ void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Mo m_snapshots.erase(it, m_snapshots.end()); } // Take new snapshots. - this->save_mutable_object(model); + this->save_mutable_object(model); // this->save_mutable_object(selection); // Save the snapshot info m_snapshots.emplace_back(snapshot_name, m_current_time ++, model.id().id); @@ -556,8 +596,10 @@ void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GU if (it_snapshot == m_snapshots.end() || it_snapshot->timestamp != timestamp) throw std::runtime_error((boost::format("Snapshot with timestamp %1% does not exist") % timestamp).str()); - this->load_mutable_object(ObjectID(it_snapshot->model_object_id), model); - this->load_mutable_object(selection.id(), selection); + model.clear_objects(); + model.clear_materials(); + this->load_mutable_object(ObjectID(it_snapshot->model_object_id), model); + this->load_mutable_object(selection.id(), selection); this->m_active_snapshot_time = timestamp; } From 5a2ace1a6e466c3501817cfa73d3b74606222395 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 4 Jul 2019 10:45:41 +0200 Subject: [PATCH 24/72] WIP Undo / Redo: First Undo in the history of PrusaSlicer! --- src/libslic3r/Model.cpp | 13 +++++++++++ src/libslic3r/Model.hpp | 3 +++ src/slic3r/GUI/GLCanvas3D.cpp | 21 ++++++++++++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 2 ++ src/slic3r/GUI/Plater.cpp | 38 ++++++++++++++++++++++++-------- src/slic3r/GUI/Plater.hpp | 2 ++ src/slic3r/Utils/UndoRedo.cpp | 41 ++++++++++++++++++++++++++++++----- src/slic3r/Utils/UndoRedo.hpp | 3 +++ 8 files changed, 108 insertions(+), 15 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 16f8fec4e..fbf10bf83 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -72,6 +72,19 @@ void Model::assign_new_unique_ids_recursive() model_object->assign_new_unique_ids_recursive(); } +void Model::update_links_bottom_up_recursive() +{ + for (std::pair &kvp : this->materials) + kvp.second->set_model(this); + for (ModelObject *model_object : this->objects) { + model_object->set_model(this); + for (ModelInstance *model_instance : model_object->instances) + model_instance->set_model_object(model_object); + for (ModelVolume *model_volume : model_object->volumes) + model_volume->set_model_object(model_object); + } +} + Model Model::read_from_file(const std::string &input_file, DynamicPrintConfig *config, bool add_default_instances) { Model model; diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 5fd958f86..398756fc9 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -457,6 +457,7 @@ public: protected: friend class Print; friend class SLAPrint; + friend class Model; friend class ModelObject; // Copies IDs of both the ModelVolume and its config. @@ -598,6 +599,7 @@ public: protected: friend class Print; friend class SLAPrint; + friend class Model; friend class ModelObject; explicit ModelInstance(const ModelInstance &rhs) = default; @@ -713,6 +715,7 @@ public: private: explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); }; void assign_new_unique_ids_recursive(); + void update_links_bottom_up_recursive(); friend class cereal::access; friend class UndoRedo::StackImpl; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 4116ac34b..511c423e6 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1191,6 +1191,8 @@ wxDEFINE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent); wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent); wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); +wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar) : m_canvas(canvas) @@ -2350,6 +2352,25 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) #endif /* __APPLE__ */ post_event(SimpleEvent(EVT_GLTOOLBAR_PASTE)); break; + + +#ifdef __APPLE__ + case 'y': + case 'Y': +#else /* __APPLE__ */ + case WXK_CONTROL_Y: +#endif /* __APPLE__ */ + post_event(SimpleEvent(EVT_GLCANVAS_REDO)); + break; +#ifdef __APPLE__ + case 'z': + case 'Z': +#else /* __APPLE__ */ + case WXK_CONTROL_Z: +#endif /* __APPLE__ */ + post_event(SimpleEvent(EVT_GLCANVAS_UNDO)); + break; + #ifdef __APPLE__ case WXK_BACK: // the low cost Apple solutions are not equipped with a Delete key, use Backspace instead. #else /* __APPLE__ */ diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index d39a910b3..d71817b34 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -126,6 +126,8 @@ wxDECLARE_EVENT(EVT_GLCANVAS_TAB, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_RESETGIZMOS, SimpleEvent); wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent); wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent); +wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent); class GLCanvas3D { diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f08480559..3e6f3763a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1552,6 +1552,8 @@ struct Plater::priv void take_snapshot(const std::string& snapshot_name) { this->undo_redo_stack.take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection()); } void take_snapshot(const wxString& snapshot_name) { this->take_snapshot(std::string(snapshot_name.ToUTF8().data())); } + void undo(); + void redo(); bool background_processing_enabled() const { return this->get_config("background_processing") == "1"; } void update_print_volume_state(); @@ -1745,6 +1747,8 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED, &priv::on_3dcanvas_mouse_dragging_finished, this); view3D_canvas->Bind(EVT_GLCANVAS_TAB, [this](SimpleEvent&) { select_next_view_3D(); }); view3D_canvas->Bind(EVT_GLCANVAS_RESETGIZMOS, [this](SimpleEvent&) { reset_all_gizmos(); }); + view3D_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); }); + view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); }); // 3DScene/Toolbar: view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this); @@ -1785,6 +1789,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) camera.set_type(get_config("use_perspective_camera")); this->undo_redo_stack.initialize(model, view3D->get_canvas3d()->get_selection()); + this->take_snapshot(_(L("New Project"))); } void Plater::priv::update(bool force_full_scene_refresh) @@ -3490,6 +3495,26 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const } } +void Plater::priv::undo() +{ + if (this->undo_redo_stack.undo(model, const_cast(view3D->get_canvas3d()->get_selection()))) { + this->update(false); + //YS_FIXME update obj_list from the deserialized model (maybe store ObjectIDs into the tree?) +// wxGetApp().obj_list()->update_selections(); +// selection_changed(); + } +} + +void Plater::priv::redo() +{ + if (this->undo_redo_stack.redo(model, const_cast(view3D->get_canvas3d()->get_selection()))) { + this->update(false); + //YS_FIXME update obj_list from the deserialized model (maybe store ObjectIDs into the tree?) +// wxGetApp().obj_list()->update_selections(); +// selection_changed(); + } +} + void Sidebar::set_btn_label(const ActionButtonType btn_type, const wxString& label) const { switch (btn_type) @@ -4001,15 +4026,10 @@ void Plater::send_gcode() } } -void Plater::take_snapshot(const std::string &snapshot_name) -{ - p->take_snapshot(snapshot_name); -} - -void Plater::take_snapshot(const wxString &snapshot_name) -{ - p->take_snapshot(snapshot_name); -} +void Plater::take_snapshot(const std::string &snapshot_name) { p->take_snapshot(snapshot_name); } +void Plater::take_snapshot(const wxString &snapshot_name) { p->take_snapshot(snapshot_name); } +void Plater::undo() { p->undo(); } +void Plater::redo() { p->redo(); } void Plater::on_extruders_change(int num_extruders) { diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 9a6bcda7b..91f218f6c 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -181,6 +181,8 @@ public: void take_snapshot(const std::string &snapshot_name); void take_snapshot(const wxString &snapshot_name); + void undo(); + void redo(); void on_extruders_change(int extruders_count); void on_config_change(const DynamicPrintConfig &config); diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 74565a301..978bf149f 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -130,7 +130,7 @@ public: if (m_history.empty()) return false; auto it = std::lower_bound(m_history.begin(), m_history.end(), Interval(timestamp, timestamp)); - if (it == m_history.end() || it->begin() >= timestamp) { + if (it == m_history.end() || it->begin() > timestamp) { if (it == m_history.begin()) return false; -- it; @@ -265,7 +265,7 @@ public: std::string load(size_t timestamp) const { assert(! m_history.empty()); auto it = std::lower_bound(m_history.begin(), m_history.end(), MutableHistoryInterval(timestamp, timestamp)); - if (it == m_history.end() || it->begin() >= timestamp) { + if (it == m_history.end() || it->begin() > timestamp) { assert(it != m_history.begin()); -- it; } @@ -328,6 +328,9 @@ public: void take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection); void load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GUI::Selection &selection); + bool undo(Slic3r::Model &model, Slic3r::GUI::Selection &selection); + bool redo(Slic3r::Model &model, Slic3r::GUI::Selection &selection); + // Snapshot history (names with timestamps). const std::vector& snapshots() const { return m_snapshots; } @@ -566,7 +569,7 @@ void StackImpl::initialize(const Slic3r::Model &model, const Slic3r::GUI::Select // The initial time interval will be <0, 1) m_active_snapshot_time = SIZE_MAX; // let it overflow to zero in take_snapshot m_current_time = 0; - this->take_snapshot("New Project", model, selection); + this->take_snapshot("Internal - Initialized", model, selection); } // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time. @@ -584,7 +587,8 @@ void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Mo this->save_mutable_object(model); // this->save_mutable_object(selection); // Save the snapshot info - m_snapshots.emplace_back(snapshot_name, m_current_time ++, model.id().id); + m_active_snapshot_time = m_current_time ++; + m_snapshots.emplace_back(snapshot_name, m_active_snapshot_time, model.id().id); // Release empty objects from the history. this->collect_garbage(); } @@ -593,16 +597,38 @@ void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GU { // Find the snapshot by time. It must exist. const auto it_snapshot = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(timestamp)); - if (it_snapshot == m_snapshots.end() || it_snapshot->timestamp != timestamp) + if (it_snapshot == m_snapshots.begin() || it_snapshot == m_snapshots.end() || it_snapshot->timestamp != timestamp) throw std::runtime_error((boost::format("Snapshot with timestamp %1% does not exist") % timestamp).str()); + m_active_snapshot_time = timestamp; model.clear_objects(); model.clear_materials(); this->load_mutable_object(ObjectID(it_snapshot->model_object_id), model); - this->load_mutable_object(selection.id(), selection); + model.update_links_bottom_up_recursive(); +// this->load_mutable_object(selection.id(), selection); this->m_active_snapshot_time = timestamp; } +bool StackImpl::undo(Slic3r::Model &model, Slic3r::GUI::Selection &selection) +{ + auto it_current = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); + assert(it_current != m_snapshots.end() && it_current != m_snapshots.begin() && it_current->timestamp == m_active_snapshot_time); + if (-- it_current == m_snapshots.begin()) + return false; + this->load_snapshot(it_current->timestamp, model, selection); + return true; +} + +bool StackImpl::redo(Slic3r::Model &model, Slic3r::GUI::Selection &selection) +{ + auto it_current = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); + assert(it_current != m_snapshots.end() && it_current != m_snapshots.begin() && it_current->timestamp == m_active_snapshot_time); + if (++ it_current == m_snapshots.end()) + return false; + this->load_snapshot(it_current->timestamp, model, selection); + return true; +} + void StackImpl::collect_garbage() { // Purge objects with empty histories. @@ -623,6 +649,9 @@ Stack::~Stack() {} void Stack::initialize(const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) { pimpl->initialize(model, selection); } void Stack::take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) { pimpl->take_snapshot(snapshot_name, model, selection); } void Stack::load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GUI::Selection &selection) { pimpl->load_snapshot(timestamp, model, selection); } +bool Stack::undo(Slic3r::Model &model, Slic3r::GUI::Selection &selection) { return pimpl->undo(model, selection); } +bool Stack::redo(Slic3r::Model &model, Slic3r::GUI::Selection &selection) { return pimpl->redo(model, selection); } + const std::vector& Stack::snapshots() const { return pimpl->snapshots(); } } // namespace UndoRedo diff --git a/src/slic3r/Utils/UndoRedo.hpp b/src/slic3r/Utils/UndoRedo.hpp index d452e777c..be024c282 100644 --- a/src/slic3r/Utils/UndoRedo.hpp +++ b/src/slic3r/Utils/UndoRedo.hpp @@ -44,6 +44,9 @@ public: void take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection); void load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GUI::Selection &selection); + bool undo(Slic3r::Model &model, Slic3r::GUI::Selection &selection); + bool redo(Slic3r::Model &model, Slic3r::GUI::Selection &selection); + // Snapshot history (names with timestamps). const std::vector& snapshots() const; From 1798e2a84c57a9ecde31ffbeebdf6da0758bcd94 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 4 Jul 2019 14:35:04 +0200 Subject: [PATCH 25/72] WIP Undo / Redo : serialization / deserialization of object selection. --- src/libslic3r/ObjectID.hpp | 4 +- src/slic3r/GUI/Plater.cpp | 25 +++++++----- src/slic3r/GUI/Selection.cpp | 18 +++++++++ src/slic3r/GUI/Selection.hpp | 6 ++- src/slic3r/Utils/UndoRedo.cpp | 71 +++++++++++++++-------------------- src/slic3r/Utils/UndoRedo.hpp | 19 ++++++++-- 6 files changed, 85 insertions(+), 58 deletions(-) diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp index 0988acf5a..c708e5687 100644 --- a/src/libslic3r/ObjectID.hpp +++ b/src/libslic3r/ObjectID.hpp @@ -24,6 +24,8 @@ class ObjectID { public: ObjectID(size_t id) : id(id) {} + // Default constructor constructs an invalid ObjectID. + ObjectID() : id(0) {} bool operator==(const ObjectID &rhs) const { return this->id == rhs.id; } bool operator!=(const ObjectID &rhs) const { return this->id != rhs.id; } @@ -38,8 +40,6 @@ public: size_t id; private: - ObjectID() {} - friend class cereal::access; template void serialize(Archive &ar) { ar(id); } }; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3e6f3763a..a492584e6 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1645,6 +1645,7 @@ private: void update_fff_scene(); void update_sla_scene(); + void update_after_undo_redo(); // path to project file stored with no extension wxString m_project_filename; @@ -3497,22 +3498,26 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const void Plater::priv::undo() { - if (this->undo_redo_stack.undo(model, const_cast(view3D->get_canvas3d()->get_selection()))) { - this->update(false); - //YS_FIXME update obj_list from the deserialized model (maybe store ObjectIDs into the tree?) -// wxGetApp().obj_list()->update_selections(); -// selection_changed(); - } + if (this->undo_redo_stack.undo(model)) + this->update_after_undo_redo(); } void Plater::priv::redo() { - if (this->undo_redo_stack.redo(model, const_cast(view3D->get_canvas3d()->get_selection()))) { - this->update(false); - //YS_FIXME update obj_list from the deserialized model (maybe store ObjectIDs into the tree?) + if (this->undo_redo_stack.redo(model)) + this->update_after_undo_redo(); +} + +void Plater::priv::update_after_undo_redo() +{ + this->view3D->get_canvas3d()->get_selection().clear(); + this->update(false); // update volumes from the deserializd model + //YS_FIXME update obj_list from the deserialized model (maybe store ObjectIDs into the tree?) (no selections at this point of time) + this->view3D->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack.selection_deserialized().mode), this->undo_redo_stack.selection_deserialized().volumes_and_instances); // wxGetApp().obj_list()->update_selections(); // selection_changed(); - } + //FIXME what about the state of the manipulators? + //FIXME what about the focus? Cursor in the side panel? } void Sidebar::set_btn_label(const ActionButtonType btn_type, const wxString& label) const diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 0784b70ff..2986d97dd 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -311,6 +311,24 @@ void Selection::add_all() this->set_bounding_boxes_dirty(); } +void Selection::set_deserialized(EMode mode, const std::vector> &volumes_and_instances) +{ + if (! m_valid) + return; + + m_mode = mode; + for (unsigned int i : m_list) + (*m_volumes)[i]->selected = false; + m_list.clear(); + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++ i) { + const GLVolume::CompositeID &id = (*m_volumes)[i]->composite_id; + if (std::binary_search(volumes_and_instances.begin(), volumes_and_instances.end(), std::make_pair(id.volume_id, id.instance_id))) + this->do_add_volume(i); + } + update_type(); + this->set_bounding_boxes_dirty(); +} + void Selection::clear() { if (!m_valid) diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 17ae72356..8168e5e88 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -3,7 +3,6 @@ #include #include "libslic3r/Geometry.hpp" -#include "libslic3r/ObjectID.hpp" #include "3DScene.hpp" #if ENABLE_RENDER_SELECTION_CENTER @@ -67,7 +66,7 @@ private: Enum m_value; }; -class Selection : public Slic3r::ObjectBase +class Selection { public: typedef std::set IndicesList; @@ -238,6 +237,9 @@ public: void add_all(); + // To be called after Undo or Redo once the volumes are updated. + void set_deserialized(EMode mode, const std::vector> &volumes_and_instances); + // Update the selection based on the new instance IDs. void instances_changed(const std::vector &instance_ids_selected); // Update the selection based on the map from old indices to new indices after m_volumes changed. diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 978bf149f..34d83c445 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #define CEREAL_FUTURE_EXPERIMENTAL @@ -326,20 +327,17 @@ public: // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time. void take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection); - void load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GUI::Selection &selection); + void load_snapshot(size_t timestamp, Slic3r::Model &model); - bool undo(Slic3r::Model &model, Slic3r::GUI::Selection &selection); - bool redo(Slic3r::Model &model, Slic3r::GUI::Selection &selection); + bool undo(Slic3r::Model &model); + bool redo(Slic3r::Model &model); // Snapshot history (names with timestamps). const std::vector& snapshots() const { return m_snapshots; } -//protected: - void save_model(const Slic3r::Model &model, size_t snapshot_time); - void save_selection(const Slic3r::Model& model, const Slic3r::GUI::Selection &selection, size_t snapshot_time); - void load_model(const Slic3r::Model &model, size_t snapshot_time); - void load_selection(const Slic3r::GUI::Selection &selection, size_t snapshot_time); + const Selection& selection_deserialized() const { return m_selection; } +//protected: template ObjectID save_mutable_object(const T &object); template ObjectID save_immutable_object(std::shared_ptr &object); template T* load_mutable_object(const Slic3r::ObjectID id); @@ -372,6 +370,8 @@ private: size_t m_active_snapshot_time; // Logical time counter. m_current_time is being incremented with each snapshot taken. size_t m_current_time; + // Last selection serialized or deserialized. + Selection m_selection; }; using InputArchive = cereal::UserDataAdapter; @@ -469,28 +469,6 @@ namespace cereal ar(id); ptr = stack.load_immutable_object(Slic3r::ObjectID(id)); } - -#if 0 - void save(BinaryOutputArchive &ar, const Slic3r::GUI::Selection &selection) - { - size_t num = selection.get_volume_idxs().size(); - ar(num); - for (unsigned int volume_idx : selection.get_volume_idxs()) { - const Slic3r::GLVolume::CompositeID &id = selection.get_volume(volume_idx)->composite_id; - ar(id.object_id, id.volume_id, id.instance_id); - } - } - - template void load(BinaryInputArchive &ar, Slic3r::GUI::Selection &selection) - { - size_t num; - ar(num); - for (size_t i = 0; i < num; ++ i) { - Slic3r::GLVolume::CompositeID id; - ar(id.object_id, id.volume_id, id.instance_id); - } - } -#endif } #include @@ -585,15 +563,22 @@ void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Mo } // Take new snapshots. this->save_mutable_object(model); -// this->save_mutable_object(selection); - // Save the snapshot info + m_selection.volumes_and_instances.clear(); + m_selection.volumes_and_instances.reserve(selection.get_volume_idxs().size()); + m_selection.mode = selection.get_mode(); + for (unsigned int volume_idx : selection.get_volume_idxs()) { + const Slic3r::GLVolume::CompositeID &id = selection.get_volume(volume_idx)->composite_id; + m_selection.volumes_and_instances.emplace_back(id.volume_id, id.instance_id); + } + this->save_mutable_object(m_selection); + // Save the snapshot info. m_active_snapshot_time = m_current_time ++; m_snapshots.emplace_back(snapshot_name, m_active_snapshot_time, model.id().id); // Release empty objects from the history. this->collect_garbage(); } -void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GUI::Selection &selection) +void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model) { // Find the snapshot by time. It must exist. const auto it_snapshot = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(timestamp)); @@ -605,27 +590,30 @@ void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GU model.clear_materials(); this->load_mutable_object(ObjectID(it_snapshot->model_object_id), model); model.update_links_bottom_up_recursive(); -// this->load_mutable_object(selection.id(), selection); + m_selection.volumes_and_instances.clear(); + this->load_mutable_object(m_selection.id(), m_selection); + // Sort the volumes so that we may use binary search. + std::sort(m_selection.volumes_and_instances.begin(), m_selection.volumes_and_instances.end()); this->m_active_snapshot_time = timestamp; } -bool StackImpl::undo(Slic3r::Model &model, Slic3r::GUI::Selection &selection) +bool StackImpl::undo(Slic3r::Model &model) { auto it_current = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); assert(it_current != m_snapshots.end() && it_current != m_snapshots.begin() && it_current->timestamp == m_active_snapshot_time); if (-- it_current == m_snapshots.begin()) return false; - this->load_snapshot(it_current->timestamp, model, selection); + this->load_snapshot(it_current->timestamp, model); return true; } -bool StackImpl::redo(Slic3r::Model &model, Slic3r::GUI::Selection &selection) +bool StackImpl::redo(Slic3r::Model &model) { auto it_current = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); assert(it_current != m_snapshots.end() && it_current != m_snapshots.begin() && it_current->timestamp == m_active_snapshot_time); if (++ it_current == m_snapshots.end()) return false; - this->load_snapshot(it_current->timestamp, model, selection); + this->load_snapshot(it_current->timestamp, model); return true; } @@ -648,9 +636,10 @@ Stack::Stack() : pimpl(new StackImpl()) {} Stack::~Stack() {} void Stack::initialize(const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) { pimpl->initialize(model, selection); } void Stack::take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) { pimpl->take_snapshot(snapshot_name, model, selection); } -void Stack::load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GUI::Selection &selection) { pimpl->load_snapshot(timestamp, model, selection); } -bool Stack::undo(Slic3r::Model &model, Slic3r::GUI::Selection &selection) { return pimpl->undo(model, selection); } -bool Stack::redo(Slic3r::Model &model, Slic3r::GUI::Selection &selection) { return pimpl->redo(model, selection); } +void Stack::load_snapshot(size_t timestamp, Slic3r::Model &model) { pimpl->load_snapshot(timestamp, model); } +bool Stack::undo(Slic3r::Model &model) { return pimpl->undo(model); } +bool Stack::redo(Slic3r::Model &model) { return pimpl->redo(model); } +const Selection& Stack::selection_deserialized() const { return pimpl->selection_deserialized(); } const std::vector& Stack::snapshots() const { return pimpl->snapshots(); } diff --git a/src/slic3r/Utils/UndoRedo.hpp b/src/slic3r/Utils/UndoRedo.hpp index be024c282..0c97a0307 100644 --- a/src/slic3r/Utils/UndoRedo.hpp +++ b/src/slic3r/Utils/UndoRedo.hpp @@ -4,6 +4,8 @@ #include #include +#include + namespace Slic3r { class Model; @@ -27,6 +29,13 @@ struct Snapshot bool operator==(const Snapshot &rhs) const { return this->timestamp == rhs.timestamp; } }; +// Excerpt of Slic3r::GUI::Selection for serialization onto the Undo / Redo stack. +struct Selection : public Slic3r::ObjectBase { + unsigned char mode; + std::vector> volumes_and_instances; + template void serialize(Archive &ar) { ar(mode, volumes_and_instances); } +}; + class StackImpl; class Stack @@ -42,14 +51,18 @@ public: // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time. void take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection); - void load_snapshot(size_t timestamp, Slic3r::Model &model, Slic3r::GUI::Selection &selection); + void load_snapshot(size_t timestamp, Slic3r::Model &model); - bool undo(Slic3r::Model &model, Slic3r::GUI::Selection &selection); - bool redo(Slic3r::Model &model, Slic3r::GUI::Selection &selection); + bool undo(Slic3r::Model &model); + bool redo(Slic3r::Model &model); // Snapshot history (names with timestamps). const std::vector& snapshots() const; + // After load_snapshot() / undo() / redo() the selection is deserialized into a list of ObjectIDs, which needs to be converted + // into the list of GLVolume pointers once the 3D scene is updated. + const Selection& selection_deserialized() const; + private: friend class StackImpl; std::unique_ptr pimpl; From e586475bc32cb8ef8b2569ea60a5aa0d35a9bc09 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 4 Jul 2019 17:14:15 +0200 Subject: [PATCH 26/72] WIP Undo / Redo: Optional debug print outs. --- src/slic3r/GUI/Plater.cpp | 2 +- src/slic3r/Utils/UndoRedo.cpp | 151 ++++++++++++++++++++++++++++++---- src/slic3r/Utils/UndoRedo.hpp | 9 +- 3 files changed, 140 insertions(+), 22 deletions(-) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a492584e6..4a743aab1 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3498,7 +3498,7 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const void Plater::priv::undo() { - if (this->undo_redo_stack.undo(model)) + if (this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection())) this->update_after_undo_redo(); } diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 34d83c445..4a9af3b43 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -1,7 +1,10 @@ #include "UndoRedo.hpp" #include +#include +#include #include +#include #include #include @@ -18,6 +21,10 @@ #include +#ifndef NDEBUG +// #define SLIC3R_UNDOREDO_DEBUG +#endif /* NDEBUG */ + namespace Slic3r { namespace UndoRedo { @@ -63,8 +70,13 @@ public: // Release all data after the given timestamp. For the ImmutableObjectHistory, the shared pointer is NOT released. virtual void relese_after_timestamp(size_t timestamp) = 0; +#ifdef SLIC3R_UNDOREDO_DEBUG + // Human readable debug information. + virtual std::string format() = 0; +#endif /* SLIC3R_UNDOREDO_DEBUG */ + #ifndef NDEBUG - virtual bool validate() = 0; + virtual bool valid() = 0; #endif /* NDEBUG */ }; @@ -79,7 +91,7 @@ public: // Release all data after the given timestamp. The shared pointer is NOT released. void relese_after_timestamp(size_t timestamp) override { assert(! m_history.empty()); - assert(this->validate()); + assert(this->valid()); // it points to an interval which either starts with timestamp, or follows the timestamp. auto it = std::lower_bound(m_history.begin(), m_history.end(), T(timestamp, timestamp)); if (it == m_history.end()) { @@ -90,7 +102,7 @@ public: it_prev->trim_end(timestamp); } m_history.erase(it, m_history.end()); - assert(this->validate()); + assert(this->valid()); } protected: @@ -155,8 +167,20 @@ public: return m_shared_object; } +#ifdef SLIC3R_UNDOREDO_DEBUG + std::string format() override { + std::string out = typeid(T).name(); + out += this->is_serialized() ? + std::string(" len:") + std::to_string(m_serialized.size()) : + std::string(" ptr:") + ptr_to_string(m_shared_object.get()); + for (const Interval &interval : m_history) + out += std::string(",<") + std::to_string(interval.begin()) + "," + std::to_string(interval.end()) + ")"; + return out; + } +#endif /* SLIC3R_UNDOREDO_DEBUG */ + #ifndef NDEBUG - bool validate() override; + bool valid() override; #endif /* NDEBUG */ private: @@ -225,6 +249,13 @@ private: MutableHistoryInterval& operator=(const MutableHistoryInterval &rhs); }; +static inline std::string ptr_to_string(const void* ptr) +{ + char buf[64]; + sprintf(buf, "%p", ptr); + return buf; +} + // Smaller objects (Model, ModelObject, ModelInstance, ModelVolume, DynamicPrintConfig) // are mutable and there is not tracking of the changes, therefore a snapshot needs to be // taken every time and compared to the previous data at the Undo / Redo stack. @@ -274,14 +305,28 @@ public: return std::string(it->data(), it->data() + it->size()); } +#ifdef SLIC3R_UNDOREDO_DEBUG + std::string format() override { + std::string out = typeid(T).name(); + bool first = true; + for (const MutableHistoryInterval &interval : m_history) { + if (! first) + out += ","; + out += std::string("ptr:") + ptr_to_string(interval.data()) + " len:" + std::to_string(interval.size()) + " <" + std::to_string(interval.begin()) + "," + std::to_string(interval.end()) + ")"; + first = false; + } + return out; + } +#endif /* SLIC3R_UNDOREDO_DEBUG */ + #ifndef NDEBUG - bool validate() override; + bool valid() override; #endif /* NDEBUG */ }; #ifndef NDEBUG template -bool ImmutableObjectHistory::validate() +bool ImmutableObjectHistory::valid() { // The immutable object content is captured either by a shared object, or by its serialization, but not both. assert(! m_shared_object == ! m_serialized.empty()); @@ -295,7 +340,7 @@ bool ImmutableObjectHistory::validate() #ifndef NDEBUG template -bool MutableObjectHistory::validate() +bool MutableObjectHistory::valid() { // Verify that the history intervals are sorted and do not overlap, and that the data reference counters are correct. if (! m_history.empty()) { @@ -329,7 +374,9 @@ public: void take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection); void load_snapshot(size_t timestamp, Slic3r::Model &model); - bool undo(Slic3r::Model &model); + bool has_undo_snapshot() const; + bool has_redo_snapshot() const; + bool undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection); bool redo(Slic3r::Model &model); // Snapshot history (names with timestamps). @@ -344,6 +391,41 @@ public: template std::shared_ptr load_immutable_object(const Slic3r::ObjectID id); template void load_mutable_object(const Slic3r::ObjectID id, T &target); +#ifdef SLIC3R_UNDOREDO_DEBUG + std::string format() const { + std::string out = "Objects\n"; + for (const std::pair> &kvp : m_objects) + out += std::string("ObjectID:") + std::to_string(kvp.first.id) + " " + kvp.second->format() + "\n"; + out += "Snapshots\n"; + for (const Snapshot &snapshot : m_snapshots) { + if (snapshot.timestamp == m_active_snapshot_time) + out += ">>> "; + out += std::string("Name:") + snapshot.name + ", timestamp: " + std::to_string(snapshot.timestamp) + ", Model ID:" + std::to_string(snapshot.model_id) + "\n"; + } + if (m_active_snapshot_time > m_snapshots.back().timestamp) + out += ">>>\n"; + out += "Current time: " + std::to_string(m_current_time) + "\n"; + return out; + } + void print() const { + std::cout << "Undo / Redo stack" << std::endl; + std::cout << this->format() << std::endl; + } +#endif /* SLIC3R_UNDOREDO_DEBUG */ + + +#ifndef NDEBUG + bool valid() const { + assert(! m_snapshots.empty()); + auto it = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); + assert(it == m_snapshots.end() || (it != m_snapshots.begin() && it->timestamp == m_active_snapshot_time)); + assert(it != m_snapshots.end() || m_active_snapshot_time > m_snapshots.back().timestamp); + for (auto it = m_objects.begin(); it != m_objects.end(); ++ it) + assert(it->second->valid()); + return true; + } +#endif /* NDEBUG */ + private: template ObjectID immutable_object_id(const std::shared_ptr &ptr) { return this->immutable_object_id_impl((const void*)ptr.get()); @@ -554,7 +636,11 @@ void StackImpl::initialize(const Slic3r::Model &model, const Slic3r::GUI::Select void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) { // Release old snapshot data. - ++ m_active_snapshot_time; + // The active snapshot may be above the last snapshot if there is no redo data available. + if (! m_snapshots.empty() && m_active_snapshot_time > m_snapshots.back().timestamp) + m_active_snapshot_time = m_snapshots.back().timestamp + 1; + else + ++ m_active_snapshot_time; for (auto &kvp : m_objects) kvp.second->relese_after_timestamp(m_active_snapshot_time); { @@ -572,10 +658,15 @@ void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Mo } this->save_mutable_object(m_selection); // Save the snapshot info. - m_active_snapshot_time = m_current_time ++; - m_snapshots.emplace_back(snapshot_name, m_active_snapshot_time, model.id().id); + m_snapshots.emplace_back(snapshot_name, m_current_time ++, model.id().id); + m_active_snapshot_time = m_current_time; // Release empty objects from the history. this->collect_garbage(); + assert(this->valid()); +#ifdef SLIC3R_UNDOREDO_DEBUG + std::cout << "After snapshot" << std::endl; + this->print(); +#endif /* SLIC3R_UNDOREDO_DEBUG */ } void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model) @@ -588,32 +679,54 @@ void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model) m_active_snapshot_time = timestamp; model.clear_objects(); model.clear_materials(); - this->load_mutable_object(ObjectID(it_snapshot->model_object_id), model); + this->load_mutable_object(ObjectID(it_snapshot->model_id), model); model.update_links_bottom_up_recursive(); m_selection.volumes_and_instances.clear(); this->load_mutable_object(m_selection.id(), m_selection); // Sort the volumes so that we may use binary search. std::sort(m_selection.volumes_and_instances.begin(), m_selection.volumes_and_instances.end()); this->m_active_snapshot_time = timestamp; + assert(this->valid()); } -bool StackImpl::undo(Slic3r::Model &model) +bool StackImpl::has_undo_snapshot() const { + assert(this->valid()); + auto it = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); + return -- it != m_snapshots.begin(); +} + +bool StackImpl::has_redo_snapshot() const +{ + auto it = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); + return it != m_snapshots.end() && ++ it != m_snapshots.end(); +} + +bool StackImpl::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection) +{ + assert(this->valid()); auto it_current = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); - assert(it_current != m_snapshots.end() && it_current != m_snapshots.begin() && it_current->timestamp == m_active_snapshot_time); if (-- it_current == m_snapshots.begin()) return false; this->load_snapshot(it_current->timestamp, model); +#ifdef SLIC3R_UNDOREDO_DEBUG + std::cout << "After undo" << std::endl; + this->print(); +#endif /* SLIC3R_UNDOREDO_DEBUG */ return true; } bool StackImpl::redo(Slic3r::Model &model) { + assert(this->valid()); auto it_current = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); - assert(it_current != m_snapshots.end() && it_current != m_snapshots.begin() && it_current->timestamp == m_active_snapshot_time); - if (++ it_current == m_snapshots.end()) + if (it_current == m_snapshots.end() || ++ it_current == m_snapshots.end()) return false; - this->load_snapshot(it_current->timestamp, model); + this->load_snapshot(it_current->timestamp, model); +#ifdef SLIC3R_UNDOREDO_DEBUG + std::cout << "After redo" << std::endl; + this->print(); +#endif /* SLIC3R_UNDOREDO_DEBUG */ return true; } @@ -637,7 +750,9 @@ Stack::~Stack() {} void Stack::initialize(const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) { pimpl->initialize(model, selection); } void Stack::take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) { pimpl->take_snapshot(snapshot_name, model, selection); } void Stack::load_snapshot(size_t timestamp, Slic3r::Model &model) { pimpl->load_snapshot(timestamp, model); } -bool Stack::undo(Slic3r::Model &model) { return pimpl->undo(model); } +bool Stack::has_undo_snapshot() const { return pimpl->has_undo_snapshot(); } +bool Stack::has_redo_snapshot() const { return pimpl->has_redo_snapshot(); } +bool Stack::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection) { return pimpl->undo(model, selection); } bool Stack::redo(Slic3r::Model &model) { return pimpl->redo(model); } const Selection& Stack::selection_deserialized() const { return pimpl->selection_deserialized(); } diff --git a/src/slic3r/Utils/UndoRedo.hpp b/src/slic3r/Utils/UndoRedo.hpp index 0c97a0307..e50f6ad63 100644 --- a/src/slic3r/Utils/UndoRedo.hpp +++ b/src/slic3r/Utils/UndoRedo.hpp @@ -19,11 +19,11 @@ namespace UndoRedo { struct Snapshot { Snapshot(size_t timestamp) : timestamp(timestamp) {} - Snapshot(const std::string &name, size_t timestamp, size_t model_object_id) : name(name), timestamp(timestamp), model_object_id(model_object_id) {} + Snapshot(const std::string &name, size_t timestamp, size_t model_id) : name(name), timestamp(timestamp), model_id(model_id) {} std::string name; size_t timestamp; - size_t model_object_id; + size_t model_id; bool operator< (const Snapshot &rhs) const { return this->timestamp < rhs.timestamp; } bool operator==(const Snapshot &rhs) const { return this->timestamp == rhs.timestamp; } @@ -53,7 +53,10 @@ public: void take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection); void load_snapshot(size_t timestamp, Slic3r::Model &model); - bool undo(Slic3r::Model &model); + bool has_undo_snapshot() const; + bool has_redo_snapshot() const; + // Undoing an action may need to take a snapshot of the current application state. + bool undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection); bool redo(Slic3r::Model &model); // Snapshot history (names with timestamps). From a29cc9e242285bc394fdd4e08a6911830cdd6631 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 4 Jul 2019 17:33:19 +0200 Subject: [PATCH 27/72] Update object list after undo/redo --- src/libslic3r/Model.hpp | 4 +-- src/slic3r/GUI/GUI_ObjectList.cpp | 42 ++++++++++++++++++++++++++++++- src/slic3r/GUI/GUI_ObjectList.hpp | 2 ++ src/slic3r/GUI/Plater.cpp | 40 ++++++++++++++++++++++++++--- src/slic3r/GUI/Selection.cpp | 8 +++--- src/slic3r/GUI/Selection.hpp | 2 +- src/slic3r/Utils/UndoRedo.cpp | 6 ++--- src/slic3r/Utils/UndoRedo.hpp | 4 +-- 8 files changed, 89 insertions(+), 19 deletions(-) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 398756fc9..0c4c2ed2b 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -508,8 +508,8 @@ private: ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) : name(other.name), m_mesh(new TriangleMesh(std::move(mesh))), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) { - assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); - assert(this->id() == other.id() && this->config.id() == other.config.id()); +// assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); +// assert(this->id() == other.id() && this->config.id() == other.config.id()); this->set_material_id(other.material_id()); this->config.set_new_unique_id(); if (mesh.stl.stats.number_of_facets > 1) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index d7d1a1af7..acb6d3a86 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -65,6 +65,11 @@ static int extruders_count() return wxGetApp().extruders_cnt(); } +static void take_snapshot(const wxString& snapshot_name) +{ + wxGetApp().plater()->take_snapshot(snapshot_name); +} + ObjectList::ObjectList(wxWindow* parent) : wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_MULTIPLE), m_parent(parent) @@ -580,7 +585,6 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol if (volumes.empty()) return; - ModelObject& model_object = *(*m_objects)[obj_idx]; const auto object_item = m_objects_model->GetItemById(obj_idx); wxDataViewItemArray items; @@ -816,6 +820,7 @@ void ObjectList::OnDrop(wxDataViewEvent &event) if (m_dragged_data.type() == itInstance) { + take_snapshot(_(L("Instances to Separated Objects"))); instances_to_separated_object(m_dragged_data.obj_idx(), m_dragged_data.inst_idxs()); m_dragged_data.clear(); return; @@ -833,6 +838,8 @@ void ObjectList::OnDrop(wxDataViewEvent &event) // if (to_volume_id > from_volume_id) to_volume_id--; // #endif // __WXGTK__ + take_snapshot(_(L("Remov Volume(s)"))); + auto& volumes = (*m_objects)[m_dragged_data.obj_idx()]->volumes; auto delta = to_volume_id < from_volume_id ? -1 : 1; int cnt = 0; @@ -1427,6 +1434,8 @@ void ObjectList::load_subobject(ModelVolumeType type) if (m_objects_model->GetItemType(item)&itInstance) item = m_objects_model->GetItemById(obj_idx); + take_snapshot(_(L("Load Part"))); + std::vector> volumes_info; load_part((*m_objects)[obj_idx], volumes_info, type); @@ -1502,6 +1511,8 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode if (instance_idx == -1) return; + take_snapshot(_(L("Add Generic Subobject"))); + // Selected object ModelObject &model_object = *(*m_objects)[obj_idx]; // Bounding box of the selected instance in world coordinate system including the translation, without modifiers. @@ -1616,6 +1627,8 @@ void ObjectList::del_instances_from_object(const int obj_idx) if (instances.size() <= 1) return; + take_snapshot(_(L("Delete All Instances from Object"))); + while ( instances.size()> 1) instances.pop_back(); @@ -1645,6 +1658,8 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con return false; } + take_snapshot(_(L("Delete Subobject"))); + object->delete_volume(idx); if (object->volumes.size() == 1) @@ -1661,6 +1676,8 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con Slic3r::GUI::show_error(nullptr, _(L("You can't delete the last intance from object."))); return false; } + + take_snapshot(_(L("Delete Instance"))); object->delete_instance(idx); } else @@ -1688,6 +1705,8 @@ void ObjectList::split() return; } + take_snapshot(_(L("Split to Parts"))); + wxBusyCursor wait; auto model_object = (*m_objects)[obj_idx]; @@ -1945,6 +1964,8 @@ void ObjectList::delete_from_model_and_list(const ItemType type, const int obj_i if ( !(type&(itObject|itVolume|itInstance)) ) return; + take_snapshot(_(L("Delete Selected Item"))); + if (type&itObject) { del_object(obj_idx); delete_object_from_list(obj_idx); @@ -2502,6 +2523,8 @@ void ObjectList::change_part_type() if (new_type == type || new_type == ModelVolumeType::INVALID) return; + take_snapshot(_(L("Paste from Clipboard"))); + const auto item = GetSelection(); volume->set_type(new_type); m_objects_model->SetVolumeType(item, new_type); @@ -2862,5 +2885,22 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) const wxGetApp().plater()->update(); } +void ObjectList::recreate_object_list() +{ + m_prevent_list_events = true; + m_prevent_canvas_selection_update = true; + + m_objects_model->DeleteAll(); + + size_t obj_idx = 0; + while (obj_idx < m_objects->size()) { + add_object_to_list(obj_idx); + ++obj_idx; + } + + m_prevent_canvas_selection_update = false; + m_prevent_list_events = false; +} + } //namespace GUI } //namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 166606e2e..3d312d9c9 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -300,6 +300,8 @@ public: void msw_rescale(); + void recreate_object_list(); + private: #ifdef __WXOSX__ // void OnChar(wxKeyEvent& event); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a492584e6..567be0d41 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1738,7 +1738,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); }); view3D_canvas->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [this](Event &evt) { if (evt.data == 1) this->q->increase_instances(); else if (this->can_decrease_instances()) this->q->decrease_instances(); }); - view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); }); + view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { + this->take_snapshot(_(L("Instance Moved"))); + update(); }); view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this); view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_ROTATED, &priv::on_wipetower_rotated, this); view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_ROTATED, [this](SimpleEvent&) { update(); }); @@ -2337,17 +2339,21 @@ void Plater::priv::object_list_changed() void Plater::priv::select_all() { +// this->take_snapshot(_(L("Select All"))); + view3D->select_all(); this->sidebar->obj_list()->update_selections(); } void Plater::priv::deselect_all() { +// this->take_snapshot(_(L("Deselect All"))); view3D->deselect_all(); } void Plater::priv::remove(size_t obj_idx) { + this->take_snapshot(_(L("Remove Object"))); // Prevent toolpaths preview from rendering while we modify the Print object preview->set_enabled(false); @@ -2364,6 +2370,7 @@ void Plater::priv::remove(size_t obj_idx) void Plater::priv::delete_object_from_model(size_t obj_idx) { +// this->take_snapshot(_(L("Delete Object"))); // ys_FIXME What is the difference with "Remove Object"? model.delete_object(obj_idx); update(); object_list_changed(); @@ -2398,17 +2405,20 @@ void Plater::priv::reset() void Plater::priv::mirror(Axis axis) { + this->take_snapshot(_(L("Mirror"))); view3D->mirror_selection(axis); } void Plater::priv::arrange() { + this->take_snapshot(_(L("Arrange"))); m_ui_jobs.start(Jobs::Arrange); } // This method will find an optimal orientation for the currently selected item // Very similar in nature to the arrange method above... void Plater::priv::sla_optimize_rotation() { + this->take_snapshot(_(L("Optimize Rotation"))); m_ui_jobs.start(Jobs::Rotoptimize); } @@ -2564,6 +2574,8 @@ void Plater::priv::split_object() Slic3r::GUI::warning_catcher(q, _(L("The selected object couldn't be split because it contains only one part."))); else { + this->take_snapshot(_(L("Split to Objects"))); + unsigned int counter = 1; for (ModelObject* m : new_objects) m->name = current_model_object->name + "_" + std::to_string(counter++); @@ -2835,6 +2847,9 @@ void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = { if (obj_idx < 0) return; + + this->take_snapshot(_(L("Fix Throught NetFabb"))); + fix_model_by_win10_sdk_gui(*model.objects[obj_idx], vol_idx); this->update(); this->object_list_changed(); @@ -3074,6 +3089,8 @@ void Plater::priv::on_action_layersediting(SimpleEvent&) void Plater::priv::on_object_select(SimpleEvent& evt) { +// this->take_snapshot(_(L("Object Selection"))); + wxGetApp().obj_list()->update_selections(); selection_changed(); } @@ -3135,6 +3152,8 @@ void Plater::priv::on_right_click(Vec2dEvent& evt) void Plater::priv::on_wipetower_moved(Vec3dEvent &evt) { + this->take_snapshot(_(L("Wipe Tower Moved"))); + DynamicPrintConfig cfg; cfg.opt("wipe_tower_x", true)->value = evt.data(0); cfg.opt("wipe_tower_y", true)->value = evt.data(1); @@ -3143,6 +3162,8 @@ void Plater::priv::on_wipetower_moved(Vec3dEvent &evt) void Plater::priv::on_wipetower_rotated(Vec3dEvent& evt) { + this->take_snapshot(_(L("Wipe Tower Rotated"))); + DynamicPrintConfig cfg; cfg.opt("wipe_tower_x", true)->value = evt.data(0); cfg.opt("wipe_tower_y", true)->value = evt.data(1); @@ -3514,7 +3535,9 @@ void Plater::priv::update_after_undo_redo() this->update(false); // update volumes from the deserializd model //YS_FIXME update obj_list from the deserialized model (maybe store ObjectIDs into the tree?) (no selections at this point of time) this->view3D->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack.selection_deserialized().mode), this->undo_redo_stack.selection_deserialized().volumes_and_instances); -// wxGetApp().obj_list()->update_selections(); + + wxGetApp().obj_list()->recreate_object_list(); + wxGetApp().obj_list()->update_selections(); // selection_changed(); //FIXME what about the state of the manipulators? //FIXME what about the focus? Cursor in the side panel? @@ -3580,6 +3603,8 @@ void Plater::add_model() if (input_files.empty()) return; + this->take_snapshot(_(L("Add object(s)"))); + std::vector input_paths; for (const auto &file : input_files) { input_paths.push_back(into_path(file)); @@ -3717,6 +3742,8 @@ void Plater::set_number_of_copies(/*size_t num*/) if (num < 0) return; + this->take_snapshot(wxString::Format(_(L("Set numbers of copies to %d")), num)); + int diff = (int)num - (int)model_object->instances.size(); if (diff > 0) increase_instances(diff); @@ -3745,6 +3772,8 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_uppe return; } + this->take_snapshot(_(L("Cut"))); + wxBusyCursor wait; const auto new_objects = object->cut(instance_idx, z, keep_upper, keep_lower, rotate_lower); @@ -4245,8 +4274,11 @@ void Plater::copy_selection_to_clipboard() void Plater::paste_from_clipboard() { - if (can_paste_from_clipboard()) - p->view3D->get_canvas3d()->get_selection().paste_from_clipboard(); + if (!can_paste_from_clipboard()) + return; + + this->take_snapshot(_(L("Paste From Clipboard"))); + p->view3D->get_canvas3d()->get_selection().paste_from_clipboard(); } void Plater::msw_rescale() diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 2986d97dd..ffb423758 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -311,7 +311,7 @@ void Selection::add_all() this->set_bounding_boxes_dirty(); } -void Selection::set_deserialized(EMode mode, const std::vector> &volumes_and_instances) +void Selection::set_deserialized(EMode mode, const std::vector> &volumes_and_instances) { if (! m_valid) return; @@ -320,11 +320,9 @@ void Selection::set_deserialized(EMode mode, const std::vectorselected = false; m_list.clear(); - for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++ i) { - const GLVolume::CompositeID &id = (*m_volumes)[i]->composite_id; - if (std::binary_search(volumes_and_instances.begin(), volumes_and_instances.end(), std::make_pair(id.volume_id, id.instance_id))) + for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++ i) + if (std::binary_search(volumes_and_instances.begin(), volumes_and_instances.end(), (*m_volumes)[i]->geometry_id)) this->do_add_volume(i); - } update_type(); this->set_bounding_boxes_dirty(); } diff --git a/src/slic3r/GUI/Selection.hpp b/src/slic3r/GUI/Selection.hpp index 8168e5e88..35336c2b3 100644 --- a/src/slic3r/GUI/Selection.hpp +++ b/src/slic3r/GUI/Selection.hpp @@ -238,7 +238,7 @@ public: void add_all(); // To be called after Undo or Redo once the volumes are updated. - void set_deserialized(EMode mode, const std::vector> &volumes_and_instances); + void set_deserialized(EMode mode, const std::vector> &volumes_and_instances); // Update the selection based on the new instance IDs. void instances_changed(const std::vector &instance_ids_selected); diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 34d83c445..8c0e06526 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -566,10 +566,8 @@ void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Mo m_selection.volumes_and_instances.clear(); m_selection.volumes_and_instances.reserve(selection.get_volume_idxs().size()); m_selection.mode = selection.get_mode(); - for (unsigned int volume_idx : selection.get_volume_idxs()) { - const Slic3r::GLVolume::CompositeID &id = selection.get_volume(volume_idx)->composite_id; - m_selection.volumes_and_instances.emplace_back(id.volume_id, id.instance_id); - } + for (unsigned int volume_idx : selection.get_volume_idxs()) + m_selection.volumes_and_instances.emplace_back(selection.get_volume(volume_idx)->geometry_id); this->save_mutable_object(m_selection); // Save the snapshot info. m_active_snapshot_time = m_current_time ++; diff --git a/src/slic3r/Utils/UndoRedo.hpp b/src/slic3r/Utils/UndoRedo.hpp index 0c97a0307..9e808c31f 100644 --- a/src/slic3r/Utils/UndoRedo.hpp +++ b/src/slic3r/Utils/UndoRedo.hpp @@ -31,8 +31,8 @@ struct Snapshot // Excerpt of Slic3r::GUI::Selection for serialization onto the Undo / Redo stack. struct Selection : public Slic3r::ObjectBase { - unsigned char mode; - std::vector> volumes_and_instances; + unsigned char mode; + std::vector> volumes_and_instances; template void serialize(Archive &ar) { ar(mode, volumes_and_instances); } }; From 70c6558a4c89e00ff806c602f32dc5f0cb2c58cd Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 4 Jul 2019 19:48:00 +0200 Subject: [PATCH 28/72] Fix of compilation on Linux --- src/libslic3r/Geometry.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index f1987734f..bd248d1b6 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -270,17 +270,17 @@ public: private: template void load(Archive& archive, Slic3r::Geometry::Transformation &t) { - archive.loadBinary((char*)m.data(), sizeof(float) * 4); + archive.loadBinary((char*)t.data(), sizeof(float) * 4); } template void save(Archive& archive, const Slic3r::Geometry::Transformation &t) const { - archive.saveBinary((char*)m.data(), sizeof(float) * 4); + archive.saveBinary((char*)t.data(), sizeof(float) * 4); } private: friend class cereal::access; template void serialize(Archive & ar) { ar(m_offset, m_rotation, m_scaling_factor, m_mirror); } explicit Transformation(int) : m_dirty(true) {} - template static void load_and_construct(Archive & ar, cereal::construct &construct) + template static void load_and_construct(Archive &ar, cereal::construct &construct) { // Calling a private constructor with special "int" parameter to indicate that no construction is necessary. construct(1); From 3e5f9b5a224a921297a5de0c7407e4e7862ec98b Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 4 Jul 2019 19:59:45 +0200 Subject: [PATCH 29/72] Removed some junk templates, which pass compilation on Windows even if they are invalid. --- src/libslic3r/Geometry.hpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index bd248d1b6..d8eefbed0 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -268,14 +268,6 @@ public: // Bounding box is expected to be centered around zero in all axes. static Transformation volume_to_bed_transformation(const Transformation& instance_transformation, const BoundingBoxf3& bbox); -private: - template void load(Archive& archive, Slic3r::Geometry::Transformation &t) { - archive.loadBinary((char*)t.data(), sizeof(float) * 4); - } - template void save(Archive& archive, const Slic3r::Geometry::Transformation &t) const { - archive.saveBinary((char*)t.data(), sizeof(float) * 4); - } - private: friend class cereal::access; template void serialize(Archive & ar) { ar(m_offset, m_rotation, m_scaling_factor, m_mirror); } From b5b7463dc5290b963cfe4b14d536eb4d77e020dc Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 4 Jul 2019 20:14:38 +0200 Subject: [PATCH 30/72] Testing code for serialization of DynamicPrintConfig --- src/slic3r/Utils/UndoRedo.cpp | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 4a9af3b43..528c1c135 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -760,3 +760,43 @@ const std::vector& Stack::snapshots() const { return pimpl->snapshots( } // namespace UndoRedo } // namespace Slic3r + + +//FIXME we should have unit tests for testing serialization of basic types as DynamicPrintConfig. +#if 0 +#include "libslic3r/Config.hpp" +#include "libslic3r/PrintConfig.hpp" +namespace Slic3r { + bool test_dynamic_print_config_serialization() { + FullPrintConfig full_print_config; + DynamicPrintConfig cfg; + cfg.apply(full_print_config, false); + + std::string serialized; + try { + std::ostringstream ss; + cereal::BinaryOutputArchive oarchive(ss); + oarchive(cfg); + serialized = ss.str(); + } catch (std::runtime_error e) { + e.what(); + } + + DynamicPrintConfig cfg2; + try { + std::stringstream ss(serialized); + cereal::BinaryInputArchive iarchive(ss); + iarchive(cfg2); + } catch (std::runtime_error e) { + e.what(); + } + + if (cfg == cfg2) { + printf("Yes!\n"); + return true; + } + printf("No!\n"); + return false; + } +} // namespace Slic3r +#endif From 3a24fb2f4725d79b181fcf5d80646711be5c888c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 4 Jul 2019 20:25:52 +0200 Subject: [PATCH 31/72] Yet another compilation fix. --- src/libslic3r/Geometry.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index d8eefbed0..585dc4b0b 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -276,7 +276,7 @@ private: { // Calling a private constructor with special "int" parameter to indicate that no construction is necessary. construct(1); - ar(construct.ptr()->m_offset, construct.ptr()->m_rotation, construct.ptr()->m_scaling_factor, m_mirror); + ar(construct.ptr()->m_offset, construct.ptr()->m_rotation, construct.ptr()->m_scaling_factor, construct.ptr()->m_mirror); } }; From b1420283b6a548b3a64a7d4129e37651f655562f Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 4 Jul 2019 20:49:46 +0200 Subject: [PATCH 32/72] Fixed merge issues. --- src/libslic3r/Format/3mf.cpp | 2 +- src/libslic3r/Format/AMF.cpp | 6 ++---- src/libslic3r/Model.cpp | 4 ++-- src/libslic3r/Model.hpp | 2 +- src/libslic3r/Print.cpp | 8 ++++---- src/slic3r/GUI/GUI_ObjectList.cpp | 8 ++++++++ 6 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index e9f068974..21c680d2d 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -2134,7 +2134,7 @@ namespace Slic3r { const DynamicPrintConfig& config = range.second; for (const std::string& opt_key : config.keys()) { - pt::ptree& opt_tree = range_tree.add("option", config.serialize(opt_key)); + pt::ptree& opt_tree = range_tree.add("option", config.opt_serialize(opt_key)); opt_tree.put(".opt_key", opt_key); } } diff --git a/src/libslic3r/Format/AMF.cpp b/src/libslic3r/Format/AMF.cpp index 074b7e933..e964d3b9d 100644 --- a/src/libslic3r/Format/AMF.cpp +++ b/src/libslic3r/Format/AMF.cpp @@ -933,10 +933,8 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) stream << ";" << layer_height_profile[i]; stream << "\n \n"; } - //FIXME Store the layer height ranges (ModelObject::layer_height_ranges) - - // #ys_FIXME_experiment : Try to export layer config range + // Export layer height ranges including the layer range specific config overrides. const t_layer_config_ranges& config_ranges = object->layer_config_ranges; if (!config_ranges.empty()) { @@ -950,7 +948,7 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) stream << range.first.first << ";" << range.first.second << "\n"; for (const std::string& key : range.second.keys()) - stream << " " << range.second.serialize(key) << "\n"; + stream << " " << range.second.opt_serialize(key) << "\n"; stream << " \n"; layer_counter++; diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 0c29aa721..949f82c0a 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -1579,9 +1579,9 @@ void ModelVolume::center_geometry_after_creation() if (!shift.isApprox(Vec3d::Zero())) { if (m_mesh) - m_mesh->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); + const_cast(m_mesh.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); if (m_convex_hull) - m_convex_hull->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); + const_cast(m_convex_hull.get())->translate(-(float)shift(0), -(float)shift(1), -(float)shift(2)); translate(shift); } } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 0a50315ce..f8ebe3134 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -342,7 +342,7 @@ private: } template void serialize(Archive &ar) { ar(cereal::base_class(this)); - ar(name, input_file, instances, volumes, config, layer_height_ranges, layer_height_profile, sla_support_points, sla_points_status, origin_translation, + ar(name, input_file, instances, volumes, config, layer_config_ranges, layer_height_profile, sla_support_points, sla_points_status, origin_translation, m_bounding_box, m_bounding_box_valid, m_raw_bounding_box, m_raw_bounding_box_valid, m_raw_mesh_bounding_box, m_raw_mesh_bounding_box_valid); } }; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 122034c53..f34a66760 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -587,10 +587,10 @@ Print::ApplyStatus Print::apply(const Model &model, const DynamicPrintConfig &co Moved, Deleted, }; - ModelObjectStatus(ModelID id, Status status = Unknown) : id(id), status(status) {} - ModelID id; - Status status; - LayerRanges layer_ranges; + ModelObjectStatus(ObjectID id, Status status = Unknown) : id(id), status(status) {} + ObjectID id; + Status status; + LayerRanges layer_ranges; // Search by id. bool operator<(const ModelObjectStatus &rhs) const { return id < rhs.id; } }; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index d47c84c37..4ef1cb0d2 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -3400,5 +3400,13 @@ void ObjectList::recreate_object_list() m_prevent_list_events = false; } +ModelObject* ObjectList::object(const int obj_idx) const +{ + if (obj_idx < 0) + return nullptr; + + return (*m_objects)[obj_idx]; +} + } //namespace GUI } //namespace Slic3r \ No newline at end of file From 497b01f24a50b7f61210722acd546ee18bc72afa Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 4 Jul 2019 21:02:08 +0200 Subject: [PATCH 33/72] Trying to fix some template resolution on Linux --- src/libslic3r/Config.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index c5754fb83..aea3f215c 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -674,7 +674,7 @@ ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key, bool cre // Let the parent decide what to do if the opt_key is not defined by this->def(). return nullptr; ConfigOption *opt = optdef->create_default_option(); - this->options.insert(it, std::make_pair(opt_key, opt)); + this->options.emplace_hint(it, opt_key, std::unique_ptr(opt)); return opt; } From 3d420db531afc88a41cbcfff1b0902c15108e9d8 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 4 Jul 2019 21:28:57 +0200 Subject: [PATCH 34/72] Fix of perl bindings --- xs/src/xsinit.h | 1 + 1 file changed, 1 insertion(+) diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index f14e1262d..8745abdc7 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -87,6 +87,7 @@ extern "C" { #endif /* _MSC_VER */ #undef Zero #undef Packet +#undef ST #undef _ } #endif From 9fd0c55eb86110182b4cf53ac93e3fb802cbd35b Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 4 Jul 2019 22:09:14 +0200 Subject: [PATCH 35/72] Simplified the "cereal" includes to not clash with Perl includes --- src/libslic3r/BoundingBox.hpp | 3 --- src/libslic3r/Config.cpp | 1 + src/libslic3r/Config.hpp | 7 ++----- src/libslic3r/Geometry.hpp | 3 +-- src/libslic3r/ObjectID.hpp | 6 +----- src/libslic3r/Point.hpp | 3 --- src/libslic3r/PrintConfig.cpp | 1 + src/libslic3r/pchheader.hpp | 8 ++------ xs/src/xsinit.h | 1 - 9 files changed, 8 insertions(+), 25 deletions(-) diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp index b12ed5551..b1ebdcfbc 100644 --- a/src/libslic3r/BoundingBox.hpp +++ b/src/libslic3r/BoundingBox.hpp @@ -161,9 +161,6 @@ inline bool empty(const BoundingBox3Base &bb) } // namespace Slic3r -#include -#include - // Serialization through the Cereal library namespace cereal { template void serialize(Archive& archive, Slic3r::BoundingBox &bb) { archive(bb.min, bb.max, bb.defined); } diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index aea3f215c..9d0649a1f 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -822,6 +822,7 @@ t_config_option_keys StaticConfig::keys() const } +#include CEREAL_REGISTER_TYPE(Slic3r::ConfigOption) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle) CEREAL_REGISTER_TYPE(Slic3r::ConfigOptionSingle) diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 1511c4b28..844287efb 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -18,11 +18,8 @@ #include #include -#include -#include -#include -#include -#include +#include +#include namespace Slic3r { diff --git a/src/libslic3r/Geometry.hpp b/src/libslic3r/Geometry.hpp index 585dc4b0b..dca1872d8 100644 --- a/src/libslic3r/Geometry.hpp +++ b/src/libslic3r/Geometry.hpp @@ -8,8 +8,7 @@ #include "Polyline.hpp" // Serialization through the Cereal library -#include -#include +#include #include "boost/polygon/voronoi.hpp" using boost::polygon::voronoi_builder; diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp index c708e5687..fe0bf465f 100644 --- a/src/libslic3r/ObjectID.hpp +++ b/src/libslic3r/ObjectID.hpp @@ -1,11 +1,7 @@ #ifndef slic3r_ObjectID_hpp_ #define slic3r_ObjectID_hpp_ -#include -#include -#include -#include -#include +#include namespace Slic3r { diff --git a/src/libslic3r/Point.hpp b/src/libslic3r/Point.hpp index 8979523b7..994f45e59 100644 --- a/src/libslic3r/Point.hpp +++ b/src/libslic3r/Point.hpp @@ -291,9 +291,6 @@ namespace boost { namespace polygon { } } // end Boost -#include -#include - // Serialization through the Cereal library namespace cereal { // template void serialize(Archive& archive, Slic3r::Vec2crd &v) { archive(v.x(), v.y()); } diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 97787fff6..638ce38f1 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -3198,5 +3198,6 @@ void DynamicPrintAndCLIConfig::handle_legacy(t_config_option_key &opt_key, std:: } +#include CEREAL_REGISTER_TYPE(Slic3r::DynamicPrintConfig) CEREAL_REGISTER_POLYMORPHIC_RELATION(Slic3r::DynamicConfig, Slic3r::DynamicPrintConfig) diff --git a/src/libslic3r/pchheader.hpp b/src/libslic3r/pchheader.hpp index 54b563def..c0ffe2108 100644 --- a/src/libslic3r/pchheader.hpp +++ b/src/libslic3r/pchheader.hpp @@ -102,12 +102,8 @@ #include #include -#include -#include -#include -#include -#include -#include +#include +#include #include "BoundingBox.hpp" #include "ClipperUtils.hpp" diff --git a/xs/src/xsinit.h b/xs/src/xsinit.h index 8745abdc7..f14e1262d 100644 --- a/xs/src/xsinit.h +++ b/xs/src/xsinit.h @@ -87,7 +87,6 @@ extern "C" { #endif /* _MSC_VER */ #undef Zero #undef Packet -#undef ST #undef _ } #endif From 211d1ee1e32f8eb2d68173323208a9dd660689bc Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 4 Jul 2019 22:52:33 +0200 Subject: [PATCH 36/72] Trying to make all C++ of the platforms happy. --- src/CMakeLists.txt | 2 +- src/libslic3r/ObjectID.hpp | 3 +-- src/slic3r/Utils/UndoRedo.cpp | 29 ++++++++++++++++------------- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 61faa0571..3ee46289a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -75,7 +75,7 @@ if (NOT MSVC) set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer") endif () -target_link_libraries(PrusaSlicer libslic3r) +target_link_libraries(PrusaSlicer libslic3r cereal) if (APPLE) # add_compile_options(-stdlib=libc++) # add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE) diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp index fe0bf465f..df16f9202 100644 --- a/src/libslic3r/ObjectID.hpp +++ b/src/libslic3r/ObjectID.hpp @@ -69,7 +69,6 @@ protected: void assign_new_unique_ids_recursive() { this->set_new_unique_id(); } private: - friend class UndoRedo::StackImpl; ObjectID m_id; static inline ObjectID generate_new_id() { return ObjectID(++ s_last_id); } @@ -77,9 +76,9 @@ private: friend ObjectID wipe_tower_object_id(); friend ObjectID wipe_tower_instance_id(); - friend class Slic3r::UndoRedo::StackImpl; friend class cereal::access; + friend class Slic3r::UndoRedo::StackImpl; template void serialize(Archive &ar) { ar(m_id); } ObjectBase(const ObjectID id) : m_id(id) {} template static void load_and_construct(Archive & ar, cereal::construct &construct) { ObjectID id; ar(id); construct(id); } diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 11fceee0a..d5da0c618 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -153,19 +153,7 @@ public: bool is_serialized() const { return m_shared_object.get() == nullptr; } const std::string& serialized_data() const { return m_serialized; } - std::shared_ptr& shared_ptr(StackImpl &stack) { - if (m_shared_object.get() == nullptr && ! this->m_serialized.empty()) { - // Deserialize the object. - std::istringstream iss(m_serialized); - { - Slic3r::UndoRedo::InputArchive archive(stack, iss); - std::unique_ptr::type> mesh(new std::remove_const::type()); - archive(*mesh.get()); - m_shared_object = std::move(mesh); - } - } - return m_shared_object; - } + std::shared_ptr& shared_ptr(StackImpl &stack); #ifdef SLIC3R_UNDOREDO_DEBUG std::string format() override { @@ -560,6 +548,21 @@ namespace cereal namespace Slic3r { namespace UndoRedo { +template std::shared_ptr& ImmutableObjectHistory::shared_ptr(StackImpl &stack) +{ + if (m_shared_object.get() == nullptr && ! this->m_serialized.empty()) { + // Deserialize the object. + std::istringstream iss(m_serialized); + { + Slic3r::UndoRedo::InputArchive archive(stack, iss); + std::unique_ptr::type> mesh(new std::remove_const::type()); + archive(*mesh.get()); + m_shared_object = std::move(mesh); + } + } + return m_shared_object; +} + template ObjectID StackImpl::save_mutable_object(const T &object) { // First find or allocate a history stack for the ObjectID of this object instance. From 7c732c7482c415dbd4726612bee4f93a2e72c9f9 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Thu, 4 Jul 2019 23:34:18 +0200 Subject: [PATCH 37/72] Trying to fix some Linux & OSX compilation issues. --- src/libslic3r/TriangleMesh.hpp | 1 + src/slic3r/Utils/UndoRedo.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/TriangleMesh.hpp b/src/libslic3r/TriangleMesh.hpp index 1fc512893..5dd2597a5 100644 --- a/src/libslic3r/TriangleMesh.hpp +++ b/src/libslic3r/TriangleMesh.hpp @@ -196,6 +196,7 @@ TriangleMesh make_sphere(double rho, double fa=(2*PI/360)); } // Serialization through the Cereal library +#include namespace cereal { template struct specialize {}; template void load(Archive &archive, Slic3r::TriangleMesh &mesh) { diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index d5da0c618..ca40c6939 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -555,7 +555,8 @@ template std::shared_ptr& ImmutableObjectHistory::share std::istringstream iss(m_serialized); { Slic3r::UndoRedo::InputArchive archive(stack, iss); - std::unique_ptr::type> mesh(new std::remove_const::type()); + typedef typename std::remove_const::type Type; + std::unique_ptr mesh(new Type()); archive(*mesh.get()); m_shared_object = std::move(mesh); } From 357e578a840cc07a5ae277c96534b866de2d5c79 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 5 Jul 2019 10:46:42 +0200 Subject: [PATCH 38/72] Fixed includes on OSX --- src/slic3r/Utils/UndoRedo.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/Utils/UndoRedo.hpp b/src/slic3r/Utils/UndoRedo.hpp index 92946d761..b08e410ff 100644 --- a/src/slic3r/Utils/UndoRedo.hpp +++ b/src/slic3r/Utils/UndoRedo.hpp @@ -1,8 +1,10 @@ #ifndef slic3r_Utils_UndoRedo_hpp_ #define slic3r_Utils_UndoRedo_hpp_ +#include #include #include +#include #include From 6a3fc5bde3eb1c6fca1ce8944d6d2dc0fef3d0fa Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 5 Jul 2019 11:42:36 +0200 Subject: [PATCH 39/72] Documented the cereal library manual patching (FIXME!) --- doc/How to build - Mac OS.md | 3 +++ src/libslic3r/Utils.hpp | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/How to build - Mac OS.md b/doc/How to build - Mac OS.md index 42a71e10d..b4196909d 100644 --- a/doc/How to build - Mac OS.md +++ b/doc/How to build - Mac OS.md @@ -20,6 +20,9 @@ You can also customize the bundle output path using the `-DDESTDIR=` **Warning**: Once the dependency bundle is installed in a destdir, the destdir cannot be moved elsewhere. (This is because wxWidgets hardcodes the installation path.) +FIXME The Cereal serialization library needs a tiny patch on some old OSX clang installations +https://github.com/USCiLab/cereal/issues/339#issuecomment-246166717 + ### Building PrusaSlicer diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index adf7f57a7..3b30e981c 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -182,7 +182,7 @@ class ScopeGuard public: typedef std::function Closure; private: - bool committed; +// bool committed; Closure closure; public: From 4e2fda3315dcfcc9272b71e7c286585a71dca0a5 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 5 Jul 2019 19:06:19 +0200 Subject: [PATCH 40/72] Undo / Redo fixes --- src/libslic3r/Model.hpp | 10 +- src/libslic3r/ObjectID.hpp | 2 +- src/slic3r/GUI/GLCanvas3D.cpp | 3 +- src/slic3r/GUI/GUI_ObjectList.cpp | 28 +++-- src/slic3r/GUI/GUI_ObjectList.hpp | 2 +- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 1 + src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 1 + src/slic3r/GUI/Plater.cpp | 10 +- src/slic3r/GUI/Selection.cpp | 24 ++++ src/slic3r/Utils/UndoRedo.cpp | 129 ++++++++++++---------- src/slic3r/Utils/UndoRedo.hpp | 30 +++-- 11 files changed, 151 insertions(+), 89 deletions(-) diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index f8ebe3134..7551bd8cb 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -283,7 +283,7 @@ private: explicit ModelObject(int) : ObjectBase(-1), config(-1), m_model(nullptr), origin_translation(Vec3d::Zero()), m_bounding_box_valid(false), m_raw_bounding_box_valid(false), m_raw_mesh_bounding_box_valid(false) { assert(this->id().invalid()); assert(this->config.id().invalid()); } ~ModelObject(); - void assign_new_unique_ids_recursive(); + void assign_new_unique_ids_recursive() override; // To be able to return an object from own copy / clone methods. Hopefully the compiler will do the "Copy elision" // (Omits copy and move(since C++11) constructors, resulting in zero - copy pass - by - value semantics). @@ -463,6 +463,7 @@ protected: // Copies IDs of both the ModelVolume and its config. explicit ModelVolume(const ModelVolume &rhs) = default; void set_model_object(ModelObject *model_object) { object = model_object; } + void assign_new_unique_ids_recursive() override { ObjectBase::set_new_unique_id(); config.set_new_unique_id(); } void transform_this_mesh(const Transform3d& t, bool fix_left_handed); void transform_this_mesh(const Matrix3d& m, bool fix_left_handed); @@ -508,14 +509,13 @@ private: ModelVolume(ModelObject *object, const ModelVolume &other, const TriangleMesh &&mesh) : name(other.name), m_mesh(new TriangleMesh(std::move(mesh))), config(other.config), m_type(other.m_type), object(object), m_transformation(other.m_transformation) { -// assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); -// assert(this->id() == other.id() && this->config.id() == other.config.id()); + assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); + assert(this->id() != other.id() && this->config.id() == other.config.id()); this->set_material_id(other.material_id()); this->config.set_new_unique_id(); if (mesh.stl.stats.number_of_facets > 1) calculate_convex_hull(); - assert(this->id().valid()); assert(this->config.id().valid()); assert(this->id() != this->config.id()); - assert(this->id() != other.id() && this->config.id() != other.config.id()); + assert(this->config.id().valid()); assert(this->config.id() != other.config.id()); assert(this->id() != this->config.id()); } ModelVolume& operator=(ModelVolume &rhs) = delete; diff --git a/src/libslic3r/ObjectID.hpp b/src/libslic3r/ObjectID.hpp index df16f9202..484d1173b 100644 --- a/src/libslic3r/ObjectID.hpp +++ b/src/libslic3r/ObjectID.hpp @@ -66,7 +66,7 @@ protected: void copy_id(const ObjectBase &rhs) { m_id = rhs.id(); } // Override this method if a ObjectBase derived class owns other ObjectBase derived instances. - void assign_new_unique_ids_recursive() { this->set_new_unique_id(); } + virtual void assign_new_unique_ids_recursive() { this->set_new_unique_id(); } private: ObjectID m_id; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 43d419eea..1d5cec04a 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2947,9 +2947,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if ((m_mouse.drag.move_volume_idx != -1) && m_mouse.dragging) { m_regenerate_volumes = false; + wxGetApp().plater()->take_snapshot(_(L("Move Object"))); do_move(); wxGetApp().obj_manipul()->set_dirty(); - // Let the platter know that the dragging finished, so a delayed refresh + // Let the plater know that the dragging finished, so a delayed refresh // of the scene with the background processing data should be performed. post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); } diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 4ef1cb0d2..582fe1007 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2116,7 +2116,7 @@ void ObjectList::part_selection_changed() panel.Thaw(); } -void ObjectList::add_object_to_list(size_t obj_idx) +void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) { auto model_object = (*m_objects)[obj_idx]; const wxString& item_name = from_u8(model_object->name); @@ -2137,7 +2137,9 @@ void ObjectList::add_object_to_list(size_t obj_idx) false); auto opt_keys = volume->config.keys(); if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) { - select_item(m_objects_model->AddSettingsChild(vol_item)); + const wxDataViewItem &settings_item = m_objects_model->AddSettingsChild(vol_item); + if (call_selection_changed) + select_item(settings_item); Expand(vol_item); } } @@ -2151,7 +2153,9 @@ void ObjectList::add_object_to_list(size_t obj_idx) // add settings to the object, if it has those auto opt_keys = model_object->config.keys(); if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) { - select_item(m_objects_model->AddSettingsChild(item)); + const wxDataViewItem &settings_item = m_objects_model->AddSettingsChild(item); + if (call_selection_changed) + select_item(settings_item); Expand(item); } @@ -2159,7 +2163,8 @@ void ObjectList::add_object_to_list(size_t obj_idx) add_layer_root_item(item); #ifndef __WXOSX__ - selection_changed(); + if (call_selection_changed) + selection_changed(); #endif //__WXMSW__ } @@ -2963,11 +2968,10 @@ void ObjectList::change_part_type() void ObjectList::last_volume_is_deleted(const int obj_idx) { - if (obj_idx < 0 || m_objects->empty() || - obj_idx <= m_objects->size() || - (*m_objects)[obj_idx]->volumes.empty()) + if (obj_idx < 0 || obj_idx >= m_objects->size() || (*m_objects)[obj_idx]->volumes.empty()) return; - auto volume = (*m_objects)[obj_idx]->volumes[0]; + + auto volume = (*m_objects)[obj_idx]->volumes.front(); // clear volume's config values volume->config.clear(); @@ -3388,14 +3392,20 @@ void ObjectList::recreate_object_list() m_prevent_list_events = true; m_prevent_canvas_selection_update = true; + // Unselect all objects before deleting them, so that no change of selection is emitted during deletion. + this->UnselectAll(); m_objects_model->DeleteAll(); size_t obj_idx = 0; while (obj_idx < m_objects->size()) { - add_object_to_list(obj_idx); + add_object_to_list(obj_idx, false); ++obj_idx; } +#ifndef __WXOSX__ + selection_changed(); +#endif /* __WXOSX__ */ + m_prevent_canvas_selection_update = false; m_prevent_list_events = false; } diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 354f6c019..2a92ecbe4 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -261,7 +261,7 @@ public: void part_selection_changed(); // Add object to the list - void add_object_to_list(size_t obj_idx); + void add_object_to_list(size_t obj_idx, bool call_selection_changed = true); // Delete object from the list void delete_object_from_list(); void delete_object_from_list(const size_t obj_idx); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 372cd79ef..79e45facd 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -655,6 +655,7 @@ void ObjectManipulation::change_position_value(int axis, double value) Selection& selection = canvas->get_selection(); selection.start_dragging(); selection.translate(position - m_cache.position, selection.requires_local_axes()); + wxGetApp().plater()->take_snapshot(_(L("Set Position"))); canvas->do_move(); m_cache.position = position; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index c7435636d..c8900d8da 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -673,6 +673,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas) { case Move: { + wxGetApp().plater()->take_snapshot(_(L("Move Object"))); canvas.disable_regenerate_volumes(); canvas.do_move(); break; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 0865ab713..bb394ffcf 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1773,9 +1773,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) view3D_canvas->Bind(EVT_GLCANVAS_QUESTION_MARK, [this](SimpleEvent&) { wxGetApp().keyboard_shortcuts(); }); view3D_canvas->Bind(EVT_GLCANVAS_INCREASE_INSTANCES, [this](Event &evt) { if (evt.data == 1) this->q->increase_instances(); else if (this->can_decrease_instances()) this->q->decrease_instances(); }); - view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { - this->take_snapshot(_(L("Instance Moved"))); - update(); }); + view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_MOVED, [this](SimpleEvent&) { update(); }); view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_MOVED, &priv::on_wipetower_moved, this); view3D_canvas->Bind(EVT_GLCANVAS_WIPETOWER_ROTATED, &priv::on_wipetower_rotated, this); view3D_canvas->Bind(EVT_GLCANVAS_INSTANCE_ROTATED, [this](SimpleEvent&) { update(); }); @@ -1826,7 +1824,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) // updates camera type from .ini file camera.set_type(get_config("use_perspective_camera")); - this->undo_redo_stack.initialize(model, view3D->get_canvas3d()->get_selection()); + // Initialize the Undo / Redo stack with a first snapshot. this->take_snapshot(_(L("New Project"))); } @@ -3196,8 +3194,6 @@ void Plater::priv::on_right_click(Vec2dEvent& evt) void Plater::priv::on_wipetower_moved(Vec3dEvent &evt) { - this->take_snapshot(_(L("Wipe Tower Moved"))); - DynamicPrintConfig cfg; cfg.opt("wipe_tower_x", true)->value = evt.data(0); cfg.opt("wipe_tower_y", true)->value = evt.data(1); @@ -3206,8 +3202,6 @@ void Plater::priv::on_wipetower_moved(Vec3dEvent &evt) void Plater::priv::on_wipetower_rotated(Vec3dEvent& evt) { - this->take_snapshot(_(L("Wipe Tower Rotated"))); - DynamicPrintConfig cfg; cfg.opt("wipe_tower_x", true)->value = evt.data(0); cfg.opt("wipe_tower_y", true)->value = evt.data(1); diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 2d5be01ff..a71005cf2 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -806,6 +806,8 @@ void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config) double s = std::min(sx, std::min(sy, sz)); if (s != 1.0) { + wxGetApp().plater()->take_snapshot(_(L("Scale To Fit"))); + TransformationType type; type.set_world(); type.set_relative(); @@ -2032,6 +2034,10 @@ bool Selection::is_from_fully_selected_instance(unsigned int volume_idx) const void Selection::paste_volumes_from_clipboard() { +#ifdef _DEBUG + check_model_ids_validity(*m_model); +#endif /* _DEBUG */ + int dst_obj_idx = get_object_idx(); if ((dst_obj_idx < 0) || ((int)m_model->objects.size() <= dst_obj_idx)) return; @@ -2073,6 +2079,9 @@ void Selection::paste_volumes_from_clipboard() } volumes.push_back(dst_volume); +#ifdef _DEBUG + check_model_ids_validity(*m_model); +#endif /* _DEBUG */ } // keeps relative position of multivolume selections @@ -2086,10 +2095,18 @@ void Selection::paste_volumes_from_clipboard() wxGetApp().obj_list()->paste_volumes_into_list(dst_obj_idx, volumes); } + +#ifdef _DEBUG + check_model_ids_validity(*m_model); +#endif /* _DEBUG */ } void Selection::paste_objects_from_clipboard() { +#ifdef _DEBUG + check_model_ids_validity(*m_model); +#endif /* _DEBUG */ + std::vector object_idxs; const ModelObjectPtrs& src_objects = m_clipboard.get_objects(); for (const ModelObject* src_object : src_objects) @@ -2103,9 +2120,16 @@ void Selection::paste_objects_from_clipboard() } object_idxs.push_back(m_model->objects.size() - 1); +#ifdef _DEBUG + check_model_ids_validity(*m_model); +#endif /* _DEBUG */ } wxGetApp().obj_list()->paste_objects_into_list(object_idxs); + +#ifdef _DEBUG + check_model_ids_validity(*m_model); +#endif /* _DEBUG */ } } // namespace GUI diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index ca40c6939..058062502 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -28,6 +28,13 @@ namespace Slic3r { namespace UndoRedo { +static std::string topmost_snapsnot_name = "@@@ Topmost @@@"; + +bool Snapshot::is_topmost() const +{ + return this->name == topmost_snapsnot_name; +} + // Time interval, start is closed, end is open. struct Interval { @@ -63,12 +70,13 @@ public: // Is the object captured by this history mutable or immutable? virtual bool is_mutable() const = 0; virtual bool is_immutable() const = 0; + virtual const void* immutable_object_ptr() const { return nullptr; } // If the history is empty, the ObjectHistory object could be released. virtual bool empty() = 0; // Release all data after the given timestamp. For the ImmutableObjectHistory, the shared pointer is NOT released. - virtual void relese_after_timestamp(size_t timestamp) = 0; + virtual void release_after_timestamp(size_t timestamp) = 0; #ifdef SLIC3R_UNDOREDO_DEBUG // Human readable debug information. @@ -89,12 +97,12 @@ public: bool empty() override { return m_history.empty(); } // Release all data after the given timestamp. The shared pointer is NOT released. - void relese_after_timestamp(size_t timestamp) override { + void release_after_timestamp(size_t timestamp) override { assert(! m_history.empty()); assert(this->valid()); // it points to an interval which either starts with timestamp, or follows the timestamp. auto it = std::lower_bound(m_history.begin(), m_history.end(), T(timestamp, timestamp)); - if (it == m_history.end()) { + if (it != m_history.begin()) { auto it_prev = it; -- it_prev; assert(it_prev->begin() < timestamp); @@ -128,6 +136,7 @@ public: bool is_mutable() const override { return false; } bool is_immutable() const override { return true; } + const void* immutable_object_ptr() const { return (const void*)m_shared_object.get(); } void save(size_t active_snapshot_time, size_t current_time) { assert(m_history.empty() || m_history.back().end() <= active_snapshot_time || @@ -160,9 +169,9 @@ public: std::string out = typeid(T).name(); out += this->is_serialized() ? std::string(" len:") + std::to_string(m_serialized.size()) : - std::string(" ptr:") + ptr_to_string(m_shared_object.get()); + std::string(" shared_ptr:") + ptr_to_string(m_shared_object.get()); for (const Interval &interval : m_history) - out += std::string(",<") + std::to_string(interval.begin()) + "," + std::to_string(interval.end()) + ")"; + out += std::string(", <") + std::to_string(interval.begin()) + "," + std::to_string(interval.end()) + ")"; return out; } #endif /* SLIC3R_UNDOREDO_DEBUG */ @@ -296,13 +305,8 @@ public: #ifdef SLIC3R_UNDOREDO_DEBUG std::string format() override { std::string out = typeid(T).name(); - bool first = true; - for (const MutableHistoryInterval &interval : m_history) { - if (! first) - out += ","; - out += std::string("ptr:") + ptr_to_string(interval.data()) + " len:" + std::to_string(interval.size()) + " <" + std::to_string(interval.begin()) + "," + std::to_string(interval.end()) + ")"; - first = false; - } + for (const MutableHistoryInterval &interval : m_history) + out += std::string(", ptr:") + ptr_to_string(interval.data()) + " len:" + std::to_string(interval.size()) + " <" + std::to_string(interval.begin()) + "," + std::to_string(interval.end()) + ")"; return out; } #endif /* SLIC3R_UNDOREDO_DEBUG */ @@ -364,11 +368,13 @@ public: bool has_undo_snapshot() const; bool has_redo_snapshot() const; - bool undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection); - bool redo(Slic3r::Model &model); + bool undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection, size_t jump_to_time); + bool redo(Slic3r::Model &model, size_t jump_to_time); // Snapshot history (names with timestamps). - const std::vector& snapshots() const { return m_snapshots; } + const std::vector& snapshots() const { return m_snapshots; } + // Timestamp of the active snapshot. + size_t active_snapshot_time() const { return m_active_snapshot_time; } const Selection& selection_deserialized() const { return m_selection; } @@ -388,7 +394,8 @@ public: for (const Snapshot &snapshot : m_snapshots) { if (snapshot.timestamp == m_active_snapshot_time) out += ">>> "; - out += std::string("Name:") + snapshot.name + ", timestamp: " + std::to_string(snapshot.timestamp) + ", Model ID:" + std::to_string(snapshot.model_id) + "\n"; + out += std::string("Name: \"") + snapshot.name + "\", timestamp: " + std::to_string(snapshot.timestamp) + + ", Model ID:" + ((snapshot.model_id == 0) ? "Invalid" : std::to_string(snapshot.model_id)) + "\n"; } if (m_active_snapshot_time > m_snapshots.back().timestamp) out += ">>>\n"; @@ -406,9 +413,9 @@ public: bool valid() const { assert(! m_snapshots.empty()); auto it = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); - assert(it == m_snapshots.end() || (it != m_snapshots.begin() && it->timestamp == m_active_snapshot_time)); - assert(it != m_snapshots.end() || m_active_snapshot_time > m_snapshots.back().timestamp); - for (auto it = m_objects.begin(); it != m_objects.end(); ++ it) + assert(it != m_snapshots.begin() && it != m_snapshots.end() && it->timestamp == m_active_snapshot_time); + assert(m_active_snapshot_time < m_snapshots.back().timestamp || m_snapshots.back().is_topmost()); + for (auto it = m_objects.begin(); it != m_objects.end(); ++ it) assert(it->second->valid()); return true; } @@ -624,29 +631,13 @@ template void StackImpl::load_mutable_object(const Sl archive(static_cast(target)); } -// The Undo / Redo stack is being initialized with an empty model and an empty selection. -// The first snapshot cannot be removed. -void StackImpl::initialize(const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) -{ - assert(m_active_snapshot_time == 0); - assert(m_current_time == 0); - // The initial time interval will be <0, 1) - m_active_snapshot_time = SIZE_MAX; // let it overflow to zero in take_snapshot - m_current_time = 0; - this->take_snapshot("Internal - Initialized", model, selection); -} - // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time. void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) { // Release old snapshot data. - // The active snapshot may be above the last snapshot if there is no redo data available. - if (! m_snapshots.empty() && m_active_snapshot_time > m_snapshots.back().timestamp) - m_active_snapshot_time = m_snapshots.back().timestamp + 1; - else - ++ m_active_snapshot_time; + assert(m_active_snapshot_time <= m_current_time); for (auto &kvp : m_objects) - kvp.second->relese_after_timestamp(m_active_snapshot_time); + kvp.second->release_after_timestamp(m_active_snapshot_time); { auto it = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); m_snapshots.erase(it, m_snapshots.end()); @@ -662,6 +653,9 @@ void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Mo // Save the snapshot info. m_snapshots.emplace_back(snapshot_name, m_current_time ++, model.id().id); m_active_snapshot_time = m_current_time; + // Save snapshot info of the last "current" aka "top most" state, that is only being serialized + // if undoing an action. Such a snapshot has an invalid Model ID assigned if it was not taken yet. + m_snapshots.emplace_back(topmost_snapsnot_name, m_active_snapshot_time, 0); // Release empty objects from the history. this->collect_garbage(); assert(this->valid()); @@ -675,7 +669,7 @@ void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model) { // Find the snapshot by time. It must exist. const auto it_snapshot = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(timestamp)); - if (it_snapshot == m_snapshots.begin() || it_snapshot == m_snapshots.end() || it_snapshot->timestamp != timestamp) + if (it_snapshot == m_snapshots.end() || it_snapshot->timestamp != timestamp) throw std::runtime_error((boost::format("Snapshot with timestamp %1% does not exist") % timestamp).str()); m_active_snapshot_time = timestamp; @@ -699,18 +693,37 @@ bool StackImpl::has_undo_snapshot() const } bool StackImpl::has_redo_snapshot() const -{ +{ + assert(this->valid()); auto it = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); - return it != m_snapshots.end() && ++ it != m_snapshots.end(); + return ++ it != m_snapshots.end(); } -bool StackImpl::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection) +bool StackImpl::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection, size_t time_to_load) { assert(this->valid()); - auto it_current = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); - if (-- it_current == m_snapshots.begin()) - return false; - this->load_snapshot(it_current->timestamp, model); + if (time_to_load == SIZE_MAX) { + auto it_current = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); + if (-- it_current == m_snapshots.begin()) + return false; + time_to_load = it_current->timestamp; + } + assert(time_to_load < m_active_snapshot_time); + assert(std::binary_search(m_snapshots.begin(), m_snapshots.end(), Snapshot(time_to_load))); + if (m_active_snapshot_time == m_snapshots.back().timestamp && ! m_snapshots.back().is_topmost_captured()) { + // The current state is temporary. The current state needs to be captured to be redoable. + this->take_snapshot(topmost_snapsnot_name, model, selection); + // The line above entered another topmost_snapshot_name. + assert(m_snapshots.back().is_topmost()); + assert(! m_snapshots.back().is_topmost_captured()); + // Pop it back, it is not needed as there is now a captured topmost state. + m_snapshots.pop_back(); + // current_time was extended, but it should not cause any harm. Resetting it back may complicate the logic unnecessarily. + //-- m_current_time; + assert(m_snapshots.back().is_topmost()); + assert(m_snapshots.back().is_topmost_captured()); + } + this->load_snapshot(time_to_load, model); #ifdef SLIC3R_UNDOREDO_DEBUG std::cout << "After undo" << std::endl; this->print(); @@ -718,13 +731,18 @@ bool StackImpl::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selecti return true; } -bool StackImpl::redo(Slic3r::Model &model) +bool StackImpl::redo(Slic3r::Model &model, size_t time_to_load) { assert(this->valid()); - auto it_current = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); - if (it_current == m_snapshots.end() || ++ it_current == m_snapshots.end()) - return false; - this->load_snapshot(it_current->timestamp, model); + if (time_to_load == SIZE_MAX) { + auto it_current = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); + if (++ it_current == m_snapshots.end()) + return false; + time_to_load = it_current->timestamp; + } + assert(time_to_load > m_active_snapshot_time); + assert(std::binary_search(m_snapshots.begin(), m_snapshots.end(), Snapshot(time_to_load))); + this->load_snapshot(time_to_load, model); #ifdef SLIC3R_UNDOREDO_DEBUG std::cout << "After redo" << std::endl; this->print(); @@ -737,9 +755,9 @@ void StackImpl::collect_garbage() // Purge objects with empty histories. for (auto it = m_objects.begin(); it != m_objects.end();) { if (it->second->empty()) { - if (it->second->is_immutable()) + if (it->second->immutable_object_ptr() != nullptr) // Release the immutable object from the ptr to ObjectID map. - this->m_objects.erase(it->first); + m_shared_ptr_to_object_id.erase(it->second->immutable_object_ptr()); it = m_objects.erase(it); } else ++ it; @@ -749,16 +767,15 @@ void StackImpl::collect_garbage() // Wrappers of the private implementation. Stack::Stack() : pimpl(new StackImpl()) {} Stack::~Stack() {} -void Stack::initialize(const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) { pimpl->initialize(model, selection); } void Stack::take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) { pimpl->take_snapshot(snapshot_name, model, selection); } -void Stack::load_snapshot(size_t timestamp, Slic3r::Model &model) { pimpl->load_snapshot(timestamp, model); } bool Stack::has_undo_snapshot() const { return pimpl->has_undo_snapshot(); } bool Stack::has_redo_snapshot() const { return pimpl->has_redo_snapshot(); } -bool Stack::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection) { return pimpl->undo(model, selection); } -bool Stack::redo(Slic3r::Model &model) { return pimpl->redo(model); } +bool Stack::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection, size_t time_to_load) { return pimpl->undo(model, selection, time_to_load); } +bool Stack::redo(Slic3r::Model &model, size_t time_to_load) { return pimpl->redo(model, time_to_load); } const Selection& Stack::selection_deserialized() const { return pimpl->selection_deserialized(); } const std::vector& Stack::snapshots() const { return pimpl->snapshots(); } +size_t Stack::active_snapshot_time() const { return pimpl->active_snapshot_time(); } } // namespace UndoRedo } // namespace Slic3r diff --git a/src/slic3r/Utils/UndoRedo.hpp b/src/slic3r/Utils/UndoRedo.hpp index b08e410ff..178e5a11a 100644 --- a/src/slic3r/Utils/UndoRedo.hpp +++ b/src/slic3r/Utils/UndoRedo.hpp @@ -29,6 +29,12 @@ struct Snapshot bool operator< (const Snapshot &rhs) const { return this->timestamp < rhs.timestamp; } bool operator==(const Snapshot &rhs) const { return this->timestamp == rhs.timestamp; } + + // The topmost snapshot represents the current state when going forward. + bool is_topmost() const; + // The topmost snapshot is not being serialized to the Undo / Redo stack until going back in time, + // when the top most state is being serialized, so we can redo back to the top most state. + bool is_topmost_captured() const { assert(this->is_topmost()); return model_id > 0; } }; // Excerpt of Slic3r::GUI::Selection for serialization onto the Undo / Redo stack. @@ -44,25 +50,33 @@ class Stack { public: // Stack needs to be initialized. An empty stack is not valid, there must be a "New Project" status stored at the beginning. + // The first "New Project" snapshot shall not be removed. Stack(); ~Stack(); - // The Undo / Redo stack is being initialized with an empty model and an empty selection. - // The first snapshot cannot be removed. - void initialize(const Slic3r::Model &model, const Slic3r::GUI::Selection &selection); - // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time. void take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection); - void load_snapshot(size_t timestamp, Slic3r::Model &model); + // To be queried to enable / disable the Undo / Redo buttons at the UI. bool has_undo_snapshot() const; bool has_redo_snapshot() const; - // Undoing an action may need to take a snapshot of the current application state. - bool undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection); - bool redo(Slic3r::Model &model); + + // Roll back the time. If time_to_load is SIZE_MAX, the previous snapshot is activated. + // Undoing an action may need to take a snapshot of the current application state, so that redo to the current state is possible. + bool undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection, size_t time_to_load = SIZE_MAX); + + // Jump forward in time. If time_to_load is SIZE_MAX, the next snapshot is activated. + bool redo(Slic3r::Model &model, size_t time_to_load = SIZE_MAX); // Snapshot history (names with timestamps). + // Each snapshot indicates start of an interval in which this operation is performed. + // There is one additional snapshot taken at the very end, which indicates the current unnamed state. + const std::vector& snapshots() const; + // Timestamp of the active snapshot. One of the snapshots of this->snapshots() shall have Snapshot::timestamp equal to this->active_snapshot_time(). + // The snapshot time indicates start of an operation, which is finished at the time of the following snapshot, therefore + // the active snapshot is the successive snapshot. The same logic applies to the time_to_load parameter of undo() and redo() operations. + size_t active_snapshot_time() const; // After load_snapshot() / undo() / redo() the selection is deserialized into a list of ObjectIDs, which needs to be converted // into the list of GLVolume pointers once the 3D scene is updated. From 270fec84d330a931a523081a460369d9871dbd2b Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 5 Jul 2019 19:46:48 +0200 Subject: [PATCH 41/72] Fix of the Undo / Redo for Cut. Added some more operations (for example Rotation) to the Undo / Redo. --- src/admesh/connect.cpp | 2 +- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 2 ++ src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 2 ++ src/slic3r/GUI/Plater.cpp | 5 +++-- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/admesh/connect.cpp b/src/admesh/connect.cpp index e729c8922..b86ec5055 100644 --- a/src/admesh/connect.cpp +++ b/src/admesh/connect.cpp @@ -132,7 +132,7 @@ struct HashTableEdges { ~HashTableEdges() { #ifndef NDEBUG for (int i = 0; i < this->M; ++ i) - for (HashEdge *temp = this->heads[i]; this->heads[i] != this->tail; temp = this->heads[i]) + for (HashEdge *temp = this->heads[i]; temp != this->tail; temp = temp->next) ++ this->freed; this->tail = nullptr; #endif /* NDEBUG */ diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 79e45facd..ec5272a44 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -302,6 +302,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL); selection.synchronize_unselected_volumes(); // Copy rotation values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing. + wxGetApp().plater()->take_snapshot(_(L("Set Rotation"))); canvas->do_rotate(); UpdateAndShow(true); @@ -687,6 +688,7 @@ void ObjectManipulation::change_rotation_value(int axis, double value) selection.rotate( (M_PI / 180.0) * (transformation_type.absolute() ? rotation : rotation - m_cache.rotation), transformation_type); + wxGetApp().plater()->take_snapshot(_(L("Set Orientation"))); canvas->do_rotate(); m_cache.rotation = rotation; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index c8900d8da..438cd1a10 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -600,6 +600,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas) if (m_current == Flatten) { // Rotate the object so the normal points downward: + wxGetApp().plater()->take_snapshot(_(L("Place on Face"))); selection.flattening_rotate(get_flattening_normal()); canvas.do_flatten(); wxGetApp().obj_manipul()->set_dirty(); @@ -685,6 +686,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas) } case Rotate: { + wxGetApp().plater()->take_snapshot(_(L("Rotate Object"))); canvas.do_rotate(); break; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index bb394ffcf..aa7a54286 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2389,7 +2389,6 @@ void Plater::priv::deselect_all() void Plater::priv::remove(size_t obj_idx) { - this->take_snapshot(_(L("Remove Object"))); // Prevent toolpaths preview from rendering while we modify the Print object preview->set_enabled(false); @@ -2406,7 +2405,7 @@ void Plater::priv::remove(size_t obj_idx) void Plater::priv::delete_object_from_model(size_t obj_idx) { -// this->take_snapshot(_(L("Delete Object"))); // ys_FIXME What is the difference with "Remove Object"? + this->take_snapshot(_(L("Delete Object"))); model.delete_object(obj_idx); update(); object_list_changed(); @@ -2850,6 +2849,8 @@ void Plater::priv::update_sla_scene() void Plater::priv::reload_from_disk() { + this->take_snapshot(_(L("Reload from Disk"))); + const auto &selection = get_selection(); const auto obj_orig_idx = selection.get_object_idx(); if (selection.is_wipe_tower() || obj_orig_idx == -1) { return; } From 45a5487e51e7ee451cba7140524a0d4a659c559c Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 5 Jul 2019 20:09:30 +0200 Subject: [PATCH 42/72] Fix of compilation on clang --- src/slic3r/Utils/UndoRedo.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/Utils/UndoRedo.hpp b/src/slic3r/Utils/UndoRedo.hpp index 178e5a11a..f8bfda08c 100644 --- a/src/slic3r/Utils/UndoRedo.hpp +++ b/src/slic3r/Utils/UndoRedo.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include From fb725502b7a6f7fd645d83ab640e22e0581d413d Mon Sep 17 00:00:00 2001 From: bubnikv Date: Fri, 5 Jul 2019 20:27:44 +0200 Subject: [PATCH 43/72] Undo / Redo: Bound Ctrl-V/Ctrl-Z to the side panel. --- src/slic3r/GUI/GUI_ObjectList.cpp | 26 ++++++++++++++++++++++---- src/slic3r/GUI/GUI_ObjectList.hpp | 2 ++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 582fe1007..90c3f2665 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -143,20 +143,24 @@ ObjectList::ObjectList(wxWindow* parent) : // Bind(wxEVT_KEY_DOWN, &ObjectList::OnChar, this); { // Accelerators - wxAcceleratorEntry entries[6]; + wxAcceleratorEntry entries[8]; entries[0].Set(wxACCEL_CTRL, (int) 'C', wxID_COPY); entries[1].Set(wxACCEL_CTRL, (int) 'X', wxID_CUT); entries[2].Set(wxACCEL_CTRL, (int) 'V', wxID_PASTE); entries[3].Set(wxACCEL_CTRL, (int) 'A', wxID_SELECTALL); - entries[4].Set(wxACCEL_NORMAL, WXK_DELETE, wxID_DELETE); - entries[5].Set(wxACCEL_NORMAL, WXK_BACK, wxID_DELETE); - wxAcceleratorTable accel(6, entries); + entries[4].Set(wxACCEL_CTRL, (int) 'Z', wxID_UNDO); + entries[5].Set(wxACCEL_CTRL, (int) 'Y', wxID_REDO); + entries[6].Set(wxACCEL_NORMAL, WXK_DELETE, wxID_DELETE); + entries[7].Set(wxACCEL_NORMAL, WXK_BACK, wxID_DELETE); + wxAcceleratorTable accel(8, entries); SetAcceleratorTable(accel); this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->copy(); }, wxID_COPY); this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->paste(); }, wxID_PASTE); this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->select_item_all_children(); }, wxID_SELECTALL); this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->remove(); }, wxID_DELETE); + this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->undo(); }, wxID_UNDO); + this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->redo(); }, wxID_REDO); } #else __WXOSX__ Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { key_event(event); }); // doesn't work on OSX @@ -809,6 +813,16 @@ void ObjectList::paste() wxPostEvent((wxEvtHandler*)wxGetApp().plater()->canvas3D()->get_wxglcanvas(), SimpleEvent(EVT_GLTOOLBAR_PASTE)); } +void ObjectList::undo() +{ + wxGetApp().plater()->undo(); +} + +void ObjectList::redo() +{ + wxGetApp().plater()->redo(); +} + #ifndef __WXOSX__ void ObjectList::key_event(wxKeyEvent& event) { @@ -827,6 +841,10 @@ void ObjectList::key_event(wxKeyEvent& event) copy(); else if (wxGetKeyState(wxKeyCode('V')) && wxGetKeyState(WXK_CONTROL)) paste(); + else if (wxGetKeyState(wxKeyCode('Y')) && wxGetKeyState(WXK_CONTROL)) + redo(); + else if (wxGetKeyState(wxKeyCode('Z')) && wxGetKeyState(WXK_CONTROL)) + undo(); else event.Skip(); } diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 2a92ecbe4..a5a9c2138 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -204,6 +204,8 @@ public: void copy(); void paste(); + void undo(); + void redo(); void get_settings_choice(const wxString& category_name); void get_freq_settings_choice(const wxString& bundle_name); From 7b6229289d144d1f24feb53cda31973a64e34ff8 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 8 Jul 2019 10:57:35 +0200 Subject: [PATCH 44/72] Added undo/redo to the "Edit" menu --- src/slic3r/GUI/MainFrame.cpp | 8 ++++++++ src/slic3r/GUI/Plater.cpp | 10 ++++++++++ src/slic3r/GUI/Plater.hpp | 2 ++ 3 files changed, 20 insertions(+) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index d800f6f38..67b336a0e 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -502,6 +502,14 @@ void MainFrame::init_menubar() _(L("Deletes all objects")), [this](wxCommandEvent&) { m_plater->reset_with_confirm(); }, menu_icon("delete_all_menu"), nullptr, [this](){return can_delete_all(); }, this); + editMenu->AppendSeparator(); + append_menu_item(editMenu, wxID_ANY, _(L("&Undo")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "Z", + _(L("Undo")), [this](wxCommandEvent&) { m_plater->undo(); }, + "undo", nullptr, [this](){return m_plater->can_undo(); }, this); + append_menu_item(editMenu, wxID_ANY, _(L("&Redo")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "Y", + _(L("Redo")), [this](wxCommandEvent&) { m_plater->redo(); }, + "undo", nullptr, [this](){return m_plater->can_redo(); }, this); + editMenu->AppendSeparator(); append_menu_item(editMenu, wxID_ANY, _(L("&Copy")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "C", _(L("Copy selection to clipboard")), [this](wxCommandEvent&) { m_plater->copy_selection_to_clipboard(); }, diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index aa7a54286..6ac58eb84 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4405,6 +4405,16 @@ bool Plater::can_copy_to_clipboard() const return true; } +bool Plater::can_undo() const +{ + return p->undo_redo_stack.has_undo_snapshot(); +} + +bool Plater::can_redo() const +{ + return p->undo_redo_stack.has_redo_snapshot(); +} + SuppressBackgroundProcessingUpdate::SuppressBackgroundProcessingUpdate() : m_was_running(wxGetApp().plater()->is_background_process_running()) { diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index d38957b3a..0be465f53 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -222,6 +222,8 @@ public: bool can_layers_editing() const; bool can_paste_from_clipboard() const; bool can_copy_to_clipboard() const; + bool can_undo() const; + bool can_redo() const; void msw_rescale(); From fbf14b42e9831b5fc99be555668e126df37f9d58 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Mon, 8 Jul 2019 18:01:14 +0200 Subject: [PATCH 45/72] Added undo/redo icons. Fist step to implementation Undo/Redo list for toolbar --- resources/icons/redo.svg | 12 +++++++ resources/icons/redo_toolbar.svg | 12 +++++++ resources/icons/undo_toolbar.svg | 12 +++++++ src/slic3r/GUI/GLCanvas3D.cpp | 58 ++++++++++++++++++++++++++++++++ src/slic3r/GUI/GLToolbar.cpp | 4 +++ src/slic3r/GUI/GLToolbar.hpp | 7 ++++ src/slic3r/GUI/ImGuiWrapper.cpp | 21 ++++++++++++ src/slic3r/GUI/ImGuiWrapper.hpp | 1 + src/slic3r/GUI/MainFrame.cpp | 2 +- 9 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 resources/icons/redo.svg create mode 100644 resources/icons/redo_toolbar.svg create mode 100644 resources/icons/undo_toolbar.svg diff --git a/resources/icons/redo.svg b/resources/icons/redo.svg new file mode 100644 index 000000000..9109779bb --- /dev/null +++ b/resources/icons/redo.svg @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/resources/icons/redo_toolbar.svg b/resources/icons/redo_toolbar.svg new file mode 100644 index 000000000..ad073244f --- /dev/null +++ b/resources/icons/redo_toolbar.svg @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/resources/icons/undo_toolbar.svg b/resources/icons/undo_toolbar.svg new file mode 100644 index 000000000..699ccd807 --- /dev/null +++ b/resources/icons/undo_toolbar.svg @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 1d5cec04a..970b349cb 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3618,6 +3618,64 @@ bool GLCanvas3D::_init_toolbar() if (!m_toolbar.add_item(item)) return false; + if (!m_toolbar.add_separator()) + return false; + + item.name = "undo"; +#if ENABLE_SVG_ICONS + item.icon_filename = "undo_toolbar.svg"; +#endif // ENABLE_SVG_ICONS + item.tooltip = _utf8(L("Undo")) + " [" + GUI::shortkey_ctrl_prefix() + "Z]"; + item.sprite_id = 11; + item.action_callback = [this]() + { + if (m_canvas != nullptr) { + wxPostEvent(m_canvas, SimpleEvent(EVT_GLCANVAS_UNDO)); + m_toolbar.set_imgui_visible(); + } + }; + item.visibility_callback = []()->bool { return true; }; + item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_undo(); }; + item.render_callback = [this]() + { + if (m_canvas != nullptr && m_toolbar.get_imgui_visible()) { + ImGuiWrapper* imgui = wxGetApp().imgui(); + + const float approx_height = m_toolbar.get_height(); + imgui->set_next_window_pos(600, approx_height, ImGuiCond_Always); + + imgui->set_next_window_bg_alpha(0.5f); + imgui->begin(_(L("Undo Stack")), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + + std::vector undo_stack = {"A", "B", "C", "D","A", "B", "C", "D","A", "B", "C", "D",}; + int sel = 4; + imgui->multi_sel_list("", undo_stack, sel); + + const bool undo_clicked = imgui->button(_(L("Undo N Action"))); + + imgui->end(); + if (undo_clicked) + m_toolbar.set_imgui_visible(false); + } + }; + if (!m_toolbar.add_item(item)) + return false; + + item.name = "redo"; +#if ENABLE_SVG_ICONS + item.icon_filename = "redo_toolbar.svg"; +#endif // ENABLE_SVG_ICONS + item.tooltip = _utf8(L("Redo")) + " [" + GUI::shortkey_ctrl_prefix() + "Y]"; + item.sprite_id = 12; + item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLCANVAS_REDO)); }; + item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_redo(); }; + item.render_callback = []() {}; + if (!m_toolbar.add_item(item)) + return false; + + if (!m_toolbar.add_separator()) + return false; + return true; } diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index f8082ad7e..a851e3a4c 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -35,6 +35,7 @@ wxDEFINE_EVENT(EVT_GLVIEWTOOLBAR_PREVIEW, SimpleEvent); const GLToolbarItem::ActionCallback GLToolbarItem::Default_Action_Callback = [](){}; const GLToolbarItem::VisibilityCallback GLToolbarItem::Default_Visibility_Callback = []()->bool { return true; }; const GLToolbarItem::EnabledStateCallback GLToolbarItem::Default_Enabled_State_Callback = []()->bool { return true; }; +const GLToolbarItem::RenderCallback GLToolbarItem::Default_Render_Callback = [](){}; GLToolbarItem::Data::Data() : name("") @@ -48,6 +49,7 @@ GLToolbarItem::Data::Data() , action_callback(Default_Action_Callback) , visibility_callback(Default_Visibility_Callback) , enabled_state_callback(Default_Enabled_State_Callback) + , render_callback(Default_Render_Callback) { } @@ -81,6 +83,8 @@ bool GLToolbarItem::update_enabled_state() void GLToolbarItem::render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const { GLTexture::render_sub_texture(tex_id, left, right, bottom, top, get_uvs(tex_width, tex_height, icon_size)); + + m_data.render_callback(); } GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 24314d60f..aa68fae38 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -37,6 +37,7 @@ public: typedef std::function ActionCallback; typedef std::function VisibilityCallback; typedef std::function EnabledStateCallback; + typedef std::function RenderCallback; enum EType : unsigned char { @@ -68,6 +69,7 @@ public: ActionCallback action_callback; VisibilityCallback visibility_callback; EnabledStateCallback enabled_state_callback; + RenderCallback render_callback; Data(); }; @@ -75,6 +77,7 @@ public: static const ActionCallback Default_Action_Callback; static const VisibilityCallback Default_Visibility_Callback; static const EnabledStateCallback Default_Enabled_State_Callback; + static const RenderCallback Default_Render_Callback; private: EType m_type; @@ -249,6 +252,7 @@ private: MouseCapture m_mouse_capture; std::string m_tooltip; + bool m_imgui_visible {false}; public: #if ENABLE_SVG_ICONS @@ -305,6 +309,9 @@ public: bool on_mouse(wxMouseEvent& evt, GLCanvas3D& parent); + void set_imgui_visible(bool visible = true) { m_imgui_visible = visible; } + bool get_imgui_visible() { return m_imgui_visible; } + private: void calc_layout() const; float get_width_horizontal() const; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index b6abf641a..cbcf33f77 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -342,6 +342,27 @@ bool ImGuiWrapper::combo(const wxString& label, const std::vector& return res; } +// Getter for the const char*[] +static bool StringGetter(void* data, int i, const char** out_text) +{ + const std::vector* v = (std::vector*)data; + if (out_text) + *out_text = (*v)[i].c_str(); + return true; +} + +bool ImGuiWrapper::multi_sel_list(const wxString& label, const std::vector& options, int& selection) +{ + // this is to force the label to the left of the widget: + if (!label.IsEmpty()) + text(label); + + bool res = false; + ImGui::ListBox("", &selection, StringGetter, (void*)&options, (int)options.size()); + + return res; +} + void ImGuiWrapper::disabled_begin(bool disabled) { wxCHECK_RET(!m_disabled, "ImGUI: Unbalanced disabled_begin() call"); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 0479e4743..42c562b72 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -67,6 +67,7 @@ public: void text(const std::string &label); void text(const wxString &label); bool combo(const wxString& label, const std::vector& options, int& selection); // Use -1 to not mark any option as selected + bool multi_sel_list(const wxString& label, const std::vector& options, int& selection); // Use -1 to not mark any option as selected void disabled_begin(bool disabled); void disabled_end(); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 67b336a0e..cca6e02e6 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -508,7 +508,7 @@ void MainFrame::init_menubar() "undo", nullptr, [this](){return m_plater->can_undo(); }, this); append_menu_item(editMenu, wxID_ANY, _(L("&Redo")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "Y", _(L("Redo")), [this](wxCommandEvent&) { m_plater->redo(); }, - "undo", nullptr, [this](){return m_plater->can_redo(); }, this); + "redo", nullptr, [this](){return m_plater->can_redo(); }, this); editMenu->AppendSeparator(); append_menu_item(editMenu, wxID_ANY, _(L("&Copy")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "C", From d4914441f3dc5038c1599fb92561502d260d05ab Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 9 Jul 2019 10:18:57 +0200 Subject: [PATCH 46/72] Modified logic to add snapshots to undo/redo stack using GLCanvas::do_xxxxxx() methods --- src/slic3r/GUI/GLCanvas3D.cpp | 41 ++++++++++++++++++----- src/slic3r/GUI/GLCanvas3D.hpp | 11 +++--- src/slic3r/GUI/GUI_ObjectManipulation.cpp | 14 +++----- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 12 +++---- src/slic3r/GUI/Selection.cpp | 6 ++-- 5 files changed, 50 insertions(+), 34 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 1d5cec04a..7a2e5345d 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1799,7 +1799,7 @@ std::vector GLCanvas3D::load_object(const Model& model, int obj_idx) void GLCanvas3D::mirror_selection(Axis axis) { m_selection.mirror(axis); - do_mirror(); + do_mirror("Mirror Object"); wxGetApp().obj_manipul()->set_dirty(); } @@ -2947,8 +2947,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) else if ((m_mouse.drag.move_volume_idx != -1) && m_mouse.dragging) { m_regenerate_volumes = false; - wxGetApp().plater()->take_snapshot(_(L("Move Object"))); - do_move(); + do_move("Move Object"); wxGetApp().obj_manipul()->set_dirty(); // Let the plater know that the dragging finished, so a delayed refresh // of the scene with the background processing data should be performed. @@ -3107,11 +3106,14 @@ void GLCanvas3D::set_tooltip(const std::string& tooltip) const } -void GLCanvas3D::do_move() +void GLCanvas3D::do_move(const std::string& snapshot_type) { if (m_model == nullptr) return; + if (!snapshot_type.empty()) + wxGetApp().plater()->take_snapshot(_(L(snapshot_type))); + std::set> done; // keeps track of modified instances bool object_moved = false; Vec3d wipe_tower_origin = Vec3d::Zero(); @@ -3162,13 +3164,18 @@ void GLCanvas3D::do_move() if (wipe_tower_origin != Vec3d::Zero()) post_event(Vec3dEvent(EVT_GLCANVAS_WIPETOWER_MOVED, std::move(wipe_tower_origin))); + + m_dirty = true; } -void GLCanvas3D::do_rotate() +void GLCanvas3D::do_rotate(const std::string& snapshot_type) { if (m_model == nullptr) return; + if (!snapshot_type.empty()) + wxGetApp().plater()->take_snapshot(_(L(snapshot_type))); + std::set> done; // keeps track of modified instances Selection::EMode selection_mode = m_selection.get_mode(); @@ -3217,13 +3224,18 @@ void GLCanvas3D::do_rotate() if (!done.empty()) post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED)); + + m_dirty = true; } -void GLCanvas3D::do_scale() +void GLCanvas3D::do_scale(const std::string& snapshot_type) { if (m_model == nullptr) return; + if (!snapshot_type.empty()) + wxGetApp().plater()->take_snapshot(_(L(snapshot_type))); + std::set> done; // keeps track of modified instances Selection::EMode selection_mode = m_selection.get_mode(); @@ -3269,18 +3281,27 @@ void GLCanvas3D::do_scale() if (!done.empty()) post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED)); + + m_dirty = true; } -void GLCanvas3D::do_flatten() +void GLCanvas3D::do_flatten(const Vec3d& normal, const std::string& snapshot_type) { - do_rotate(); + if (!snapshot_type.empty()) + wxGetApp().plater()->take_snapshot(_(L(snapshot_type))); + + m_selection.flattening_rotate(normal); + do_rotate(""); // avoid taking another snapshot } -void GLCanvas3D::do_mirror() +void GLCanvas3D::do_mirror(const std::string& snapshot_type) { if (m_model == nullptr) return; + if (!snapshot_type.empty()) + wxGetApp().plater()->take_snapshot(_(L(snapshot_type))); + std::set> done; // keeps track of modified instances Selection::EMode selection_mode = m_selection.get_mode(); @@ -3319,6 +3340,8 @@ void GLCanvas3D::do_mirror() } post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + + m_dirty = true; } void GLCanvas3D::set_camera_zoom(double zoom) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 47b1c5ec2..c1b6dce14 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -599,11 +599,12 @@ public: void set_tooltip(const std::string& tooltip) const; - void do_move(); - void do_rotate(); - void do_scale(); - void do_flatten(); - void do_mirror(); + // the following methods add a snapshot to the undo/redo stack, unless the given string is empty + void do_move(const std::string& snapshot_type); + void do_rotate(const std::string& snapshot_type); + void do_scale(const std::string& snapshot_type); + void do_flatten(const Vec3d& normal, const std::string& snapshot_type); + void do_mirror(const std::string& snapshot_type); void set_camera_zoom(double zoom); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index ec5272a44..787c92451 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -220,8 +220,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL); selection.synchronize_unselected_volumes(); // Copy mirroring values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing. - canvas->do_mirror(); - canvas->set_as_dirty(); + canvas->do_mirror("Set Mirror"); UpdateAndShow(true); }); return sizer; @@ -302,8 +301,7 @@ ObjectManipulation::ObjectManipulation(wxWindow* parent) : selection.synchronize_unselected_instances(Selection::SYNC_ROTATION_GENERAL); selection.synchronize_unselected_volumes(); // Copy rotation values from GLVolumes into Model (ModelInstance / ModelVolume), trigger background processing. - wxGetApp().plater()->take_snapshot(_(L("Set Rotation"))); - canvas->do_rotate(); + canvas->do_rotate("Set Rotation"); UpdateAndShow(true); }); @@ -656,8 +654,7 @@ void ObjectManipulation::change_position_value(int axis, double value) Selection& selection = canvas->get_selection(); selection.start_dragging(); selection.translate(position - m_cache.position, selection.requires_local_axes()); - wxGetApp().plater()->take_snapshot(_(L("Set Position"))); - canvas->do_move(); + canvas->do_move("Set Position"); m_cache.position = position; m_cache.position_rounded(axis) = DBL_MAX; @@ -688,8 +685,7 @@ void ObjectManipulation::change_rotation_value(int axis, double value) selection.rotate( (M_PI / 180.0) * (transformation_type.absolute() ? rotation : rotation - m_cache.rotation), transformation_type); - wxGetApp().plater()->take_snapshot(_(L("Set Orientation"))); - canvas->do_rotate(); + canvas->do_rotate("Set Orientation"); m_cache.rotation = rotation; m_cache.rotation_rounded(axis) = DBL_MAX; @@ -754,7 +750,7 @@ void ObjectManipulation::do_scale(int axis, const Vec3d &scale) const selection.start_dragging(); selection.scale(scaling_factor * 0.01, transformation_type); - wxGetApp().plater()->canvas3D()->do_scale(); + wxGetApp().plater()->canvas3D()->do_scale("Set Scale"); } void ObjectManipulation::on_change(t_config_option_key opt_key, const boost::any& value) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 438cd1a10..23f3cc6c3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -600,9 +600,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas) if (m_current == Flatten) { // Rotate the object so the normal points downward: - wxGetApp().plater()->take_snapshot(_(L("Place on Face"))); - selection.flattening_rotate(get_flattening_normal()); - canvas.do_flatten(); + canvas.do_flatten(get_flattening_normal(), "Place on Face"); wxGetApp().obj_manipul()->set_dirty(); } @@ -674,20 +672,18 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas) { case Move: { - wxGetApp().plater()->take_snapshot(_(L("Move Object"))); canvas.disable_regenerate_volumes(); - canvas.do_move(); + canvas.do_move("Gizmo-Move Object"); break; } case Scale: { - canvas.do_scale(); + canvas.do_scale("Gizmo-Scale Object"); break; } case Rotate: { - wxGetApp().plater()->take_snapshot(_(L("Rotate Object"))); - canvas.do_rotate(); + canvas.do_rotate("Gizmo-Rotate Object"); break; } default: diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index a71005cf2..997146546 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -806,7 +806,7 @@ void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config) double s = std::min(sx, std::min(sy, sz)); if (s != 1.0) { - wxGetApp().plater()->take_snapshot(_(L("Scale To Fit"))); + wxGetApp().plater()->take_snapshot(_(L("Scale To Fit"))); TransformationType type; type.set_world(); @@ -816,12 +816,12 @@ void Selection::scale_to_fit_print_volume(const DynamicPrintConfig& config) // apply scale start_dragging(); scale(s * Vec3d::Ones(), type); - wxGetApp().plater()->canvas3D()->do_scale(); + wxGetApp().plater()->canvas3D()->do_scale(""); // avoid storing another snapshot // center selection on print bed start_dragging(); translate(print_volume.center() - get_bounding_box().center()); - wxGetApp().plater()->canvas3D()->do_move(); + wxGetApp().plater()->canvas3D()->do_move(""); // avoid storing another snapshot wxGetApp().obj_manipul()->set_dirty(); } From 1347e655c21ec665621e0929373e5e211b5610a4 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 9 Jul 2019 15:27:28 +0200 Subject: [PATCH 47/72] Next improvements of an undo/redo from a toolbar --- src/slic3r/GUI/GLCanvas3D.cpp | 84 +++++++++++++++++++++------------ src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/GLToolbar.cpp | 4 +- src/slic3r/GUI/GLToolbar.hpp | 24 ++++++++-- src/slic3r/GUI/ImGuiWrapper.cpp | 35 ++++++++------ src/slic3r/GUI/ImGuiWrapper.hpp | 2 +- src/slic3r/GUI/Plater.cpp | 15 ++++++ src/slic3r/GUI/Plater.hpp | 1 + 8 files changed, 113 insertions(+), 53 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 970b349cb..e362cf74d 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3442,6 +3442,41 @@ bool GLCanvas3D::_is_shown_on_screen() const return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; } +// Getter for the const char*[] +static bool string_getter(const bool is_undo, int idx, const char** out_text) +{ + return wxGetApp().plater()->undo_redo_string_getter(is_undo, idx, out_text); +} + +void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) +{ + if (m_canvas != nullptr && m_toolbar.get_imgui_visible(is_undo)) + { + const wxString& stack_name = _(is_undo ? L("Undo") : L("Redo")); + ImGuiWrapper* imgui = wxGetApp().imgui(); + + const float x = pos_x * (float)get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width(); + imgui->set_next_window_pos(x, m_toolbar.get_height(), ImGuiCond_Always); + + imgui->set_next_window_bg_alpha(0.5f); + imgui->begin(wxString::Format(_(L("%s Stack")), stack_name), + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + + int hovered = m_toolbar.get_imgui_hovered_pos(); + int selected = -1; + const float em = static_cast(wxGetApp().em_unit()); + + if (imgui->undo_redo_list(ImVec2(12 * em, 20 * em), is_undo, &string_getter, hovered, selected)) + m_toolbar.set_imgui_hovered_pos(hovered); + if (selected >= 0) + m_toolbar.hide_imgui(is_undo); + + imgui->text(wxString::Format(_(L("%s %d Action")), stack_name, hovered + 1)); + + imgui->end(); + } +} + bool GLCanvas3D::_init_toolbar() { if (!m_toolbar.is_enabled()) @@ -3627,37 +3662,19 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Undo")) + " [" + GUI::shortkey_ctrl_prefix() + "Z]"; item.sprite_id = 11; - item.action_callback = [this]() - { + item.is_toggable = false; + item.action_callback = [this]() { if (m_canvas != nullptr) { wxPostEvent(m_canvas, SimpleEvent(EVT_GLCANVAS_UNDO)); - m_toolbar.set_imgui_visible(); + m_toolbar.activate_imgui(true); } }; item.visibility_callback = []()->bool { return true; }; - item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_undo(); }; - item.render_callback = [this]() - { - if (m_canvas != nullptr && m_toolbar.get_imgui_visible()) { - ImGuiWrapper* imgui = wxGetApp().imgui(); - - const float approx_height = m_toolbar.get_height(); - imgui->set_next_window_pos(600, approx_height, ImGuiCond_Always); - - imgui->set_next_window_bg_alpha(0.5f); - imgui->begin(_(L("Undo Stack")), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - - std::vector undo_stack = {"A", "B", "C", "D","A", "B", "C", "D","A", "B", "C", "D",}; - int sel = 4; - imgui->multi_sel_list("", undo_stack, sel); - - const bool undo_clicked = imgui->button(_(L("Undo N Action"))); - - imgui->end(); - if (undo_clicked) - m_toolbar.set_imgui_visible(false); - } + item.enabled_state_callback = [this]()->bool { + if (!wxGetApp().plater()->can_undo()) { m_toolbar.hide_imgui(true); return false; } + return true; }; + item.render_callback = [this](float pos_x, float, float, float) { _render_undo_redo_stack(true, pos_x); }; if (!m_toolbar.add_item(item)) return false; @@ -3667,15 +3684,20 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Redo")) + " [" + GUI::shortkey_ctrl_prefix() + "Y]"; item.sprite_id = 12; - item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLCANVAS_REDO)); }; - item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_redo(); }; - item.render_callback = []() {}; + item.action_callback = [this]() { + if (m_canvas != nullptr) { + wxPostEvent(m_canvas, SimpleEvent(EVT_GLCANVAS_REDO)); + m_toolbar.activate_imgui(false); + } + }; + item.enabled_state_callback = [this]()->bool { + if (!wxGetApp().plater()->can_redo()) { m_toolbar.hide_imgui(false); return false; } + return true; + }; + item.render_callback = [this](float pos_x, float, float, float) { _render_undo_redo_stack(false, pos_x); }; if (!m_toolbar.add_item(item)) return false; - if (!m_toolbar.add_separator()) - return false; - return true; } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 47b1c5ec2..4867f94ce 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -676,6 +676,7 @@ private: #endif // ENABLE_SHOW_CAMERA_TARGET void _render_sla_slices() const; void _render_selection_sidebar_hints() const; + void _render_undo_redo_stack(const bool is_undo, float pos_x); void _update_volumes_hover_state() const; diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index a851e3a4c..f6140464b 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -35,7 +35,7 @@ wxDEFINE_EVENT(EVT_GLVIEWTOOLBAR_PREVIEW, SimpleEvent); const GLToolbarItem::ActionCallback GLToolbarItem::Default_Action_Callback = [](){}; const GLToolbarItem::VisibilityCallback GLToolbarItem::Default_Visibility_Callback = []()->bool { return true; }; const GLToolbarItem::EnabledStateCallback GLToolbarItem::Default_Enabled_State_Callback = []()->bool { return true; }; -const GLToolbarItem::RenderCallback GLToolbarItem::Default_Render_Callback = [](){}; +const GLToolbarItem::RenderCallback GLToolbarItem::Default_Render_Callback = [](float, float, float, float){}; GLToolbarItem::Data::Data() : name("") @@ -84,7 +84,7 @@ void GLToolbarItem::render(unsigned int tex_id, float left, float right, float b { GLTexture::render_sub_texture(tex_id, left, right, bottom, top, get_uvs(tex_width, tex_height, icon_size)); - m_data.render_callback(); + m_data.render_callback(left, right, bottom, top); } GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index aa68fae38..e32e4a41e 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -37,7 +37,7 @@ public: typedef std::function ActionCallback; typedef std::function VisibilityCallback; typedef std::function EnabledStateCallback; - typedef std::function RenderCallback; + typedef std::function RenderCallback; enum EType : unsigned char { @@ -252,7 +252,10 @@ private: MouseCapture m_mouse_capture; std::string m_tooltip; - bool m_imgui_visible {false}; + bool m_undo_imgui_visible {false}; + bool m_redo_imgui_visible {false}; + int m_imgui_hovered_pos { -1 }; + int m_imgui_selected_pos { -1 }; public: #if ENABLE_SVG_ICONS @@ -309,8 +312,21 @@ public: bool on_mouse(wxMouseEvent& evt, GLCanvas3D& parent); - void set_imgui_visible(bool visible = true) { m_imgui_visible = visible; } - bool get_imgui_visible() { return m_imgui_visible; } + // undo == true => "undo" imgui is activated + // undo == false => "redo" imgui is activated + bool get_imgui_visible(const bool undo) const { return undo ? m_undo_imgui_visible : m_redo_imgui_visible; } + void hide_imgui(const bool undo) { undo ? m_undo_imgui_visible = false : m_redo_imgui_visible = false; } + void activate_imgui(const bool undo) { + m_undo_imgui_visible = undo; + m_redo_imgui_visible = !undo; + m_imgui_hovered_pos = m_imgui_selected_pos = -1; + } + + void set_imgui_hovered_pos(int pos = -1) { m_imgui_hovered_pos = pos; } + int get_imgui_hovered_pos() const { return m_imgui_hovered_pos; } + + void set_imgui_selected_pos(int pos = -1) { m_imgui_selected_pos = pos; } + int get_imgui_selected_pos() const { return m_imgui_selected_pos; } private: void calc_layout() const; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index cbcf33f77..7f4a6c10c 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -342,25 +342,30 @@ bool ImGuiWrapper::combo(const wxString& label, const std::vector& return res; } -// Getter for the const char*[] -static bool StringGetter(void* data, int i, const char** out_text) +bool ImGuiWrapper::undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool , int , const char**), int& hovered, int& selected) { - const std::vector* v = (std::vector*)data; - if (out_text) - *out_text = (*v)[i].c_str(); - return true; -} + bool is_hovered = false; + ImGui::ListBoxHeader("", size); -bool ImGuiWrapper::multi_sel_list(const wxString& label, const std::vector& options, int& selection) -{ - // this is to force the label to the left of the widget: - if (!label.IsEmpty()) - text(label); + int i=0; + const char* item_text; + while (items_getter(is_undo, i, &item_text)) + { + ImGui::Selectable(item_text, i < hovered); - bool res = false; - ImGui::ListBox("", &selection, StringGetter, (void*)&options, (int)options.size()); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip(item_text); + hovered = i; + is_hovered = true; + } - return res; + if (ImGui::IsItemClicked()) + selected = i; + i++; + } + + ImGui::ListBoxFooter(); + return is_hovered; } void ImGuiWrapper::disabled_begin(bool disabled) diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 42c562b72..a18b15184 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -67,7 +67,7 @@ public: void text(const std::string &label); void text(const wxString &label); bool combo(const wxString& label, const std::vector& options, int& selection); // Use -1 to not mark any option as selected - bool multi_sel_list(const wxString& label, const std::vector& options, int& selection); // Use -1 to not mark any option as selected + bool undo_redo_list(const ImVec2& size, const bool is_undo, bool (*items_getter)(const bool, int, const char**), int& hovered, int& selected); void disabled_begin(bool disabled); void disabled_end(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 6ac58eb84..3dfc1d1b8 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4110,6 +4110,21 @@ void Plater::take_snapshot(const std::string &snapshot_name) { p->take_snapshot( void Plater::take_snapshot(const wxString &snapshot_name) { p->take_snapshot(snapshot_name); } void Plater::undo() { p->undo(); } void Plater::redo() { p->redo(); } +bool Plater::undo_redo_string_getter(const bool is_undo, int idx, const char** out_text) +{ + const size_t& active_snapshot_time = p->undo_redo_stack.active_snapshot_time(); + const std::vector& ss_stack = p->undo_redo_stack.snapshots(); + const auto it = std::lower_bound(ss_stack.begin(), ss_stack.end(), UndoRedo::Snapshot(active_snapshot_time)); + + const int idx_in_ss_stack = it - ss_stack.begin() + (is_undo ? -(++idx) : idx); + + if (0 < idx_in_ss_stack && idx_in_ss_stack < ss_stack.size() - 1) { + *out_text = ss_stack[idx_in_ss_stack].name.c_str(); + return true; + } + + return false; +} void Plater::on_extruders_change(int num_extruders) { diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 0be465f53..7f379c638 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -187,6 +187,7 @@ public: void take_snapshot(const wxString &snapshot_name); void undo(); void redo(); + bool undo_redo_string_getter(const bool is_undo, int idx, const char** out_text); void on_extruders_change(int extruders_count); void on_config_change(const DynamicPrintConfig &config); From f985f5190c8260b28b1a3d06d20138ee16113dad Mon Sep 17 00:00:00 2001 From: YuSanka Date: Tue, 9 Jul 2019 20:45:00 +0200 Subject: [PATCH 48/72] Completed undo/redo from a toolbar --- src/slic3r/GUI/GLCanvas3D.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 48 ++++++++++++++++++++++++++++++++--- src/slic3r/GUI/Plater.hpp | 2 ++ 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index dc59f4e5b..d87b97f53 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3492,7 +3492,7 @@ void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) if (imgui->undo_redo_list(ImVec2(12 * em, 20 * em), is_undo, &string_getter, hovered, selected)) m_toolbar.set_imgui_hovered_pos(hovered); if (selected >= 0) - m_toolbar.hide_imgui(is_undo); + is_undo ? wxGetApp().plater()->undo_to(selected) : wxGetApp().plater()->redo_to(selected); imgui->text(wxString::Format(_(L("%s %d Action")), stack_name, hovered + 1)); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 3dfc1d1b8..a450ac0e5 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1583,8 +1583,11 @@ struct Plater::priv void take_snapshot(const std::string& snapshot_name) { this->undo_redo_stack.take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection()); } void take_snapshot(const wxString& snapshot_name) { this->take_snapshot(std::string(snapshot_name.ToUTF8().data())); } + int get_active_snapshot_index(); void undo(); void redo(); + void undo_to(size_t time_to_load); + void redo_to(size_t time_to_load); bool background_processing_enabled() const { return this->get_config("background_processing") == "1"; } void update_print_volume_state(); @@ -3560,6 +3563,14 @@ void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const } } +int Plater::priv::get_active_snapshot_index() +{ + const size_t& active_snapshot_time = this->undo_redo_stack.active_snapshot_time(); + const std::vector& ss_stack = this->undo_redo_stack.snapshots(); + const auto it = std::lower_bound(ss_stack.begin(), ss_stack.end(), UndoRedo::Snapshot(active_snapshot_time)); + return it - ss_stack.begin(); +} + void Plater::priv::undo() { if (this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection())) @@ -3572,6 +3583,18 @@ void Plater::priv::redo() this->update_after_undo_redo(); } +void Plater::priv::undo_to(size_t time_to_load) +{ + if (this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection(), time_to_load)) + this->update_after_undo_redo(); +} + +void Plater::priv::redo_to(size_t time_to_load) +{ + if (this->undo_redo_stack.redo(model, time_to_load)) + this->update_after_undo_redo(); +} + void Plater::priv::update_after_undo_redo() { this->view3D->get_canvas3d()->get_selection().clear(); @@ -4110,13 +4133,30 @@ void Plater::take_snapshot(const std::string &snapshot_name) { p->take_snapshot( void Plater::take_snapshot(const wxString &snapshot_name) { p->take_snapshot(snapshot_name); } void Plater::undo() { p->undo(); } void Plater::redo() { p->redo(); } +void Plater::undo_to(int selection) +{ + if (selection == 0) { + p->undo(); + return; + } + + const int idx = p->get_active_snapshot_index() - selection - 1; + p->undo_to(p->undo_redo_stack.snapshots()[idx].timestamp); +} +void Plater::redo_to(int selection) +{ + if (selection == 0) { + p->redo(); + return; + } + + const int idx = selection + p->get_active_snapshot_index(); + p->redo_to(p->undo_redo_stack.snapshots()[idx].timestamp); +} bool Plater::undo_redo_string_getter(const bool is_undo, int idx, const char** out_text) { - const size_t& active_snapshot_time = p->undo_redo_stack.active_snapshot_time(); const std::vector& ss_stack = p->undo_redo_stack.snapshots(); - const auto it = std::lower_bound(ss_stack.begin(), ss_stack.end(), UndoRedo::Snapshot(active_snapshot_time)); - - const int idx_in_ss_stack = it - ss_stack.begin() + (is_undo ? -(++idx) : idx); + const int idx_in_ss_stack = p->get_active_snapshot_index() + (is_undo ? -(++idx) : idx); if (0 < idx_in_ss_stack && idx_in_ss_stack < ss_stack.size() - 1) { *out_text = ss_stack[idx_in_ss_stack].name.c_str(); diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 7f379c638..ca3d59224 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -187,6 +187,8 @@ public: void take_snapshot(const wxString &snapshot_name); void undo(); void redo(); + void undo_to(int selection); + void redo_to(int selection); bool undo_redo_string_getter(const bool is_undo, int idx, const char** out_text); void on_extruders_change(int extruders_count); From 3720e6a3a3ec59fca0f8781139e5d329156f81e6 Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 10 Jul 2019 10:15:07 +0200 Subject: [PATCH 49/72] Fixed redo_to() function and code cleaning from redundant options --- src/slic3r/GUI/GLCanvas3D.cpp | 66 +++++++++++++---------------------- src/slic3r/GUI/GLToolbar.cpp | 3 +- src/slic3r/GUI/GLToolbar.hpp | 16 --------- src/slic3r/GUI/Plater.cpp | 2 +- 4 files changed, 27 insertions(+), 60 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index d87b97f53..4fa5b8b27 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3473,31 +3473,31 @@ static bool string_getter(const bool is_undo, int idx, const char** out_text) void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) { - if (m_canvas != nullptr && m_toolbar.get_imgui_visible(is_undo)) - { - const wxString& stack_name = _(is_undo ? L("Undo") : L("Redo")); - ImGuiWrapper* imgui = wxGetApp().imgui(); + const wxString& stack_name = _(is_undo ? L("Undo") : L("Redo")); + ImGuiWrapper* imgui = wxGetApp().imgui(); - const float x = pos_x * (float)get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width(); - imgui->set_next_window_pos(x, m_toolbar.get_height(), ImGuiCond_Always); + const float x = pos_x * (float)get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width(); + imgui->set_next_window_pos(x, m_toolbar.get_height(), ImGuiCond_Always); - imgui->set_next_window_bg_alpha(0.5f); - imgui->begin(wxString::Format(_(L("%s Stack")), stack_name), - ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + imgui->set_next_window_bg_alpha(0.5f); + imgui->begin(wxString::Format(_(L("%s Stack")), stack_name), + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - int hovered = m_toolbar.get_imgui_hovered_pos(); - int selected = -1; - const float em = static_cast(wxGetApp().em_unit()); + int hovered = m_toolbar.get_imgui_hovered_pos(); + int selected = -1; + const float em = static_cast(wxGetApp().em_unit()); - if (imgui->undo_redo_list(ImVec2(12 * em, 20 * em), is_undo, &string_getter, hovered, selected)) - m_toolbar.set_imgui_hovered_pos(hovered); - if (selected >= 0) - is_undo ? wxGetApp().plater()->undo_to(selected) : wxGetApp().plater()->redo_to(selected); + if (imgui->undo_redo_list(ImVec2(12 * em, 20 * em), is_undo, &string_getter, hovered, selected)) + m_toolbar.set_imgui_hovered_pos(hovered); + else + m_toolbar.set_imgui_hovered_pos(-1); - imgui->text(wxString::Format(_(L("%s %d Action")), stack_name, hovered + 1)); + if (selected >= 0) + is_undo ? wxGetApp().plater()->undo_to(selected) : wxGetApp().plater()->redo_to(selected); - imgui->end(); - } + imgui->text(wxString::Format(_(L("%s %d Action")), stack_name, hovered + 1)); + + imgui->end(); } bool GLCanvas3D::_init_toolbar() @@ -3685,19 +3685,10 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Undo")) + " [" + GUI::shortkey_ctrl_prefix() + "Z]"; item.sprite_id = 11; - item.is_toggable = false; - item.action_callback = [this]() { - if (m_canvas != nullptr) { - wxPostEvent(m_canvas, SimpleEvent(EVT_GLCANVAS_UNDO)); - m_toolbar.activate_imgui(true); - } - }; + item.action_callback = [this]() { if (m_canvas != nullptr) m_toolbar.set_imgui_hovered_pos(-1); }; item.visibility_callback = []()->bool { return true; }; - item.enabled_state_callback = [this]()->bool { - if (!wxGetApp().plater()->can_undo()) { m_toolbar.hide_imgui(true); return false; } - return true; - }; - item.render_callback = [this](float pos_x, float, float, float) { _render_undo_redo_stack(true, pos_x); }; + item.enabled_state_callback = [this]()->bool { return wxGetApp().plater()->can_undo(); } ; + item.render_callback = [this](float pos_x, float, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(true, pos_x); }; if (!m_toolbar.add_item(item)) return false; @@ -3707,17 +3698,8 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Redo")) + " [" + GUI::shortkey_ctrl_prefix() + "Y]"; item.sprite_id = 12; - item.action_callback = [this]() { - if (m_canvas != nullptr) { - wxPostEvent(m_canvas, SimpleEvent(EVT_GLCANVAS_REDO)); - m_toolbar.activate_imgui(false); - } - }; - item.enabled_state_callback = [this]()->bool { - if (!wxGetApp().plater()->can_redo()) { m_toolbar.hide_imgui(false); return false; } - return true; - }; - item.render_callback = [this](float pos_x, float, float, float) { _render_undo_redo_stack(false, pos_x); }; + item.enabled_state_callback = [this]()->bool { return wxGetApp().plater()->can_redo(); }; + item.render_callback = [this](float pos_x, float, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(false, pos_x); }; if (!m_toolbar.add_item(item)) return false; diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index f6140464b..e78605333 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -84,7 +84,8 @@ void GLToolbarItem::render(unsigned int tex_id, float left, float right, float b { GLTexture::render_sub_texture(tex_id, left, right, bottom, top, get_uvs(tex_width, tex_height, icon_size)); - m_data.render_callback(left, right, bottom, top); + if (is_pressed()) + m_data.render_callback(left, right, bottom, top); } GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index e32e4a41e..78dd56081 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -252,10 +252,7 @@ private: MouseCapture m_mouse_capture; std::string m_tooltip; - bool m_undo_imgui_visible {false}; - bool m_redo_imgui_visible {false}; int m_imgui_hovered_pos { -1 }; - int m_imgui_selected_pos { -1 }; public: #if ENABLE_SVG_ICONS @@ -312,22 +309,9 @@ public: bool on_mouse(wxMouseEvent& evt, GLCanvas3D& parent); - // undo == true => "undo" imgui is activated - // undo == false => "redo" imgui is activated - bool get_imgui_visible(const bool undo) const { return undo ? m_undo_imgui_visible : m_redo_imgui_visible; } - void hide_imgui(const bool undo) { undo ? m_undo_imgui_visible = false : m_redo_imgui_visible = false; } - void activate_imgui(const bool undo) { - m_undo_imgui_visible = undo; - m_redo_imgui_visible = !undo; - m_imgui_hovered_pos = m_imgui_selected_pos = -1; - } - void set_imgui_hovered_pos(int pos = -1) { m_imgui_hovered_pos = pos; } int get_imgui_hovered_pos() const { return m_imgui_hovered_pos; } - void set_imgui_selected_pos(int pos = -1) { m_imgui_selected_pos = pos; } - int get_imgui_selected_pos() const { return m_imgui_selected_pos; } - private: void calc_layout() const; float get_width_horizontal() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index a450ac0e5..d8825ab3a 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4150,7 +4150,7 @@ void Plater::redo_to(int selection) return; } - const int idx = selection + p->get_active_snapshot_index(); + const int idx = p->get_active_snapshot_index() + selection + 1; p->redo_to(p->undo_redo_stack.snapshots()[idx].timestamp); } bool Plater::undo_redo_string_getter(const bool is_undo, int idx, const char** out_text) From 46e295407b3c568e39cebb79dc8b3c3dbca386c3 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 10 Jul 2019 10:52:12 +0200 Subject: [PATCH 50/72] Modified toolbar to call RenderCallback only when the item is toggable and pressed --- src/slic3r/GUI/GLCanvas3D.cpp | 40 ++++++------------ src/slic3r/GUI/GLToolbar.cpp | 79 ++++++++++++++++++++++------------- src/slic3r/GUI/GLToolbar.hpp | 23 ++-------- 3 files changed, 65 insertions(+), 77 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index d87b97f53..4c9f70019 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3473,9 +3473,9 @@ static bool string_getter(const bool is_undo, int idx, const char** out_text) void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) { - if (m_canvas != nullptr && m_toolbar.get_imgui_visible(is_undo)) + if (m_canvas != nullptr) { - const wxString& stack_name = _(is_undo ? L("Undo") : L("Redo")); + const wxString stack_name = _(is_undo ? L("Undo") : L("Redo")); ImGuiWrapper* imgui = wxGetApp().imgui(); const float x = pos_x * (float)get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width(); @@ -3483,16 +3483,17 @@ void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) imgui->set_next_window_bg_alpha(0.5f); imgui->begin(wxString::Format(_(L("%s Stack")), stack_name), - ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); + ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - int hovered = m_toolbar.get_imgui_hovered_pos(); + int hovered = -1; int selected = -1; const float em = static_cast(wxGetApp().em_unit()); if (imgui->undo_redo_list(ImVec2(12 * em, 20 * em), is_undo, &string_getter, hovered, selected)) - m_toolbar.set_imgui_hovered_pos(hovered); - if (selected >= 0) - is_undo ? wxGetApp().plater()->undo_to(selected) : wxGetApp().plater()->redo_to(selected); + { + if (selected >= 0) + is_undo ? wxGetApp().plater()->undo_to(selected) : wxGetApp().plater()->redo_to(selected); + } imgui->text(wxString::Format(_(L("%s %d Action")), stack_name, hovered + 1)); @@ -3685,18 +3686,9 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Undo")) + " [" + GUI::shortkey_ctrl_prefix() + "Z]"; item.sprite_id = 11; - item.is_toggable = false; - item.action_callback = [this]() { - if (m_canvas != nullptr) { - wxPostEvent(m_canvas, SimpleEvent(EVT_GLCANVAS_UNDO)); - m_toolbar.activate_imgui(true); - } - }; + item.action_callback = [this]() { if (m_canvas != nullptr) { wxPostEvent(m_canvas, SimpleEvent(EVT_GLCANVAS_UNDO)); } }; item.visibility_callback = []()->bool { return true; }; - item.enabled_state_callback = [this]()->bool { - if (!wxGetApp().plater()->can_undo()) { m_toolbar.hide_imgui(true); return false; } - return true; - }; + item.enabled_state_callback = [this]()->bool { return wxGetApp().plater()->can_undo(); }; item.render_callback = [this](float pos_x, float, float, float) { _render_undo_redo_stack(true, pos_x); }; if (!m_toolbar.add_item(item)) return false; @@ -3707,16 +3699,8 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Redo")) + " [" + GUI::shortkey_ctrl_prefix() + "Y]"; item.sprite_id = 12; - item.action_callback = [this]() { - if (m_canvas != nullptr) { - wxPostEvent(m_canvas, SimpleEvent(EVT_GLCANVAS_REDO)); - m_toolbar.activate_imgui(false); - } - }; - item.enabled_state_callback = [this]()->bool { - if (!wxGetApp().plater()->can_redo()) { m_toolbar.hide_imgui(false); return false; } - return true; - }; + item.action_callback = [this]() { if (m_canvas != nullptr) { wxPostEvent(m_canvas, SimpleEvent(EVT_GLCANVAS_REDO)); } }; + item.enabled_state_callback = [this]()->bool { return wxGetApp().plater()->can_redo(); }; item.render_callback = [this](float pos_x, float, float, float) { _render_undo_redo_stack(false, pos_x); }; if (!m_toolbar.add_item(item)) return false; diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index f6140464b..78e10c4f1 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -84,7 +84,8 @@ void GLToolbarItem::render(unsigned int tex_id, float left, float right, float b { GLTexture::render_sub_texture(tex_id, left, right, bottom, top, get_uvs(tex_width, tex_height, icon_size)); - m_data.render_callback(left, right, bottom, top); + if (is_toggable() && is_pressed()) + m_data.render_callback(left, right, bottom, top); } GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const @@ -164,6 +165,7 @@ GLToolbar::GLToolbar(GLToolbar::EType type) , m_icons_texture_dirty(true) #endif // ENABLE_SVG_ICONS , m_tooltip("") + , m_pressed_toggable_id(-1) { } @@ -345,7 +347,7 @@ void GLToolbar::select_item(const std::string& name) bool GLToolbar::is_item_pressed(const std::string& name) const { - for (GLToolbarItem* item : m_items) + for (const GLToolbarItem* item : m_items) { if (item->get_name() == name) return item->is_pressed(); @@ -356,7 +358,7 @@ bool GLToolbar::is_item_pressed(const std::string& name) const bool GLToolbar::is_item_disabled(const std::string& name) const { - for (GLToolbarItem* item : m_items) + for (const GLToolbarItem* item : m_items) { if (item->get_name() == name) return item->is_disabled(); @@ -367,7 +369,7 @@ bool GLToolbar::is_item_disabled(const std::string& name) const bool GLToolbar::is_item_visible(const std::string& name) const { - for (GLToolbarItem* item : m_items) + for (const GLToolbarItem* item : m_items) { if (item->get_name() == name) return item->is_visible(); @@ -376,11 +378,25 @@ bool GLToolbar::is_item_visible(const std::string& name) const return false; } +bool GLToolbar::is_any_item_pressed() const +{ + for (const GLToolbarItem* item : m_items) + { + if (item->is_pressed()) + return true; + } + + return false; +} + bool GLToolbar::update_items_state() { bool ret = false; ret |= update_items_visibility(); ret |= update_items_enabled_state(); + if (!is_any_item_pressed()) + m_pressed_toggable_id = -1; + return ret; } @@ -558,36 +574,41 @@ float GLToolbar::get_main_size() const void GLToolbar::do_action(unsigned int item_id, GLCanvas3D& parent) { - if (item_id < (unsigned int)m_items.size()) + if ((m_pressed_toggable_id == -1) || (m_pressed_toggable_id == item_id)) { - GLToolbarItem* item = m_items[item_id]; - if ((item != nullptr) && !item->is_separator() && item->is_hovered()) + if (item_id < (unsigned int)m_items.size()) { - if (item->is_toggable()) + GLToolbarItem* item = m_items[item_id]; + if ((item != nullptr) && !item->is_separator() && item->is_hovered()) { - GLToolbarItem::EState state = item->get_state(); - if (state == GLToolbarItem::Hover) - item->set_state(GLToolbarItem::HoverPressed); - else if (state == GLToolbarItem::HoverPressed) - item->set_state(GLToolbarItem::Hover); - - parent.render(); - item->do_action(); - } - else - { - if (m_type == Radio) - select_item(item->get_name()); - else - item->set_state(GLToolbarItem::HoverPressed); - - parent.render(); - item->do_action(); - if ((m_type == Normal) && (item->get_state() != GLToolbarItem::Disabled)) + if (item->is_toggable()) { - // the item may get disabled during the action, if not, set it back to hover state - item->set_state(GLToolbarItem::Hover); + GLToolbarItem::EState state = item->get_state(); + if (state == GLToolbarItem::Hover) + item->set_state(GLToolbarItem::HoverPressed); + else if (state == GLToolbarItem::HoverPressed) + item->set_state(GLToolbarItem::Hover); + + m_pressed_toggable_id = item->is_pressed() ? item_id : -1; + parent.render(); + item->do_action(); + } + else + { + if (m_type == Radio) + select_item(item->get_name()); + else + item->set_state(GLToolbarItem::HoverPressed); + + parent.render(); + item->do_action(); + if ((m_type == Normal) && (item->get_state() != GLToolbarItem::Disabled)) + { + // the item may get disabled during the action, if not, set it back to hover state + item->set_state(GLToolbarItem::Hover); + parent.render(); + } } } } diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index e32e4a41e..de19296a5 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -252,10 +252,7 @@ private: MouseCapture m_mouse_capture; std::string m_tooltip; - bool m_undo_imgui_visible {false}; - bool m_redo_imgui_visible {false}; - int m_imgui_hovered_pos { -1 }; - int m_imgui_selected_pos { -1 }; + unsigned int m_pressed_toggable_id; public: #if ENABLE_SVG_ICONS @@ -302,6 +299,8 @@ public: bool is_item_disabled(const std::string& name) const; bool is_item_visible(const std::string& name) const; + bool is_any_item_pressed() const; + const std::string& get_tooltip() const { return m_tooltip; } @@ -312,22 +311,6 @@ public: bool on_mouse(wxMouseEvent& evt, GLCanvas3D& parent); - // undo == true => "undo" imgui is activated - // undo == false => "redo" imgui is activated - bool get_imgui_visible(const bool undo) const { return undo ? m_undo_imgui_visible : m_redo_imgui_visible; } - void hide_imgui(const bool undo) { undo ? m_undo_imgui_visible = false : m_redo_imgui_visible = false; } - void activate_imgui(const bool undo) { - m_undo_imgui_visible = undo; - m_redo_imgui_visible = !undo; - m_imgui_hovered_pos = m_imgui_selected_pos = -1; - } - - void set_imgui_hovered_pos(int pos = -1) { m_imgui_hovered_pos = pos; } - int get_imgui_hovered_pos() const { return m_imgui_hovered_pos; } - - void set_imgui_selected_pos(int pos = -1) { m_imgui_selected_pos = pos; } - int get_imgui_selected_pos() const { return m_imgui_selected_pos; } - private: void calc_layout() const; float get_width_horizontal() const; From 99df9f56c4a68660d819180caa15f538b7341c5d Mon Sep 17 00:00:00 2001 From: YuSanka Date: Wed, 10 Jul 2019 11:28:11 +0200 Subject: [PATCH 51/72] Added take_snapshot() for adding of settings --- src/slic3r/GUI/GUI_ObjectList.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 90c3f2665..c11865cac 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1115,6 +1115,8 @@ void ObjectList::get_settings_choice(const wxString& category_name) } #endif + take_snapshot(wxString::Format(_(L("Add Settings for %s")), is_part ? _(L("Sub-object")) : _(L("Object")))); + std::vector selected_options; selected_options.reserve(selection_cnt); for (auto sel : selections) @@ -1165,6 +1167,8 @@ void ObjectList::get_freq_settings_choice(const wxString& bundle_name) assert(m_config); auto opt_keys = m_config->keys(); + take_snapshot(wxString::Format(_(L("Add Settings Bundle for %s")), m_objects_model->GetItemType(GetSelection()) & itObject ? _(L("Object")) : _(L("Sub-object")))); + const DynamicPrintConfig& from_config = wxGetApp().preset_bundle->prints.get_edited_preset().config; for (auto& opt_key : options) { From 40a1f31e847e8613c7b588e6a026b1dcdec317ad Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 10 Jul 2019 11:59:25 +0200 Subject: [PATCH 52/72] Disable remaining toolbar items when one of them is toggable and pressed --- src/slic3r/GUI/GLToolbar.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 78e10c4f1..8c9751086 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -1396,9 +1396,15 @@ bool GLToolbar::update_items_enabled_state() { bool ret = false; - for (GLToolbarItem* item : m_items) + for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) { + GLToolbarItem* item = m_items[i]; ret |= item->update_enabled_state(); + if (item->is_enabled() && (m_pressed_toggable_id != -1) && (m_pressed_toggable_id != i)) + { + ret = true; + item->set_state(GLToolbarItem::Disabled); + } } if (ret) From 14dad5039a316b9a9224cc638f3bd0b783a1293b Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 10 Jul 2019 13:45:25 +0200 Subject: [PATCH 53/72] Imgui dialogs for undo/redo centered on their toolbar item icon --- src/slic3r/GUI/GLCanvas3D.cpp | 7 +++---- src/slic3r/GUI/ImGuiWrapper.cpp | 4 ++-- src/slic3r/GUI/ImGuiWrapper.hpp | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 4fa5b8b27..d14739c94 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3477,8 +3477,7 @@ void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) ImGuiWrapper* imgui = wxGetApp().imgui(); const float x = pos_x * (float)get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width(); - imgui->set_next_window_pos(x, m_toolbar.get_height(), ImGuiCond_Always); - + imgui->set_next_window_pos(x, m_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f); imgui->set_next_window_bg_alpha(0.5f); imgui->begin(wxString::Format(_(L("%s Stack")), stack_name), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); @@ -3688,7 +3687,7 @@ bool GLCanvas3D::_init_toolbar() item.action_callback = [this]() { if (m_canvas != nullptr) m_toolbar.set_imgui_hovered_pos(-1); }; item.visibility_callback = []()->bool { return true; }; item.enabled_state_callback = [this]()->bool { return wxGetApp().plater()->can_undo(); } ; - item.render_callback = [this](float pos_x, float, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(true, pos_x); }; + item.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(true, 0.5f * (left + right)); }; if (!m_toolbar.add_item(item)) return false; @@ -3699,7 +3698,7 @@ bool GLCanvas3D::_init_toolbar() item.tooltip = _utf8(L("Redo")) + " [" + GUI::shortkey_ctrl_prefix() + "Y]"; item.sprite_id = 12; item.enabled_state_callback = [this]()->bool { return wxGetApp().plater()->can_redo(); }; - item.render_callback = [this](float pos_x, float, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(false, pos_x); }; + item.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(false, 0.5f * (left + right)); }; if (!m_toolbar.add_item(item)) return false; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 7f4a6c10c..f58266a5d 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -233,9 +233,9 @@ ImVec2 ImGuiWrapper::calc_text_size(const wxString &text) return size; } -void ImGuiWrapper::set_next_window_pos(float x, float y, int flag) +void ImGuiWrapper::set_next_window_pos(float x, float y, int flag, float pivot_x, float pivot_y) { - ImGui::SetNextWindowPos(ImVec2(x, y), (ImGuiCond)flag); + ImGui::SetNextWindowPos(ImVec2(x, y), (ImGuiCond)flag, ImVec2(pivot_x, pivot_y)); ImGui::SetNextWindowSize(ImVec2(0.0, 0.0)); } diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index a18b15184..c6550351e 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -51,7 +51,7 @@ public: ImVec2 scaled(float x, float y) const { return ImVec2(x * m_font_size, y * m_font_size); } ImVec2 calc_text_size(const wxString &text); - void set_next_window_pos(float x, float y, int flag); + void set_next_window_pos(float x, float y, int flag, float pivot_x = 0.0f, float pivot_y = 0.0f); void set_next_window_bg_alpha(float alpha); bool begin(const std::string &name, int flags = 0); From 1b5ab100bd685d5cf4e77d0b653b864562f9932b Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 10 Jul 2019 14:08:14 +0200 Subject: [PATCH 54/72] GLToolbar::m_imgui_hovered_pos replaced with GLCanvas3D::m_imgui_undo_redo_hovered_pos --- src/slic3r/GUI/GLCanvas3D.cpp | 8 ++++---- src/slic3r/GUI/GLCanvas3D.hpp | 2 ++ src/slic3r/GUI/GLToolbar.hpp | 4 ---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index d14739c94..3dccf4551 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3482,14 +3482,14 @@ void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) imgui->begin(wxString::Format(_(L("%s Stack")), stack_name), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); - int hovered = m_toolbar.get_imgui_hovered_pos(); + int hovered = m_imgui_undo_redo_hovered_pos; int selected = -1; const float em = static_cast(wxGetApp().em_unit()); if (imgui->undo_redo_list(ImVec2(12 * em, 20 * em), is_undo, &string_getter, hovered, selected)) - m_toolbar.set_imgui_hovered_pos(hovered); + m_imgui_undo_redo_hovered_pos = hovered; else - m_toolbar.set_imgui_hovered_pos(-1); + m_imgui_undo_redo_hovered_pos = -1; if (selected >= 0) is_undo ? wxGetApp().plater()->undo_to(selected) : wxGetApp().plater()->redo_to(selected); @@ -3684,7 +3684,7 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Undo")) + " [" + GUI::shortkey_ctrl_prefix() + "Z]"; item.sprite_id = 11; - item.action_callback = [this]() { if (m_canvas != nullptr) m_toolbar.set_imgui_hovered_pos(-1); }; + item.action_callback = [this]() { m_imgui_undo_redo_hovered_pos = -1; }; item.visibility_callback = []()->bool { return true; }; item.enabled_state_callback = [this]()->bool { return wxGetApp().plater()->can_undo(); } ; item.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(true, 0.5f * (left + right)); }; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index f9efdf37a..1949c864b 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -488,6 +488,8 @@ private: RenderStats m_render_stats; #endif // ENABLE_RENDER_STATISTICS + int m_imgui_undo_redo_hovered_pos{ -1 }; + public: GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar); ~GLCanvas3D(); diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 2ba1bb31b..de19296a5 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -252,7 +252,6 @@ private: MouseCapture m_mouse_capture; std::string m_tooltip; - int m_imgui_hovered_pos { -1 }; unsigned int m_pressed_toggable_id; public: @@ -312,9 +311,6 @@ public: bool on_mouse(wxMouseEvent& evt, GLCanvas3D& parent); - void set_imgui_hovered_pos(int pos = -1) { m_imgui_hovered_pos = pos; } - int get_imgui_hovered_pos() const { return m_imgui_hovered_pos; } - private: void calc_layout() const; float get_width_horizontal() const; From dbf0eacfa7c0ac05183dbee54e003ce0c517fbd1 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 11 Jul 2019 07:46:40 +0200 Subject: [PATCH 55/72] Deactivate undo/redo toolbar items when leaving the 3D scene or clicking into it --- src/slic3r/GUI/GLCanvas3D.cpp | 24 ++++++++++++++++++++++-- src/slic3r/GUI/GLCanvas3D.hpp | 2 ++ src/slic3r/GUI/GLToolbar.cpp | 30 ++++++++++++++++++++++++------ src/slic3r/GUI/GLToolbar.hpp | 7 +++++-- 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 3dccf4551..31087d633 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2397,7 +2397,6 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) #endif /* __APPLE__ */ post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE)); break; - case WXK_ESCAPE: { deselect_all(); break; } case '0': { select_view("iso"); break; } case '1': { select_view("top"); break; } @@ -2744,12 +2743,17 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } else if (evt.Leaving()) { + _deactivate_undo_redo_toolbar_items(); + // to remove hover on objects when the mouse goes out of this canvas m_mouse.position = Vec2d(-1.0, -1.0); m_dirty = true; } - else if (evt.LeftDown() || evt.RightDown()) + else if (evt.LeftDown() || evt.RightDown() || evt.MiddleDown()) { + if (_deactivate_undo_redo_toolbar_items()) + return; + // If user pressed left or right button we first check whether this happened // on a volume or not. m_layers_editing.state = LayersEditing::Unknown; @@ -5866,6 +5870,22 @@ void GLCanvas3D::_update_selection_from_hover() m_dirty = true; } +bool GLCanvas3D::_deactivate_undo_redo_toolbar_items() +{ + if (m_toolbar.is_item_pressed("undo")) + { + m_toolbar.force_action(m_toolbar.get_item_id("undo"), *this); + return true; + } + else if (m_toolbar.is_item_pressed("redo")) + { + m_toolbar.force_action(m_toolbar.get_item_id("redo"), *this); + return true; + } + + return false; +} + const Print* GLCanvas3D::fff_print() const { return (m_process == nullptr) ? nullptr : m_process->fff_print(); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 1949c864b..3ae420cdb 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -742,6 +742,8 @@ private: // updates the selection from the content of m_hover_volume_idxs void _update_selection_from_hover(); + bool _deactivate_undo_redo_toolbar_items(); + static std::vector _parse_colors(const std::vector& colors); public: diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 8c9751086..45e427522 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -389,6 +389,22 @@ bool GLToolbar::is_any_item_pressed() const return false; } +unsigned int GLToolbar::get_item_id(const std::string& name) const +{ + for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) + { + if (m_items[i]->get_name() == name) + return i; + } + + return -1; +} + +void GLToolbar::force_action(unsigned int item_id, GLCanvas3D& parent) +{ + do_action(item_id, parent, false); +} + bool GLToolbar::update_items_state() { bool ret = false; @@ -461,10 +477,8 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) m_mouse_capture.parent = &parent; processed = true; if ((item_id != -2) && !m_items[item_id]->is_separator()) - { // mouse is inside an icon - do_action((unsigned int)item_id, parent); - } + do_action((unsigned int)item_id, parent, true); } else if (evt.MiddleDown()) { @@ -572,14 +586,14 @@ float GLToolbar::get_main_size() const return size; } -void GLToolbar::do_action(unsigned int item_id, GLCanvas3D& parent) +void GLToolbar::do_action(unsigned int item_id, GLCanvas3D& parent, bool check_hover) { if ((m_pressed_toggable_id == -1) || (m_pressed_toggable_id == item_id)) { if (item_id < (unsigned int)m_items.size()) { GLToolbarItem* item = m_items[item_id]; - if ((item != nullptr) && !item->is_separator() && item->is_hovered()) + if ((item != nullptr) && !item->is_separator() && (!check_hover || item->is_hovered())) { if (item->is_toggable()) { @@ -588,6 +602,10 @@ void GLToolbar::do_action(unsigned int item_id, GLCanvas3D& parent) item->set_state(GLToolbarItem::HoverPressed); else if (state == GLToolbarItem::HoverPressed) item->set_state(GLToolbarItem::Hover); + else if (state == GLToolbarItem::Pressed) + item->set_state(GLToolbarItem::Normal); + else if (state == GLToolbarItem::Normal) + item->set_state(GLToolbarItem::Pressed); m_pressed_toggable_id = item->is_pressed() ? item_id : -1; @@ -599,7 +617,7 @@ void GLToolbar::do_action(unsigned int item_id, GLCanvas3D& parent) if (m_type == Radio) select_item(item->get_name()); else - item->set_state(GLToolbarItem::HoverPressed); + item->set_state(item->is_hovered() ? GLToolbarItem::HoverPressed : GLToolbarItem::Pressed); parent.render(); item->do_action(); diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index de19296a5..33e1ca234 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -301,8 +301,11 @@ public: bool is_any_item_pressed() const; - const std::string& get_tooltip() const { return m_tooltip; } + unsigned int get_item_id(const std::string& name) const; + void force_action(unsigned int item_id, GLCanvas3D& parent); + + const std::string& get_tooltip() const { return m_tooltip; } // returns true if any item changed its state bool update_items_state(); @@ -318,7 +321,7 @@ private: float get_height_horizontal() const; float get_height_vertical() const; float get_main_size() const; - void do_action(unsigned int item_id, GLCanvas3D& parent); + void do_action(unsigned int item_id, GLCanvas3D& parent, bool check_hover); std::string update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent); std::string update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent); std::string update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent); From f964f5e99a5f432569ce3e04a26a420a1490c7ae Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 11 Jul 2019 07:54:33 +0200 Subject: [PATCH 56/72] Deactivate undo/redo toolbar items by pressing Esc key --- src/slic3r/GUI/GLCanvas3D.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 31087d633..fe91e9a96 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2326,6 +2326,9 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) return; } + if ((keyCode == WXK_ESCAPE) && _deactivate_undo_redo_toolbar_items()) + return; + if (m_gizmos.on_char(evt, *this)) return; From 4c6c608342e92d6769205ea8b68bde7ebbef04d6 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 11 Jul 2019 15:29:46 +0200 Subject: [PATCH 57/72] GLToolbar and GLToolbarItem refactored to allow two different actions when left/right clicking on items. Stack dialog for undo and redo items is now shown on right click only --- src/slic3r/GUI/GLCanvas3D.cpp | 64 +++++++++++++++++--------------- src/slic3r/GUI/GLToolbar.cpp | 70 +++++++++++++++++++++++++++-------- src/slic3r/GUI/GLToolbar.hpp | 47 +++++++++++++++++------ src/slic3r/GUI/Plater.cpp | 6 +-- 4 files changed, 127 insertions(+), 60 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index fe91e9a96..ceca4e0ff 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3550,7 +3550,7 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Add...")) + " [" + GUI::shortkey_ctrl_prefix() + "I]"; item.sprite_id = 0; - item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ADD)); }; + item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ADD)); }; if (!m_toolbar.add_item(item)) return false; @@ -3560,8 +3560,8 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Delete")) + " [Del]"; item.sprite_id = 1; - item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE)); }; - item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_delete(); }; + item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE)); }; + item.enabling_callback = []()->bool { return wxGetApp().plater()->can_delete(); }; if (!m_toolbar.add_item(item)) return false; @@ -3571,8 +3571,8 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Delete all")) + " [" + GUI::shortkey_ctrl_prefix() + "Del]"; item.sprite_id = 2; - item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); }; - item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_delete_all(); }; + item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); }; + item.enabling_callback = []()->bool { return wxGetApp().plater()->can_delete_all(); }; if (!m_toolbar.add_item(item)) return false; @@ -3582,8 +3582,8 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Arrange")) + " [A]"; item.sprite_id = 3; - item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ARRANGE)); }; - item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_arrange(); }; + item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ARRANGE)); }; + item.enabling_callback = []()->bool { return wxGetApp().plater()->can_arrange(); }; if (!m_toolbar.add_item(item)) return false; @@ -3596,8 +3596,8 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Copy")) + " [" + GUI::shortkey_ctrl_prefix() + "C]"; item.sprite_id = 4; - item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_COPY)); }; - item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_copy_to_clipboard(); }; + item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_COPY)); }; + item.enabling_callback = []()->bool { return wxGetApp().plater()->can_copy_to_clipboard(); }; if (!m_toolbar.add_item(item)) return false; @@ -3607,8 +3607,8 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Paste")) + " [" + GUI::shortkey_ctrl_prefix() + "V]"; item.sprite_id = 5; - item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_PASTE)); }; - item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_paste_from_clipboard(); }; + item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_PASTE)); }; + item.enabling_callback = []()->bool { return wxGetApp().plater()->can_paste_from_clipboard(); }; if (!m_toolbar.add_item(item)) return false; @@ -3621,9 +3621,10 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Add instance")) + " [+]"; item.sprite_id = 6; - item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_MORE)); }; + item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_MORE)); }; item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; }; - item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_increase_instances(); }; + item.enabling_callback = []()->bool { return wxGetApp().plater()->can_increase_instances(); }; + if (!m_toolbar.add_item(item)) return false; @@ -3633,9 +3634,9 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Remove instance")) + " [-]"; item.sprite_id = 7; - item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_FEWER)); }; + item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_FEWER)); }; item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; }; - item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_decrease_instances(); }; + item.enabling_callback = []()->bool { return wxGetApp().plater()->can_decrease_instances(); }; if (!m_toolbar.add_item(item)) return false; @@ -3648,9 +3649,9 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Split to objects")); item.sprite_id = 8; - item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); }; + item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); }; item.visibility_callback = GLToolbarItem::Default_Visibility_Callback; - item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_split_to_objects(); }; + item.enabling_callback = []()->bool { return wxGetApp().plater()->can_split_to_objects(); }; if (!m_toolbar.add_item(item)) return false; @@ -3660,9 +3661,9 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Split to parts")); item.sprite_id = 9; - item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); }; + item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); }; item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; }; - item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_split_to_volumes(); }; + item.enabling_callback = []()->bool { return wxGetApp().plater()->can_split_to_volumes(); }; if (!m_toolbar.add_item(item)) return false; @@ -3675,10 +3676,10 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Layers editing")); item.sprite_id = 10; - item.is_toggable = true; - item.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); }; + item.left_toggable = true; + item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); }; item.visibility_callback = [this]()->bool { return m_process->current_printer_technology() == ptFFF; }; - item.enabled_state_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); }; + item.enabling_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); }; if (!m_toolbar.add_item(item)) return false; @@ -3691,10 +3692,13 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Undo")) + " [" + GUI::shortkey_ctrl_prefix() + "Z]"; item.sprite_id = 11; - item.action_callback = [this]() { m_imgui_undo_redo_hovered_pos = -1; }; + item.left_toggable = false; + item.right_toggable = true; + item.left_action_callback = [this]() { post_event(SimpleEvent(EVT_GLCANVAS_UNDO)); }; + item.right_action_callback = [this]() { m_imgui_undo_redo_hovered_pos = -1; }; item.visibility_callback = []()->bool { return true; }; - item.enabled_state_callback = [this]()->bool { return wxGetApp().plater()->can_undo(); } ; - item.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(true, 0.5f * (left + right)); }; + item.enabling_callback = [this]()->bool { return wxGetApp().plater()->can_undo(); }; + item.right_render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(true, 0.5f * (left + right)); }; if (!m_toolbar.add_item(item)) return false; @@ -3704,8 +3708,10 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Redo")) + " [" + GUI::shortkey_ctrl_prefix() + "Y]"; item.sprite_id = 12; - item.enabled_state_callback = [this]()->bool { return wxGetApp().plater()->can_redo(); }; - item.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(false, 0.5f * (left + right)); }; + item.left_action_callback = [this]() { post_event(SimpleEvent(EVT_GLCANVAS_REDO)); }; + item.right_action_callback = [this]() { m_imgui_undo_redo_hovered_pos = -1; }; + item.enabling_callback = [this]()->bool { return wxGetApp().plater()->can_redo(); }; + item.right_render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(false, 0.5f * (left + right)); }; if (!m_toolbar.add_item(item)) return false; @@ -5877,12 +5883,12 @@ bool GLCanvas3D::_deactivate_undo_redo_toolbar_items() { if (m_toolbar.is_item_pressed("undo")) { - m_toolbar.force_action(m_toolbar.get_item_id("undo"), *this); + m_toolbar.force_right_action(m_toolbar.get_item_id("undo"), *this); return true; } else if (m_toolbar.is_item_pressed("redo")) { - m_toolbar.force_action(m_toolbar.get_item_id("redo"), *this); + m_toolbar.force_right_action(m_toolbar.get_item_id("redo"), *this); return true; } diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 45e427522..862805f8d 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -34,7 +34,7 @@ wxDEFINE_EVENT(EVT_GLVIEWTOOLBAR_PREVIEW, SimpleEvent); const GLToolbarItem::ActionCallback GLToolbarItem::Default_Action_Callback = [](){}; const GLToolbarItem::VisibilityCallback GLToolbarItem::Default_Visibility_Callback = []()->bool { return true; }; -const GLToolbarItem::EnabledStateCallback GLToolbarItem::Default_Enabled_State_Callback = []()->bool { return true; }; +const GLToolbarItem::EnablingCallback GLToolbarItem::Default_Enabling_Callback = []()->bool { return true; }; const GLToolbarItem::RenderCallback GLToolbarItem::Default_Render_Callback = [](float, float, float, float){}; GLToolbarItem::Data::Data() @@ -44,12 +44,15 @@ GLToolbarItem::Data::Data() #endif // ENABLE_SVG_ICONS , tooltip("") , sprite_id(-1) - , is_toggable(false) + , left_toggable(false) + , right_toggable(false) , visible(true) - , action_callback(Default_Action_Callback) + , left_action_callback(Default_Action_Callback) + , right_action_callback(Default_Action_Callback) , visibility_callback(Default_Visibility_Callback) - , enabled_state_callback(Default_Enabled_State_Callback) - , render_callback(Default_Render_Callback) + , enabling_callback(Default_Enabling_Callback) + , left_render_callback(nullptr) + , right_render_callback(nullptr) { } @@ -57,6 +60,7 @@ GLToolbarItem::GLToolbarItem(GLToolbarItem::EType type, const GLToolbarItem::Dat : m_type(type) , m_state(Normal) , m_data(data) + , m_last_action(Undefined) { } @@ -72,7 +76,7 @@ bool GLToolbarItem::update_visibility() bool GLToolbarItem::update_enabled_state() { - bool enabled = m_data.enabled_state_callback(); + bool enabled = m_data.enabling_callback(); bool ret = (is_enabled() != enabled); if (ret) m_state = enabled ? GLToolbarItem::Normal : GLToolbarItem::Disabled; @@ -84,8 +88,13 @@ void GLToolbarItem::render(unsigned int tex_id, float left, float right, float b { GLTexture::render_sub_texture(tex_id, left, right, bottom, top, get_uvs(tex_width, tex_height, icon_size)); - if (is_toggable() && is_pressed()) - m_data.render_callback(left, right, bottom, top); + if (is_pressed()) + { + if ((m_last_action == Left) && m_data.left_toggable && (m_data.left_render_callback != nullptr)) + m_data.left_render_callback(left, right, bottom, top); + else if ((m_last_action == Right) && m_data.right_toggable && (m_data.right_render_callback != nullptr)) + m_data.right_render_callback(left, right, bottom, top); + } } GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const @@ -400,9 +409,14 @@ unsigned int GLToolbar::get_item_id(const std::string& name) const return -1; } -void GLToolbar::force_action(unsigned int item_id, GLCanvas3D& parent) +void GLToolbar::force_left_action(unsigned int item_id, GLCanvas3D& parent) { - do_action(item_id, parent, false); + do_action(GLToolbarItem::Left, item_id, parent, false); +} + +void GLToolbar::force_right_action(unsigned int item_id, GLCanvas3D& parent) +{ + do_action(GLToolbarItem::Right, item_id, parent, false); } bool GLToolbar::update_items_state() @@ -476,9 +490,12 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) m_mouse_capture.left = true; m_mouse_capture.parent = &parent; processed = true; - if ((item_id != -2) && !m_items[item_id]->is_separator()) + if ((item_id != -2) && !m_items[item_id]->is_separator() && ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action() == GLToolbarItem::Left))) + { // mouse is inside an icon - do_action((unsigned int)item_id, parent, true); + do_action(GLToolbarItem::Left, (unsigned int)item_id, parent, true); + parent.set_as_dirty(); + } } else if (evt.MiddleDown()) { @@ -489,6 +506,13 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) { m_mouse_capture.right = true; m_mouse_capture.parent = &parent; + processed = true; + if ((item_id != -2) && !m_items[item_id]->is_separator() && ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action() == GLToolbarItem::Right))) + { + // mouse is inside an icon + do_action(GLToolbarItem::Right, (unsigned int)item_id, parent, true); + parent.set_as_dirty(); + } } else if (evt.LeftUp()) processed = true; @@ -586,7 +610,7 @@ float GLToolbar::get_main_size() const return size; } -void GLToolbar::do_action(unsigned int item_id, GLCanvas3D& parent, bool check_hover) +void GLToolbar::do_action(GLToolbarItem::EActionType type, unsigned int item_id, GLCanvas3D& parent, bool check_hover) { if ((m_pressed_toggable_id == -1) || (m_pressed_toggable_id == item_id)) { @@ -595,7 +619,8 @@ void GLToolbar::do_action(unsigned int item_id, GLCanvas3D& parent, bool check_h GLToolbarItem* item = m_items[item_id]; if ((item != nullptr) && !item->is_separator() && (!check_hover || item->is_hovered())) { - if (item->is_toggable()) + if (((type == GLToolbarItem::Right) && item->is_right_toggable()) || + ((type == GLToolbarItem::Left) && item->is_left_toggable())) { GLToolbarItem::EState state = item->get_state(); if (state == GLToolbarItem::Hover) @@ -608,9 +633,15 @@ void GLToolbar::do_action(unsigned int item_id, GLCanvas3D& parent, bool check_h item->set_state(GLToolbarItem::Pressed); m_pressed_toggable_id = item->is_pressed() ? item_id : -1; + item->reset_last_action(); parent.render(); - item->do_action(); + switch (type) + { + default: + case GLToolbarItem::Left: { item->do_left_action(); break; } + case GLToolbarItem::Right: { item->do_right_action(); break; } + } } else { @@ -619,8 +650,15 @@ void GLToolbar::do_action(unsigned int item_id, GLCanvas3D& parent, bool check_h else item->set_state(item->is_hovered() ? GLToolbarItem::HoverPressed : GLToolbarItem::Pressed); + item->reset_last_action(); parent.render(); - item->do_action(); + switch (type) + { + default: + case GLToolbarItem::Left: { item->do_left_action(); break; } + case GLToolbarItem::Right: { item->do_right_action(); break; } + } + if ((m_type == Normal) && (item->get_state() != GLToolbarItem::Disabled)) { // the item may get disabled during the action, if not, set it back to hover state diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 33e1ca234..86cd1a7fc 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -36,8 +36,8 @@ class GLToolbarItem public: typedef std::function ActionCallback; typedef std::function VisibilityCallback; - typedef std::function EnabledStateCallback; - typedef std::function RenderCallback; + typedef std::function EnablingCallback; + typedef std::function RenderCallback; enum EType : unsigned char { @@ -46,6 +46,14 @@ public: Num_Types }; + enum EActionType : unsigned char + { + Undefined, + Left, + Right, + Num_Action_Types + }; + enum EState : unsigned char { Normal, @@ -64,25 +72,33 @@ public: #endif // ENABLE_SVG_ICONS std::string tooltip; unsigned int sprite_id; - bool is_toggable; + bool left_toggable; + bool right_toggable; bool visible; - ActionCallback action_callback; + // action on left click + ActionCallback left_action_callback; + // action on right click + ActionCallback right_action_callback; VisibilityCallback visibility_callback; - EnabledStateCallback enabled_state_callback; - RenderCallback render_callback; + EnablingCallback enabling_callback; + // render callback on left click + RenderCallback left_render_callback; + // render callback on right click + RenderCallback right_render_callback; Data(); }; static const ActionCallback Default_Action_Callback; static const VisibilityCallback Default_Visibility_Callback; - static const EnabledStateCallback Default_Enabled_State_Callback; + static const EnablingCallback Default_Enabling_Callback; static const RenderCallback Default_Render_Callback; private: EType m_type; EState m_state; Data m_data; + EActionType m_last_action; public: GLToolbarItem(EType type, const Data& data); @@ -96,17 +112,25 @@ public: #endif // ENABLE_SVG_ICONS const std::string& get_tooltip() const { return m_data.tooltip; } - void do_action() { m_data.action_callback(); } + void do_left_action() { m_last_action = Left; m_data.left_action_callback(); } + void do_right_action() { m_last_action = Right; m_data.right_action_callback(); } bool is_enabled() const { return m_state != Disabled; } bool is_disabled() const { return m_state == Disabled; } bool is_hovered() const { return (m_state == Hover) || (m_state == HoverPressed); } bool is_pressed() const { return (m_state == Pressed) || (m_state == HoverPressed); } - bool is_toggable() const { return m_data.is_toggable; } + bool is_left_toggable() const { return m_data.left_toggable; } + bool is_right_toggable() const { return m_data.right_toggable; } bool is_visible() const { return m_data.visible; } bool is_separator() const { return m_type == Separator; } + bool has_left_render_callback() const { return m_data.left_render_callback != nullptr; } + bool has_right_render_callback() const { return m_data.right_render_callback != nullptr; } + + EActionType get_last_action() const { return m_last_action; } + void reset_last_action() { m_last_action = Undefined; } + // returns true if the state changes bool update_visibility(); // returns true if the state changes @@ -303,7 +327,8 @@ public: unsigned int get_item_id(const std::string& name) const; - void force_action(unsigned int item_id, GLCanvas3D& parent); + void force_left_action(unsigned int item_id, GLCanvas3D& parent); + void force_right_action(unsigned int item_id, GLCanvas3D& parent); const std::string& get_tooltip() const { return m_tooltip; } @@ -321,7 +346,7 @@ private: float get_height_horizontal() const; float get_height_vertical() const; float get_main_size() const; - void do_action(unsigned int item_id, GLCanvas3D& parent, bool check_hover); + void do_action(GLToolbarItem::EActionType type, unsigned int item_id, GLCanvas3D& parent, bool check_hover); std::string update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent); std::string update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent); std::string update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index d8825ab3a..ea66d5f78 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3421,8 +3421,7 @@ void Plater::priv::init_view_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("3D editor view")) + " [" + GUI::shortkey_ctrl_prefix() + "5]"; item.sprite_id = 0; - item.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_3D)); }; - item.is_toggable = false; + item.left_action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_3D)); }; if (!view_toolbar.add_item(item)) return; @@ -3432,8 +3431,7 @@ void Plater::priv::init_view_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Preview")) + " [" + GUI::shortkey_ctrl_prefix() + "6]"; item.sprite_id = 1; - item.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_PREVIEW)); }; - item.is_toggable = false; + item.left_action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_PREVIEW)); }; if (!view_toolbar.add_item(item)) return; From a6a5b94155b22b4434e8f4ad1dc0426da95ffa1e Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 11 Jul 2019 16:00:01 +0200 Subject: [PATCH 58/72] Added suppress_snapshots() and allow_snapshots() for avoid of excess "snapshoting" --- src/slic3r/GUI/GUI_ObjectList.cpp | 10 +++++++++- src/slic3r/GUI/Plater.cpp | 22 ++++++++++++++++++++-- src/slic3r/GUI/Plater.hpp | 2 ++ 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index c11865cac..360318413 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -66,11 +66,14 @@ static int extruders_count() return wxGetApp().extruders_cnt(); } -static void take_snapshot(const wxString& snapshot_name) +static void take_snapshot(const wxString& snapshot_name) { wxGetApp().plater()->take_snapshot(snapshot_name); } +static void suppress_snapshots(){ wxGetApp().plater()->suppress_snapshots(); } +static void allow_snapshots() { wxGetApp().plater()->allow_snapshots(); } + ObjectList::ObjectList(wxWindow* parent) : wxDataViewCtrl(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_MULTIPLE), m_parent(parent) @@ -2333,6 +2336,9 @@ void ObjectList::remove() wxDataViewItem parent = wxDataViewItem(0); + take_snapshot(_(L("Delete Selected"))); + suppress_snapshots(); + for (auto& item : sels) { if (m_objects_model->GetParent(item) == wxDataViewItem(0)) @@ -2353,6 +2359,8 @@ void ObjectList::remove() if (parent) select_item(parent); + + allow_snapshots(); } void ObjectList::del_layer_range(const t_layer_height_range& range) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index d8825ab3a..15dd6b6ca 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1280,7 +1280,11 @@ struct Plater::priv PrinterTechnology printer_technology = ptFFF; Slic3r::GCodePreviewData gcode_preview_data; Slic3r::UndoRedo::Stack undo_redo_stack; - + bool m_prevent_snapshots = false; /* Used for avoid of excess "snapshoting". + * Like for "delete selected" or "set numbers of copies" + * we should call tack_snapshot just ones + * instead of calls for each action separately + * */ // GUI elements wxSizer* panel_sizer{ nullptr }; wxPanel* current_panel{ nullptr }; @@ -1581,13 +1585,20 @@ struct Plater::priv void split_volume(); void scale_selection_to_fit_print_volume(); - void take_snapshot(const std::string& snapshot_name) { this->undo_redo_stack.take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection()); } + void take_snapshot(const std::string& snapshot_name) + { + if (this->m_prevent_snapshots) + return; + this->undo_redo_stack.take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection()); + } void take_snapshot(const wxString& snapshot_name) { this->take_snapshot(std::string(snapshot_name.ToUTF8().data())); } int get_active_snapshot_index(); void undo(); void redo(); void undo_to(size_t time_to_load); void redo_to(size_t time_to_load); + void suppress_snapshots() { this->m_prevent_snapshots = true; } + void allow_snapshots() { this->m_prevent_snapshots = false; } bool background_processing_enabled() const { return this->get_config("background_processing") == "1"; } void update_print_volume_state(); @@ -3729,7 +3740,9 @@ void Plater::delete_object_from_model(size_t obj_idx) { p->delete_object_from_mo void Plater::remove_selected() { this->take_snapshot(_(L("Delete Selected Objects"))); + this->suppress_snapshots(); this->p->view3D->delete_selected(); + this->allow_snapshots(); } void Plater::increase_instances(size_t num) @@ -3809,12 +3822,15 @@ void Plater::set_number_of_copies(/*size_t num*/) return; this->take_snapshot(wxString::Format(_(L("Set numbers of copies to %d")), num)); + this->suppress_snapshots(); int diff = (int)num - (int)model_object->instances.size(); if (diff > 0) increase_instances(diff); else if (diff < 0) decrease_instances(-diff); + + this->allow_snapshots(); } bool Plater::is_selection_empty() const @@ -4131,6 +4147,8 @@ void Plater::send_gcode() void Plater::take_snapshot(const std::string &snapshot_name) { p->take_snapshot(snapshot_name); } void Plater::take_snapshot(const wxString &snapshot_name) { p->take_snapshot(snapshot_name); } +void Plater::suppress_snapshots() { p->suppress_snapshots(); } +void Plater::allow_snapshots() { p->allow_snapshots(); } void Plater::undo() { p->undo(); } void Plater::redo() { p->redo(); } void Plater::undo_to(int selection) diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index ca3d59224..23a5f12cf 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -185,6 +185,8 @@ public: void take_snapshot(const std::string &snapshot_name); void take_snapshot(const wxString &snapshot_name); + void suppress_snapshots(); + void allow_snapshots(); void undo(); void redo(); void undo_to(int selection); From 2f57f756e54151721e9be4b5f0bbe2788830ad20 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Fri, 12 Jul 2019 09:26:19 +0200 Subject: [PATCH 59/72] Follow-up of 4c6c608342e92d6769205ea8b68bde7ebbef04d6 -> refactoring --- src/slic3r/GUI/GLCanvas3D.cpp | 40 +++++++++++++++---------------- src/slic3r/GUI/GLToolbar.cpp | 31 ++++++++++++------------ src/slic3r/GUI/GLToolbar.hpp | 45 +++++++++++++++++++---------------- src/slic3r/GUI/Plater.cpp | 4 ++-- 4 files changed, 63 insertions(+), 57 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index ceca4e0ff..86aab33be 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3550,7 +3550,7 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Add...")) + " [" + GUI::shortkey_ctrl_prefix() + "I]"; item.sprite_id = 0; - item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ADD)); }; + item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ADD)); }; if (!m_toolbar.add_item(item)) return false; @@ -3560,7 +3560,7 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Delete")) + " [Del]"; item.sprite_id = 1; - item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE)); }; + item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_delete(); }; if (!m_toolbar.add_item(item)) return false; @@ -3571,7 +3571,7 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Delete all")) + " [" + GUI::shortkey_ctrl_prefix() + "Del]"; item.sprite_id = 2; - item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); }; + item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_delete_all(); }; if (!m_toolbar.add_item(item)) return false; @@ -3582,7 +3582,7 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Arrange")) + " [A]"; item.sprite_id = 3; - item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ARRANGE)); }; + item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ARRANGE)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_arrange(); }; if (!m_toolbar.add_item(item)) return false; @@ -3596,7 +3596,7 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Copy")) + " [" + GUI::shortkey_ctrl_prefix() + "C]"; item.sprite_id = 4; - item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_COPY)); }; + item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_COPY)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_copy_to_clipboard(); }; if (!m_toolbar.add_item(item)) return false; @@ -3607,7 +3607,7 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Paste")) + " [" + GUI::shortkey_ctrl_prefix() + "V]"; item.sprite_id = 5; - item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_PASTE)); }; + item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_PASTE)); }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_paste_from_clipboard(); }; if (!m_toolbar.add_item(item)) return false; @@ -3621,7 +3621,7 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Add instance")) + " [+]"; item.sprite_id = 6; - item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_MORE)); }; + item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_MORE)); }; item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_increase_instances(); }; @@ -3634,7 +3634,7 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Remove instance")) + " [-]"; item.sprite_id = 7; - item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_FEWER)); }; + item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_FEWER)); }; item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_decrease_instances(); }; if (!m_toolbar.add_item(item)) @@ -3649,7 +3649,7 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Split to objects")); item.sprite_id = 8; - item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); }; + item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); }; item.visibility_callback = GLToolbarItem::Default_Visibility_Callback; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_split_to_objects(); }; if (!m_toolbar.add_item(item)) @@ -3661,7 +3661,7 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Split to parts")); item.sprite_id = 9; - item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); }; + item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); }; item.visibility_callback = []()->bool { return wxGetApp().get_mode() != comSimple; }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_split_to_volumes(); }; if (!m_toolbar.add_item(item)) @@ -3676,8 +3676,8 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Layers editing")); item.sprite_id = 10; - item.left_toggable = true; - item.left_action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); }; + item.left.toggable = true; + item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); }; item.visibility_callback = [this]()->bool { return m_process->current_printer_technology() == ptFFF; }; item.enabling_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); }; if (!m_toolbar.add_item(item)) @@ -3692,13 +3692,13 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Undo")) + " [" + GUI::shortkey_ctrl_prefix() + "Z]"; item.sprite_id = 11; - item.left_toggable = false; - item.right_toggable = true; - item.left_action_callback = [this]() { post_event(SimpleEvent(EVT_GLCANVAS_UNDO)); }; - item.right_action_callback = [this]() { m_imgui_undo_redo_hovered_pos = -1; }; + item.left.toggable = false; + item.left.action_callback = [this]() { post_event(SimpleEvent(EVT_GLCANVAS_UNDO)); }; + item.right.toggable = true; + item.right.action_callback = [this]() { m_imgui_undo_redo_hovered_pos = -1; }; + item.right.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(true, 0.5f * (left + right)); }; item.visibility_callback = []()->bool { return true; }; item.enabling_callback = [this]()->bool { return wxGetApp().plater()->can_undo(); }; - item.right_render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(true, 0.5f * (left + right)); }; if (!m_toolbar.add_item(item)) return false; @@ -3708,10 +3708,10 @@ bool GLCanvas3D::_init_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Redo")) + " [" + GUI::shortkey_ctrl_prefix() + "Y]"; item.sprite_id = 12; - item.left_action_callback = [this]() { post_event(SimpleEvent(EVT_GLCANVAS_REDO)); }; - item.right_action_callback = [this]() { m_imgui_undo_redo_hovered_pos = -1; }; + item.left.action_callback = [this]() { post_event(SimpleEvent(EVT_GLCANVAS_REDO)); }; + item.right.action_callback = [this]() { m_imgui_undo_redo_hovered_pos = -1; }; + item.right.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(false, 0.5f * (left + right)); }; item.enabling_callback = [this]()->bool { return wxGetApp().plater()->can_redo(); }; - item.right_render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(false, 0.5f * (left + right)); }; if (!m_toolbar.add_item(item)) return false; diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 862805f8d..5093eb5d4 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -37,6 +37,13 @@ const GLToolbarItem::VisibilityCallback GLToolbarItem::Default_Visibility_Callba const GLToolbarItem::EnablingCallback GLToolbarItem::Default_Enabling_Callback = []()->bool { return true; }; const GLToolbarItem::RenderCallback GLToolbarItem::Default_Render_Callback = [](float, float, float, float){}; +GLToolbarItem::Data::Option::Option() + : toggable(false) + , action_callback(Default_Action_Callback) + , render_callback(nullptr) +{ +} + GLToolbarItem::Data::Data() : name("") #if ENABLE_SVG_ICONS @@ -44,15 +51,9 @@ GLToolbarItem::Data::Data() #endif // ENABLE_SVG_ICONS , tooltip("") , sprite_id(-1) - , left_toggable(false) - , right_toggable(false) , visible(true) - , left_action_callback(Default_Action_Callback) - , right_action_callback(Default_Action_Callback) , visibility_callback(Default_Visibility_Callback) , enabling_callback(Default_Enabling_Callback) - , left_render_callback(nullptr) - , right_render_callback(nullptr) { } @@ -60,7 +61,7 @@ GLToolbarItem::GLToolbarItem(GLToolbarItem::EType type, const GLToolbarItem::Dat : m_type(type) , m_state(Normal) , m_data(data) - , m_last_action(Undefined) + , m_last_action_type(Undefined) { } @@ -90,10 +91,10 @@ void GLToolbarItem::render(unsigned int tex_id, float left, float right, float b if (is_pressed()) { - if ((m_last_action == Left) && m_data.left_toggable && (m_data.left_render_callback != nullptr)) - m_data.left_render_callback(left, right, bottom, top); - else if ((m_last_action == Right) && m_data.right_toggable && (m_data.right_render_callback != nullptr)) - m_data.right_render_callback(left, right, bottom, top); + if ((m_last_action_type == Left) && m_data.left.can_render()) + m_data.left.render_callback(left, right, bottom, top); + else if ((m_last_action_type == Right) && m_data.right.can_render()) + m_data.right.render_callback(left, right, bottom, top); } } @@ -490,7 +491,7 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) m_mouse_capture.left = true; m_mouse_capture.parent = &parent; processed = true; - if ((item_id != -2) && !m_items[item_id]->is_separator() && ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action() == GLToolbarItem::Left))) + if ((item_id != -2) && !m_items[item_id]->is_separator() && ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Left))) { // mouse is inside an icon do_action(GLToolbarItem::Left, (unsigned int)item_id, parent, true); @@ -507,7 +508,7 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) m_mouse_capture.right = true; m_mouse_capture.parent = &parent; processed = true; - if ((item_id != -2) && !m_items[item_id]->is_separator() && ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action() == GLToolbarItem::Right))) + if ((item_id != -2) && !m_items[item_id]->is_separator() && ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Right))) { // mouse is inside an icon do_action(GLToolbarItem::Right, (unsigned int)item_id, parent, true); @@ -633,7 +634,7 @@ void GLToolbar::do_action(GLToolbarItem::EActionType type, unsigned int item_id, item->set_state(GLToolbarItem::Pressed); m_pressed_toggable_id = item->is_pressed() ? item_id : -1; - item->reset_last_action(); + item->reset_last_action_type(); parent.render(); switch (type) @@ -650,7 +651,7 @@ void GLToolbar::do_action(GLToolbarItem::EActionType type, unsigned int item_id, else item->set_state(item->is_hovered() ? GLToolbarItem::HoverPressed : GLToolbarItem::Pressed); - item->reset_last_action(); + item->reset_last_action_type(); parent.render(); switch (type) { diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 86cd1a7fc..34a92ea34 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -66,25 +66,30 @@ public: struct Data { + struct Option + { + bool toggable; + ActionCallback action_callback; + RenderCallback render_callback; + + Option(); + + bool can_render() const { return toggable && (render_callback != nullptr); } + }; + std::string name; #if ENABLE_SVG_ICONS std::string icon_filename; #endif // ENABLE_SVG_ICONS std::string tooltip; unsigned int sprite_id; - bool left_toggable; - bool right_toggable; + // mouse left click + Option left; + // mouse right click + Option right; bool visible; - // action on left click - ActionCallback left_action_callback; - // action on right click - ActionCallback right_action_callback; VisibilityCallback visibility_callback; EnablingCallback enabling_callback; - // render callback on left click - RenderCallback left_render_callback; - // render callback on right click - RenderCallback right_render_callback; Data(); }; @@ -98,7 +103,7 @@ private: EType m_type; EState m_state; Data m_data; - EActionType m_last_action; + EActionType m_last_action_type; public: GLToolbarItem(EType type, const Data& data); @@ -112,24 +117,24 @@ public: #endif // ENABLE_SVG_ICONS const std::string& get_tooltip() const { return m_data.tooltip; } - void do_left_action() { m_last_action = Left; m_data.left_action_callback(); } - void do_right_action() { m_last_action = Right; m_data.right_action_callback(); } + void do_left_action() { m_last_action_type = Left; m_data.left.action_callback(); } + void do_right_action() { m_last_action_type = Right; m_data.right.action_callback(); } bool is_enabled() const { return m_state != Disabled; } bool is_disabled() const { return m_state == Disabled; } bool is_hovered() const { return (m_state == Hover) || (m_state == HoverPressed); } bool is_pressed() const { return (m_state == Pressed) || (m_state == HoverPressed); } - - bool is_left_toggable() const { return m_data.left_toggable; } - bool is_right_toggable() const { return m_data.right_toggable; } bool is_visible() const { return m_data.visible; } bool is_separator() const { return m_type == Separator; } - bool has_left_render_callback() const { return m_data.left_render_callback != nullptr; } - bool has_right_render_callback() const { return m_data.right_render_callback != nullptr; } + bool is_left_toggable() const { return m_data.left.toggable; } + bool is_right_toggable() const { return m_data.right.toggable; } - EActionType get_last_action() const { return m_last_action; } - void reset_last_action() { m_last_action = Undefined; } + bool has_left_render_callback() const { return m_data.left.render_callback != nullptr; } + bool has_right_render_callback() const { return m_data.right.render_callback != nullptr; } + + EActionType get_last_action_type() const { return m_last_action_type; } + void reset_last_action_type() { m_last_action_type = Undefined; } // returns true if the state changes bool update_visibility(); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 06252e1e7..e826d748e 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3432,7 +3432,7 @@ void Plater::priv::init_view_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("3D editor view")) + " [" + GUI::shortkey_ctrl_prefix() + "5]"; item.sprite_id = 0; - item.left_action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_3D)); }; + item.left.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_3D)); }; if (!view_toolbar.add_item(item)) return; @@ -3442,7 +3442,7 @@ void Plater::priv::init_view_toolbar() #endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Preview")) + " [" + GUI::shortkey_ctrl_prefix() + "6]"; item.sprite_id = 1; - item.left_action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_PREVIEW)); }; + item.left.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_PREVIEW)); }; if (!view_toolbar.add_item(item)) return; From aed6acc073111d8d6134705e36894aedffba536c Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 11 Jul 2019 18:42:02 +0200 Subject: [PATCH 60/72] Add take_snapshot for layers range editing actions --- src/slic3r/GUI/GUI_ObjectList.cpp | 37 ++++++++++++++++++++++++++----- src/slic3r/GUI/GUI_ObjectList.hpp | 2 +- src/slic3r/GUI/Plater.cpp | 14 ++++++------ 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 360318413..dc4bb8795 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -1756,6 +1756,8 @@ void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item) is_layer_settings && opt_cnt == 2 && m_config->has("extruder") && m_config->has("layer_height")) return; + take_snapshot(_(L("Delete Settings"))); + int extruder = -1; if (m_config->has("extruder")) extruder = m_config->option("extruder")->value; @@ -1793,6 +1795,8 @@ void ObjectList::del_layer_from_object(const int obj_idx, const t_layer_height_r const auto del_range = object(obj_idx)->layer_config_ranges.find(layer_range); if (del_range == object(obj_idx)->layer_config_ranges.end()) return; + + take_snapshot(_(L("Delete Layers Range"))); object(obj_idx)->layer_config_ranges.erase(del_range); @@ -1923,8 +1927,10 @@ void ObjectList::layers_editing() t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges; // set some default value - if (ranges.empty()) + if (ranges.empty()) { + take_snapshot(_(L("Add Layers"))); ranges[{ 0.0f, 2.0f }] = get_default_layer_config(obj_idx); + } // create layer root item layers_item = add_layer_root_item(obj_item); @@ -1956,6 +1962,7 @@ wxDataViewItem ObjectList::add_layer_root_item(const wxDataViewItem obj_item) for (const auto range : object(obj_idx)->layer_config_ranges) add_layer_item(range.first, layers_item); + Expand(layers_item); return layers_item; } @@ -2406,6 +2413,8 @@ void ObjectList::add_layer_range_after_current(const t_layer_height_range& curre if (current_range == last_range) { + take_snapshot(_(L("Add New Layers Range"))); + const t_layer_height_range& new_range = { last_range.second, last_range.second + 2.0f }; ranges[new_range] = get_default_layer_config(obj_idx); add_layer_item(new_range, layers_item); @@ -2433,22 +2442,28 @@ void ObjectList::add_layer_range_after_current(const t_layer_height_range& curre t_layer_height_range new_range = { midl_layer, next_range.second }; + take_snapshot(_(L("Add New Layers Range"))); + suppress_snapshots(); + + // create new 2 layers instead of deleted one + // delete old layer wxDataViewItem layer_item = m_objects_model->GetItemByLayerRange(obj_idx, next_range); del_subobject_item(layer_item); - // create new 2 layers instead of deleted one - ranges[new_range] = old_config; add_layer_item(new_range, layers_item, layer_idx); new_range = { current_range.second, midl_layer }; ranges[new_range] = get_default_layer_config(obj_idx); add_layer_item(new_range, layers_item, layer_idx); + allow_snapshots(); } else { + take_snapshot(_(L("Add New Layers Range"))); + const t_layer_height_range new_range = { current_range.second, next_range.first }; ranges[new_range] = get_default_layer_config(obj_idx); add_layer_item(new_range, layers_item, layer_idx); @@ -2477,8 +2492,10 @@ void ObjectList::add_layer_item(const t_layer_height_range& range, config.opt_int("extruder"), layer_idx); - if (config.keys().size() > 2) - select_item(m_objects_model->AddSettingsChild(layer_item)); + if (config.keys().size() > 2) { + m_objects_model->AddSettingsChild(layer_item); + Expand(layer_item); + } } bool ObjectList::edit_layer_range(const t_layer_height_range& range, coordf_t layer_height) @@ -2508,6 +2525,8 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_lay const int obj_idx = get_selected_obj_idx(); if (obj_idx < 0) return false; + take_snapshot(_(L("Edit Layers Range"))); + const ItemType sel_type = m_objects_model->GetItemType(GetSelection()); t_layer_config_ranges& ranges = object(obj_idx)->layer_config_ranges; @@ -3417,11 +3436,13 @@ void ObjectList::set_extruder_for_selected_items(const int extruder) const wxGetApp().plater()->update(); } -void ObjectList::recreate_object_list() +void ObjectList::update_after_undo_redo() { m_prevent_list_events = true; m_prevent_canvas_selection_update = true; + suppress_snapshots(); + // Unselect all objects before deleting them, so that no change of selection is emitted during deletion. this->UnselectAll(); m_objects_model->DeleteAll(); @@ -3432,10 +3453,14 @@ void ObjectList::recreate_object_list() ++obj_idx; } + allow_snapshots(); + #ifndef __WXOSX__ selection_changed(); #endif /* __WXOSX__ */ + update_selections(); + m_prevent_canvas_selection_update = false; m_prevent_list_events = false; } diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index a5a9c2138..34efa9025 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -335,7 +335,7 @@ public: void msw_rescale(); - void recreate_object_list(); + void update_after_undo_redo(); private: #ifdef __WXOSX__ diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index e826d748e..9551524d4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1280,7 +1280,7 @@ struct Plater::priv PrinterTechnology printer_technology = ptFFF; Slic3r::GCodePreviewData gcode_preview_data; Slic3r::UndoRedo::Stack undo_redo_stack; - bool m_prevent_snapshots = false; /* Used for avoid of excess "snapshoting". + int m_prevent_snapshots = 0; /* Used for avoid of excess "snapshoting". * Like for "delete selected" or "set numbers of copies" * we should call tack_snapshot just ones * instead of calls for each action separately @@ -1587,8 +1587,9 @@ struct Plater::priv void take_snapshot(const std::string& snapshot_name) { - if (this->m_prevent_snapshots) + if (this->m_prevent_snapshots > 0) return; + assert(this->m_prevent_snapshots >= 0); this->undo_redo_stack.take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection()); } void take_snapshot(const wxString& snapshot_name) { this->take_snapshot(std::string(snapshot_name.ToUTF8().data())); } @@ -1597,8 +1598,8 @@ struct Plater::priv void redo(); void undo_to(size_t time_to_load); void redo_to(size_t time_to_load); - void suppress_snapshots() { this->m_prevent_snapshots = true; } - void allow_snapshots() { this->m_prevent_snapshots = false; } + void suppress_snapshots() { this->m_prevent_snapshots++; } + void allow_snapshots() { this->m_prevent_snapshots--; } bool background_processing_enabled() const { return this->get_config("background_processing") == "1"; } void update_print_volume_state(); @@ -3611,9 +3612,8 @@ void Plater::priv::update_after_undo_redo() //YS_FIXME update obj_list from the deserialized model (maybe store ObjectIDs into the tree?) (no selections at this point of time) this->view3D->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack.selection_deserialized().mode), this->undo_redo_stack.selection_deserialized().volumes_and_instances); - wxGetApp().obj_list()->recreate_object_list(); - wxGetApp().obj_list()->update_selections(); -// selection_changed(); + wxGetApp().obj_list()->update_after_undo_redo(); + //FIXME what about the state of the manipulators? //FIXME what about the focus? Cursor in the side panel? } From 6826e31e2a1c6fb519c47a58f6052d6d2335a0ea Mon Sep 17 00:00:00 2001 From: YuSanka Date: Thu, 11 Jul 2019 23:46:23 +0200 Subject: [PATCH 61/72] Some code refactoring for settings items --- src/slic3r/GUI/GUI_ObjectList.cpp | 144 ++++++++++++++++++-------- src/slic3r/GUI/GUI_ObjectList.hpp | 10 +- src/slic3r/GUI/GUI_ObjectSettings.cpp | 127 ++++++++++------------- src/slic3r/GUI/GUI_ObjectSettings.hpp | 2 +- 4 files changed, 163 insertions(+), 120 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index dc4bb8795..53c0c653e 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -25,7 +25,7 @@ namespace GUI wxDEFINE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent); // pt_FFF -FreqSettingsBundle FREQ_SETTINGS_BUNDLE_FFF = +SettingsBundle FREQ_SETTINGS_BUNDLE_FFF = { { L("Layers and Perimeters"), { "layer_height" , "perimeters", "top_solid_layers", "bottom_solid_layers" } }, { L("Infill") , { "fill_density", "fill_pattern" } }, @@ -36,7 +36,7 @@ FreqSettingsBundle FREQ_SETTINGS_BUNDLE_FFF = }; // pt_SLA -FreqSettingsBundle FREQ_SETTINGS_BUNDLE_SLA = +SettingsBundle FREQ_SETTINGS_BUNDLE_SLA = { { L("Pad and Support") , { "supports_enable", "pad_enable" } } }; @@ -676,10 +676,7 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol const wxDataViewItem& vol_item = m_objects_model->AddVolumeChild(object_item, wxString::FromUTF8(volume->name.c_str()), volume->type(), volume->get_mesh_errors_count()>0 , volume->config.has("extruder") ? volume->config.option("extruder")->value : 0); - auto opt_keys = volume->config.keys(); - if (!opt_keys.empty() && !((opt_keys.size() == 1) && (opt_keys[0] == "extruder"))) - select_item(m_objects_model->AddSettingsChild(vol_item)); - + add_settings_item(vol_item, &volume->config); items.Add(vol_item); } @@ -991,7 +988,7 @@ std::vector ObjectList::get_options(const bool is_part) const std::vector& ObjectList::get_options_for_bundle(const wxString& bundle_name) { - const FreqSettingsBundle& bundle = printer_technology() == ptSLA ? + const SettingsBundle& bundle = printer_technology() == ptSLA ? FREQ_SETTINGS_BUNDLE_SLA : FREQ_SETTINGS_BUNDLE_FFF; for (auto& it : bundle) @@ -1001,7 +998,7 @@ const std::vector& ObjectList::get_options_for_bundle(const wxStrin } #if 0 // if "Quick menu" is selected - FreqSettingsBundle& bundle_quick = printer_technology() == ptSLA ? + SettingsBundle& bundle_quick = printer_technology() == ptSLA ? m_freq_settings_sla: m_freq_settings_fff; for (auto& it : bundle_quick) @@ -1077,7 +1074,7 @@ void ObjectList::get_settings_choice(const wxString& category_name) if (selection_cnt > 0) { // Add selected items to the "Quick menu" - FreqSettingsBundle& freq_settings = printer_technology() == ptSLA ? + SettingsBundle& freq_settings = printer_technology() == ptSLA ? m_freq_settings_sla : m_freq_settings_fff; bool changed_existing = false; @@ -1149,8 +1146,8 @@ void ObjectList::get_settings_choice(const wxString& category_name) } - // Add settings item for object - update_settings_item(); + // Add settings item for object/sub-object and show them + show_settings(add_settings_item(GetSelection(), m_config)); } void ObjectList::get_freq_settings_choice(const wxString& bundle_name) @@ -1186,13 +1183,21 @@ void ObjectList::get_freq_settings_choice(const wxString& bundle_name) } } - // Add settings item for object - update_settings_item(); + // Add settings item for object/sub-object and show them + show_settings(add_settings_item(GetSelection(), m_config)); } -void ObjectList::update_settings_item() +void ObjectList::show_settings(const wxDataViewItem settings_item) { - auto item = GetSelection(); + if (!settings_item) + return; + + select_item(settings_item); + + // update object selection on Plater + if (!m_prevent_canvas_selection_update) + update_selections_on_canvas(); +/* auto item = GetSelection(); if (item) { if (m_objects_model->GetItemType(item) == itInstance) item = m_objects_model->GetTopParent(item); @@ -1204,12 +1209,14 @@ void ObjectList::update_settings_item() if (!m_prevent_canvas_selection_update) update_selections_on_canvas(); } - else { + else { + //# ys_FIXME ??? use case ??? auto panel = wxGetApp().sidebar().scrolled_panel(); panel->Freeze(); wxGetApp().obj_settings()->UpdateAndShow(true); panel->Thaw(); } + */ } wxMenu* ObjectList::append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type) { @@ -1520,7 +1527,7 @@ wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu) void ObjectList::create_freq_settings_popupmenu(wxMenu *menu) { // Add default settings bundles - const FreqSettingsBundle& bundle = printer_technology() == ptFFF ? + const SettingsBundle& bundle = printer_technology() == ptFFF ? FREQ_SETTINGS_BUNDLE_FFF : FREQ_SETTINGS_BUNDLE_SLA; const int extruders_cnt = extruders_count(); @@ -1535,7 +1542,7 @@ void ObjectList::create_freq_settings_popupmenu(wxMenu *menu) } #if 0 // Add "Quick" settings bundles - const FreqSettingsBundle& bundle_quick = printer_technology() == ptFFF ? + const SettingsBundle& bundle_quick = printer_technology() == ptFFF ? m_freq_settings_fff : m_freq_settings_sla; for (auto& it : bundle_quick) { @@ -1898,11 +1905,7 @@ void ObjectList::split() volume->config.option("extruder")->value : 0, false); // add settings to the part, if it has those - auto opt_keys = volume->config.keys(); - if ( !(opt_keys.size() == 1 && opt_keys[0] == "extruder") ) { - select_item(m_objects_model->AddSettingsChild(vol_item)); - Expand(vol_item); - } + add_settings_item(vol_item, &volume->config); } if (parent == item) @@ -2148,6 +2151,77 @@ void ObjectList::part_selection_changed() panel.Thaw(); } +SettingsBundle ObjectList::get_item_settings_bundle(const DynamicPrintConfig* config, const bool is_layers_range_settings) +{ + auto opt_keys = config->keys(); + if (opt_keys.empty()) + return SettingsBundle(); + + update_opt_keys(opt_keys); // update options list according to print technology + + if (opt_keys.size() == 1 && opt_keys[0] == "extruder" || + is_layers_range_settings && opt_keys.size() == 2) + return SettingsBundle(); + + const int extruders_cnt = wxGetApp().extruders_edited_cnt(); + + SettingsBundle bundle; + for (auto& opt_key : opt_keys) + { + auto category = config->def()->get(opt_key)->category; + if (category.empty() || (category == "Extruders" && extruders_cnt == 1)) + continue; + + std::vector< std::string > new_category; + + auto& cat_opt = bundle.find(category) == bundle.end() ? new_category : bundle.at(category); + cat_opt.push_back(opt_key); + if (cat_opt.size() == 1) + bundle[category] = cat_opt; + } + + return bundle; +} + +wxDataViewItem ObjectList::add_settings_item(wxDataViewItem parent_item, const DynamicPrintConfig* config) +{ + wxDataViewItem ret = wxDataViewItem(0); + + if (!parent_item) + return ret; + + const bool is_layers_range_settings = m_objects_model->GetItemType(parent_item) == itLayer; + SettingsBundle cat_options = get_item_settings_bundle(config, is_layers_range_settings); + if (cat_options.empty()) + return ret; + + std::vector categories; + categories.reserve(cat_options.size()); + for (auto& cat : cat_options) + { + if (cat.second.size() == 1 && + (cat.second[0] == "extruder" || is_layers_range_settings && cat.second[0] == "layer_height")) + continue; + + categories.push_back(cat.first); + } + + if (categories.empty()) + return ret; + + if (m_objects_model->GetItemType(parent_item) & itInstance) + parent_item = m_objects_model->GetTopParent(parent_item); + + ret = m_objects_model->IsSettingsItem(parent_item) ? parent_item : m_objects_model->GetSettingsItem(parent_item); + + if (!ret) ret = m_objects_model->AddSettingsChild(parent_item); + + m_objects_model->UpdateSettingsDigest(ret, categories); + Expand(parent_item); + + return ret; +} + void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) { auto model_object = (*m_objects)[obj_idx]; @@ -2167,13 +2241,7 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) !volume->config.has("extruder") ? 0 : volume->config.option("extruder")->value, false); - auto opt_keys = volume->config.keys(); - if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) { - const wxDataViewItem &settings_item = m_objects_model->AddSettingsChild(vol_item); - if (call_selection_changed) - select_item(settings_item); - Expand(vol_item); - } + add_settings_item(vol_item, &volume->config); } Expand(item); } @@ -2183,13 +2251,7 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) increase_object_instances(obj_idx, model_object->instances.size()); // add settings to the object, if it has those - auto opt_keys = model_object->config.keys(); - if (!opt_keys.empty() && !(opt_keys.size() == 1 && opt_keys[0] == "extruder")) { - const wxDataViewItem &settings_item = m_objects_model->AddSettingsChild(item); - if (call_selection_changed) - select_item(settings_item); - Expand(item); - } + add_settings_item(item, &model_object->config); // Add layers if it has add_layer_root_item(item); @@ -2491,11 +2553,7 @@ void ObjectList::add_layer_item(const t_layer_height_range& range, range, config.opt_int("extruder"), layer_idx); - - if (config.keys().size() > 2) { - m_objects_model->AddSettingsChild(layer_item); - Expand(layer_item); - } + add_settings_item(layer_item, &config); } bool ObjectList::edit_layer_range(const t_layer_height_range& range, coordf_t layer_height) @@ -3010,7 +3068,7 @@ void ObjectList::change_part_type() } else if (!settings_item && (new_type == ModelVolumeType::MODEL_PART || new_type == ModelVolumeType::PARAMETER_MODIFIER)) { - select_item(m_objects_model->AddSettingsChild(item)); + add_settings_item(item, &volume->config); } } diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 34efa9025..72bddeec8 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -26,7 +26,7 @@ enum class ModelVolumeType : int; // FIXME: broken build on mac os because of this is missing: typedef std::vector t_config_option_keys; -typedef std::map> FreqSettingsBundle; +typedef std::map> SettingsBundle; // category -> vector ( option ; label ) typedef std::map< std::string, std::vector< std::pair > > settings_menu_hierarchy; @@ -152,8 +152,8 @@ class ObjectList : public wxDataViewCtrl wxDataViewItem m_last_selected_item {nullptr}; #if 0 - FreqSettingsBundle m_freq_settings_fff; - FreqSettingsBundle m_freq_settings_sla; + SettingsBundle m_freq_settings_fff; + SettingsBundle m_freq_settings_sla; #endif public: @@ -209,7 +209,7 @@ public: void get_settings_choice(const wxString& category_name); void get_freq_settings_choice(const wxString& bundle_name); - void update_settings_item(); + void show_settings(const wxDataViewItem settings_item); wxMenu* append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type); void append_menu_items_add_volume(wxMenu* menu); @@ -247,6 +247,7 @@ public: void layers_editing(); wxDataViewItem add_layer_root_item(const wxDataViewItem obj_item); + wxDataViewItem add_settings_item(wxDataViewItem parent_item, const DynamicPrintConfig* config); DynamicPrintConfig get_default_layer_config(const int obj_idx); bool get_volume_by_item(const wxDataViewItem& item, ModelVolume*& volume); @@ -258,6 +259,7 @@ public: wxBoxSizer* get_sizer() {return m_sizer;} int get_selected_obj_idx() const; DynamicPrintConfig& get_item_config(const wxDataViewItem& item) const; + SettingsBundle get_item_settings_bundle(const DynamicPrintConfig* config, const bool is_layers_range_settings); void changed_object(const int obj_idx = -1) const; void part_selection_changed(); diff --git a/src/slic3r/GUI/GUI_ObjectSettings.cpp b/src/slic3r/GUI/GUI_ObjectSettings.cpp index ab2614895..16c64360a 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.cpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.cpp @@ -63,20 +63,28 @@ ObjectSettings::ObjectSettings(wxWindow* parent) : m_bmp_delete = ScalableBitmap(parent, "cross"); } -void ObjectSettings::update_settings_list() +bool ObjectSettings::update_settings_list() { m_settings_list_sizer->Clear(true); + m_og_settings.resize(0); auto objects_ctrl = wxGetApp().obj_list(); auto objects_model = wxGetApp().obj_list()->GetModel(); auto config = wxGetApp().obj_list()->config(); const auto item = objects_ctrl->GetSelection(); - const bool is_layers_range_settings = objects_model->GetItemType(objects_model->GetParent(item)) == itLayer; + + if (!item || !objects_model->IsSettingsItem(item) || !config || objects_ctrl->multiple_selection()) + return false; + + const bool is_layers_range_settings = objects_model->GetItemType(objects_model->GetParent(item)) == itLayer; + SettingsBundle cat_options = objects_ctrl->get_item_settings_bundle(config, is_layers_range_settings); + + if (!cat_options.empty()) + { + std::vector categories; + categories.reserve(cat_options.size()); - if (item && !objects_ctrl->multiple_selection() && - config && objects_model->IsSettingsItem(item)) - { auto extra_column = [config, this](wxWindow* parent, const Line& line) { auto opt_key = (line.get_options())[0].opt_id; //we assume that we have one option per line @@ -96,86 +104,61 @@ void ObjectSettings::update_settings_list() return btn; }; - std::map> cat_options; - auto opt_keys = config->keys(); - objects_ctrl->update_opt_keys(opt_keys); // update options list according to print technology - - m_og_settings.resize(0); - std::vector categories; - if (!(opt_keys.size() == 1 && opt_keys[0] == "extruder"))// return; + for (auto& cat : cat_options) { - const int extruders_cnt = wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA ? 1 : - wxGetApp().preset_bundle->printers.get_edited_preset().config.option("nozzle_diameter")->values.size(); + if (cat.second.size() == 1 && + (cat.second[0] == "extruder" || is_layers_range_settings && cat.second[0] == "layer_height")) + continue; - for (auto& opt_key : opt_keys) { - auto category = config->def()->get(opt_key)->category; - if (category.empty() || - (category == "Extruders" && extruders_cnt == 1)) continue; + categories.push_back(cat.first); - std::vector< std::string > new_category; + auto optgroup = std::make_shared(m_og->ctrl_parent(), _(cat.first), config, false, extra_column); + optgroup->label_width = 15; + optgroup->sidetext_width = 5.5; - auto& cat_opt = cat_options.find(category) == cat_options.end() ? new_category : cat_options.at(category); - cat_opt.push_back(opt_key); - if (cat_opt.size() == 1) - cat_options[category] = cat_opt; - } + optgroup->m_on_change = [](const t_config_option_key& opt_id, const boost::any& value) { + wxGetApp().obj_list()->changed_object(); }; - for (auto& cat : cat_options) { - if (cat.second.size() == 1 && - (cat.second[0] == "extruder" || is_layers_range_settings && cat.second[0] == "layer_height")) + // call back for rescaling of the extracolumn control + optgroup->rescale_extra_column_item = [this](wxWindow* win) { + auto *ctrl = dynamic_cast(win); + if (ctrl == nullptr) + return; + ctrl->SetBitmap_(m_bmp_delete); + }; + + const bool is_extruders_cat = cat.first == "Extruders"; + for (auto& opt : cat.second) + { + if (opt == "extruder" || is_layers_range_settings && opt == "layer_height") continue; - - auto optgroup = std::make_shared(m_og->ctrl_parent(), _(cat.first), config, false, extra_column); - optgroup->label_width = 15; - optgroup->sidetext_width = 5.5; - - optgroup->m_on_change = [](const t_config_option_key& opt_id, const boost::any& value) { - wxGetApp().obj_list()->changed_object(); }; - - const bool is_extruders_cat = cat.first == "Extruders"; - for (auto& opt : cat.second) - { - if (opt == "extruder" || is_layers_range_settings && opt == "layer_height") - continue; - Option option = optgroup->get_option(opt); - option.opt.width = 12; - if (is_extruders_cat) - option.opt.max = wxGetApp().extruders_cnt(); - optgroup->append_single_option_line(option); - } - optgroup->reload_config(); - m_settings_list_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0); - - // call back for rescaling of the extracolumn control - optgroup->rescale_extra_column_item = [this](wxWindow* win) { - auto *ctrl = dynamic_cast(win); - if (ctrl == nullptr) - return; - ctrl->SetBitmap_(m_bmp_delete); - }; - - m_og_settings.push_back(optgroup); - - categories.push_back(cat.first); + Option option = optgroup->get_option(opt); + option.opt.width = 12; + if (is_extruders_cat) + option.opt.max = wxGetApp().extruders_edited_cnt(); + optgroup->append_single_option_line(option); } + optgroup->reload_config(); + + m_settings_list_sizer->Add(optgroup->sizer, 0, wxEXPAND | wxALL, 0); + m_og_settings.push_back(optgroup); } - if (m_og_settings.empty()) { - objects_ctrl->select_item(objects_model->Delete(item)); - } - else { - if (!categories.empty()) - objects_model->UpdateSettingsDigest(item, categories); - } - } + if (!categories.empty()) + objects_model->UpdateSettingsDigest(item, categories); + } + else + { + objects_ctrl->select_item(objects_model->Delete(item)); + return false; + } + + return true; } void ObjectSettings::UpdateAndShow(const bool show) { - if (show) - update_settings_list(); - - OG_Settings::UpdateAndShow(show); + OG_Settings::UpdateAndShow(show ? update_settings_list() : false); } void ObjectSettings::msw_rescale() diff --git a/src/slic3r/GUI/GUI_ObjectSettings.hpp b/src/slic3r/GUI/GUI_ObjectSettings.hpp index 3d49f13b7..01daa5622 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.hpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.hpp @@ -44,7 +44,7 @@ public: ObjectSettings(wxWindow* parent); ~ObjectSettings() {} - void update_settings_list(); + bool update_settings_list(); void UpdateAndShow(const bool show) override; void msw_rescale(); }; From 4d8a028262f2911327e371b7c2e4863fb75ecf5d Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 12 Jul 2019 13:55:46 +0200 Subject: [PATCH 62/72] Finally fix for settings item selection --- src/slic3r/GUI/GUI_ObjectList.cpp | 52 ++++++++++++++++---- src/slic3r/GUI/GUI_ObjectList.hpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 10 ++-- 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 53c0c653e..9e681257c 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -2183,6 +2183,7 @@ SettingsBundle ObjectList::get_item_settings_bundle(const DynamicPrintConfig* co return bundle; } +// Add new SettingsItem for parent_item if it doesn't exist, or just update a digest according to new config wxDataViewItem ObjectList::add_settings_item(wxDataViewItem parent_item, const DynamicPrintConfig* config) { wxDataViewItem ret = wxDataViewItem(0); @@ -3100,6 +3101,7 @@ bool ObjectList::has_multi_part_objects() return false; } +/* #lm_FIXME_delete_after_testing void ObjectList::update_settings_items() { m_prevent_canvas_selection_update = true; @@ -3125,22 +3127,52 @@ void ObjectList::update_settings_items() SetSelections(sel); m_prevent_canvas_selection_update = false; } +*/ +void ObjectList::update_and_show_object_settings_item() +{ + const wxDataViewItem item = GetSelection(); + if (!item) return; + + const wxDataViewItem& obj_item = m_objects_model->IsSettingsItem(item) ? m_objects_model->GetParent(item) : item; + select_item(add_settings_item(obj_item, &get_item_config(obj_item))); +} // Update settings item for item had it void ObjectList::update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections) { - const wxDataViewItem& settings_item = m_objects_model->GetSettingsItem(item); - select_item(settings_item ? settings_item : m_objects_model->AddSettingsChild(item)); + const wxDataViewItem old_settings_item = m_objects_model->GetSettingsItem(item); + const wxDataViewItem new_settings_item = add_settings_item(item, &get_item_config(item)); - // If settings item was deleted from the list, - // it's need to be deleted from selection array, if it was there - if (settings_item != m_objects_model->GetSettingsItem(item) && - selections.Index(settings_item) != wxNOT_FOUND) { - selections.Remove(settings_item); + if (!new_settings_item && old_settings_item) + m_objects_model->Delete(old_settings_item); - // Select item, if settings_item doesn't exist for item anymore, but was selected - if (selections.Index(item) == wxNOT_FOUND) - selections.Add(item); + // if ols settings item was is selected area + if (selections.Index(old_settings_item) != wxNOT_FOUND) + { + // If settings item was just updated + if (old_settings_item == new_settings_item) + { + Sidebar& panel = wxGetApp().sidebar(); + panel.Freeze(); + + // update settings list + wxGetApp().obj_settings()->UpdateAndShow(true); + + panel.Layout(); + panel.Thaw(); + } + else + // If settings item was deleted from the list, + // it's need to be deleted from selection array, if it was there + { + selections.Remove(old_settings_item); + + // Select item, if settings_item doesn't exist for item anymore, but was selected + if (selections.Index(item) == wxNOT_FOUND) { + selections.Add(item); + select_item(item); // to correct update of the SettingsList and ManipulationPanel sizers + } + } } } diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 72bddeec8..2d7e9f5f1 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -319,6 +319,7 @@ public: void last_volume_is_deleted(const int obj_idx); bool has_multi_part_objects(); void update_settings_items(); + void update_and_show_object_settings_item(); void update_settings_item_and_selection(wxDataViewItem item, wxDataViewItemArray& selections); void update_object_list_by_printer_technology(); void update_object_menu(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index a4a6d1459..028320982 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -928,10 +928,12 @@ RENDER_AGAIN: } if (value_changed) { // Update side panel - wxTheApp->CallAfter([]() { - wxGetApp().obj_settings()->UpdateAndShow(true); - wxGetApp().obj_list()->update_settings_items(); - }); +/* wxTheApp->CallAfter([]() { + * wxGetApp().obj_settings()->UpdateAndShow(true); + * wxGetApp().obj_list()->update_settings_items(); + * }); + * #lm_FIXME_delete_after_testing */ + wxGetApp().obj_list()->update_and_show_object_settings_item(); } bool generate = m_imgui->button(m_desc.at("auto_generate")); From 63cf5edf28430d1040ec81b8fb93ddd162ac5f9f Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 15 Jul 2019 11:49:30 +0200 Subject: [PATCH 63/72] Updated tooltips on custom gcodes to match actual PrusaSlicer behaviour to reflect recent changes --- src/libslic3r/PrintConfig.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 979d9b46e..06bb0811f 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -368,7 +368,8 @@ void PrintConfigDef::init_fff_params() def = this->add("end_filament_gcode", coStrings); def->label = L("End G-code"); - def->tooltip = L("This end procedure is inserted at the end of the output file, before the printer end gcode. " + def->tooltip = L("This end procedure is inserted at the end of the output file, before the printer end gcode (and " + "before any toolchange from this filament in case of multimaterial printers). " "Note that you can use placeholder variables for all Slic3r settings. " "If you have multiple extruders, the gcode is processed in extruder order."); def->multiline = true; @@ -1787,7 +1788,8 @@ void PrintConfigDef::init_fff_params() def = this->add("start_filament_gcode", coStrings); def->label = L("Start G-code"); - def->tooltip = L("This start procedure is inserted at the beginning, after any printer start gcode. " + def->tooltip = L("This start procedure is inserted at the beginning, after any printer start gcode (and " + "after any toolchange to this filament in case of multi-material printers). " "This is used to override settings for a specific filament. If Slic3r detects " "M104, M109, M140 or M190 in your custom codes, such commands will " "not be prepended automatically so you're free to customize the order " @@ -2042,9 +2044,10 @@ void PrintConfigDef::init_fff_params() def = this->add("toolchange_gcode", coString); def->label = L("Tool change G-code"); - def->tooltip = L("This custom code is inserted right before every extruder change. " - "Note that you can use placeholder variables for all Slic3r settings as well " - "as [previous_extruder] and [next_extruder]."); + def->tooltip = L("This custom code is inserted at every extruder change. If you don't leave this empty, you are " + "expected to take care of the toolchange yourself - PrusaSlicer will not output any other G-code to " + "change the filament. You can use placeholder variables for all Slic3r settings as well as [previous_extruder] " + "and [next_extruder], so e.g. the standard toolchange command can be scripted as T[next_extruder]."); def->multiline = true; def->full_width = true; def->height = 5; From a492360d198e5cee4bea21e6d1c44386ec3824a5 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 15 Jul 2019 11:59:54 +0200 Subject: [PATCH 64/72] Fix of the merge - missing Undo / Redo toolbar buttons. --- src/slic3r/GUI/GLCanvas3D.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index c2948c262..a878527bb 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3647,9 +3647,7 @@ bool GLCanvas3D::_init_toolbar() return false; item.name = "undo"; -#if ENABLE_SVG_ICONS item.icon_filename = "undo_toolbar.svg"; -#endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Undo")) + " [" + GUI::shortkey_ctrl_prefix() + "Z]"; item.sprite_id = 11; item.left.toggable = false; @@ -3663,9 +3661,7 @@ bool GLCanvas3D::_init_toolbar() return false; item.name = "redo"; -#if ENABLE_SVG_ICONS item.icon_filename = "redo_toolbar.svg"; -#endif // ENABLE_SVG_ICONS item.tooltip = _utf8(L("Redo")) + " [" + GUI::shortkey_ctrl_prefix() + "Y]"; item.sprite_id = 12; item.left.action_callback = [this]() { post_event(SimpleEvent(EVT_GLCANVAS_REDO)); }; From d2a3a36013050cf0c7eaed9007a05556baea0b42 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Mon, 15 Jul 2019 15:51:25 +0200 Subject: [PATCH 65/72] Fix of the SLA Undo --- src/slic3r/GUI/GLCanvas3D.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index a878527bb..a401cabba 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2016,9 +2016,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re assert(it != aux_volume_state.end() && it->geometry_id == key.geometry_id); if (it->new_geometry()) instances[istep].emplace_back(std::pair(instance_idx, print_instance_idx)); - else - // Recycling an old GLVolume. Update the Object/Instance indices into the current Model. - m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx); + else { + // Recycling an old GLVolume. Update the Object/Instance indices into the current Model. + m_volumes.volumes[it->volume_idx]->composite_id = GLVolume::CompositeID(object_idx, m_volumes.volumes[it->volume_idx]->volume_idx(), instance_idx); + m_volumes.volumes[it->volume_idx]->set_instance_transformation(model_object->instances[instance_idx]->get_transformation()); + } } } From dc80616bf6b5f5232fe30d96dcfa024da83ab0b9 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Mon, 15 Jul 2019 17:09:06 +0200 Subject: [PATCH 66/72] Fixed a use-after-free problem in object list this was uncovered by ASAN when attempting to Delete All objects with multiple instances --- src/slic3r/GUI/wxExtensions.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 1def2915c..7e8a2d92d 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -806,8 +806,12 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item) if (node_parent) { if (node->m_type & (itInstanceRoot|itLayerRoot)) { - for (int i = node->GetChildCount() - 1; i >= (node->m_type & itInstanceRoot ? 1 : 0); i--) + // node can be deleted by the Delete, let's check its type while we safely can + bool is_instance_root = (node->m_type & itInstanceRoot); + + for (int i = node->GetChildCount() - 1; i >= (is_instance_root ? 1 : 0); i--) Delete(wxDataViewItem(node->GetNthChild(i))); + return parent; } From 4865240a9cd79069cc9e72bfa2e44451c55ef5c6 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 16 Jul 2019 09:19:00 +0200 Subject: [PATCH 67/72] Fixed compilation issue --- src/libslic3r/PrintObject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 72fab8739..37cf0cccc 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2351,7 +2351,7 @@ void PrintObject::discover_horizontal_shells() // Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == S_TYPE_TOP) ? 'top' : 'bottom'; size_t solid_layers = (type == stTop) ? region_config.top_solid_layers.value : region_config.bottom_solid_layers.value; - for (int n = (type == stTop) ? i-1 : i+1; std::abs(n - i) < solid_layers; (type == stTop) ? -- n : ++ n) { + for (int n = (type == stTop) ? i-1 : i+1; std::abs(n - (int)i) < solid_layers; (type == stTop) ? -- n : ++ n) { if (n < 0 || n >= int(m_layers.size())) continue; // Slic3r::debugf " looking for neighbors on layer %d...\n", $n; From 52ab8a5f19892af2874a670f19e93b8bcd94a00e Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Tue, 16 Jul 2019 13:06:58 +0200 Subject: [PATCH 68/72] Wipe tower fix (do not skip the first toolchange when printing without the wipe tower) Also, test multi.t updated so it matches new logic of inserting custom gcodes --- src/libslic3r/GCode.cpp | 21 ++++++++++----------- t/multi.t | 4 +++- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index cd0f8142d..37a14d42c 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -2811,17 +2811,16 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z) gcode += m_ooze_prevention.pre_toolchange(*this); const std::string& toolchange_gcode = m_config.toolchange_gcode.value; - if (m_writer.extruder() != nullptr) { - // Process the custom toolchange_gcode. If it is empty, insert just a Tn command. - if (!toolchange_gcode.empty()) { - DynamicConfig config; - config.set_key_value("previous_extruder", new ConfigOptionInt((int)m_writer.extruder()->id())); - config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id)); - config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); - gcode += placeholder_parser_process("toolchange_gcode", toolchange_gcode, extruder_id, &config); - check_add_eol(gcode); - } + + // Process the custom toolchange_gcode. If it is empty, insert just a Tn command. + if (!toolchange_gcode.empty()) { + DynamicConfig config; + config.set_key_value("previous_extruder", new ConfigOptionInt((int)(m_writer.extruder() != nullptr ? m_writer.extruder()->id() : -1 ))); + config.set_key_value("next_extruder", new ConfigOptionInt((int)extruder_id)); + config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); + config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); + gcode += placeholder_parser_process("toolchange_gcode", toolchange_gcode, extruder_id, &config); + check_add_eol(gcode); } // We inform the writer about what is happening, but we may not use the resulting gcode. diff --git a/t/multi.t b/t/multi.t index 75ce0c286..8e7225bec 100644 --- a/t/multi.t +++ b/t/multi.t @@ -25,7 +25,9 @@ use Slic3r::Test; $config->set('extruder_offset', [ [0,0], [20,0], [0,20], [20,20] ]); $config->set('temperature', [200, 180, 170, 160]); $config->set('first_layer_temperature', [206, 186, 166, 156]); - $config->set('toolchange_gcode', ';toolchange'); # test that it doesn't crash when this is supplied + $config->set('toolchange_gcode', 'T[next_extruder] ;toolchange'); # test that it doesn't crash when this is supplied + # Since July 2019, PrusaSlicer only emits automatic Tn command in case that the toolchange_gcode is empty + # The "T[next_extruder]" is therefore needed in this test. my $print = Slic3r::Test::init_print('20mm_cube', config => $config); From 21624f53058b1cdb8afba0a9c6eb97c1ad997b15 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 17 Jul 2019 08:38:48 +0200 Subject: [PATCH 69/72] Framework to serialize gizmos into undo/redo stack Serialization into undo/redo of Cut gizmo Refactoring of GLGizmosManager --- src/slic3r/GUI/GLCanvas3D.cpp | 36 ++-- src/slic3r/GUI/GLCanvas3D.hpp | 3 + src/slic3r/GUI/Gizmos/GLGizmoBase.cpp | 1 + src/slic3r/GUI/Gizmos/GLGizmoBase.hpp | 6 + src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 1 - src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 2 + src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 211 +++++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 80 +++++--- src/slic3r/GUI/Plater.cpp | 25 +-- src/slic3r/Utils/UndoRedo.cpp | 50 +++-- src/slic3r/Utils/UndoRedo.hpp | 7 +- 11 files changed, 254 insertions(+), 168 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index a401cabba..9e05f60ad 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1719,7 +1719,7 @@ void GLCanvas3D::deselect_all() m_selection.set_mode(Selection::Instance); wxGetApp().obj_manipul()->set_dirty(); m_gizmos.reset_all_states(); - m_gizmos.update_data(*this); + m_gizmos.update_data(); post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); } @@ -2082,8 +2082,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re m_selection.volumes_changed(map_glvolume_old_to_new); } - m_gizmos.update_data(*this); - m_gizmos.refresh_on_off_state(m_selection); + m_gizmos.update_data(); + m_gizmos.refresh_on_off_state(); // Update the toolbar if (update_object_list) @@ -2323,7 +2323,7 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) if ((keyCode == WXK_ESCAPE) && _deactivate_undo_redo_toolbar_items()) return; - if (m_gizmos.on_char(evt, *this)) + if (m_gizmos.on_char(evt)) return; //#ifdef __APPLE__ @@ -2450,7 +2450,7 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) } else { - if (!m_gizmos.on_key(evt, *this)) + if (!m_gizmos.on_key(evt)) { if (evt.GetEventType() == wxEVT_KEY_UP) { if (m_tab_down && keyCode == WXK_TAB && !evt.HasAnyModifiers()) { @@ -2560,7 +2560,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt) } // Inform gizmos about the event so they have the opportunity to react. - if (m_gizmos.on_mouse_wheel(evt, *this)) + if (m_gizmos.on_mouse_wheel(evt)) return; // Calculate the zoom delta and apply it to the current zoom factor @@ -2688,7 +2688,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) return; } - if (m_gizmos.on_mouse(evt, *this)) + if (m_gizmos.on_mouse(evt)) { if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) mouse_up_cleanup(); @@ -2815,9 +2815,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (m_selection.is_empty()) m_gizmos.reset_all_states(); else - m_gizmos.refresh_on_off_state(m_selection); + m_gizmos.refresh_on_off_state(); - m_gizmos.update_data(*this); + m_gizmos.update_data(); post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); m_dirty = true; } @@ -2985,9 +2985,9 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) { // forces the selection of the volume m_selection.add(volume_idx); - m_gizmos.refresh_on_off_state(m_selection); + m_gizmos.refresh_on_off_state(); post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); - m_gizmos.update_data(*this); + m_gizmos.update_data(); wxGetApp().obj_manipul()->set_dirty(); // forces a frame render to update the view before the context menu is shown render(); @@ -3355,8 +3355,8 @@ void GLCanvas3D::set_camera_zoom(double zoom) void GLCanvas3D::update_gizmos_on_off_state() { set_as_dirty(); - m_gizmos.update_data(*this); - m_gizmos.refresh_on_off_state(get_selection()); + m_gizmos.update_data(); + m_gizmos.refresh_on_off_state(); } void GLCanvas3D::handle_sidebar_focus_event(const std::string& opt_key, bool focus_on) @@ -3762,7 +3762,7 @@ void GLCanvas3D::_picking_pass() const if (m_camera_clipping_plane.is_active()) ::glDisable(GL_CLIP_PLANE0); - m_gizmos.render_current_gizmo_for_picking_pass(m_selection); + m_gizmos.render_current_gizmo_for_picking_pass(); if (m_multisample_allowed) glsafe(::glEnable(GL_MULTISAMPLE)); @@ -4072,7 +4072,7 @@ void GLCanvas3D::_render_volumes_for_picking() const void GLCanvas3D::_render_current_gizmo() const { - m_gizmos.render_current_gizmo(m_selection); + m_gizmos.render_current_gizmo(); } void GLCanvas3D::_render_gizmos_overlay() const @@ -4088,7 +4088,7 @@ void GLCanvas3D::_render_gizmos_overlay() const m_gizmos.set_overlay_icon_size(size); //! #ys_FIXME_experiment #endif /* __WXMSW__ */ - m_gizmos.render_overlay(*this, m_selection); + m_gizmos.render_overlay(); } void GLCanvas3D::_render_toolbar() const @@ -5661,9 +5661,9 @@ void GLCanvas3D::_update_selection_from_hover() if (m_selection.is_empty()) m_gizmos.reset_all_states(); else - m_gizmos.refresh_on_off_state(m_selection); + m_gizmos.refresh_on_off_state(); - m_gizmos.update_data(*this); + m_gizmos.update_data(); post_event(SimpleEvent(EVT_GLCANVAS_OBJECT_SELECT)); m_dirty = true; } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index f5a53c6a5..6bb17da4a 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -517,6 +517,9 @@ public: const Selection& get_selection() const { return m_selection; } Selection& get_selection() { return m_selection; } + const GLGizmosManager& get_gizmos_manager() const { return m_gizmos; } + GLGizmosManager& get_gizmos_manager() { return m_gizmos; } + void bed_shape_changed(); void set_clipping_plane(unsigned int id, const ClippingPlane& plane) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 7da3dec8a..ba0ea4825 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -132,6 +132,7 @@ void GLGizmoBase::Grabber::render_face(float half_size) const glsafe(::glEnd()); } + GLGizmoBase::GLGizmoBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : m_parent(parent) , m_group_id(-1) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index 73c73a2c9..88927aee2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -6,6 +6,7 @@ #include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/Selection.hpp" +#include class wxWindow; class GLUquadric; @@ -105,6 +106,9 @@ public: bool init() { return on_init(); } + void load(cereal::BinaryInputArchive& ar) { m_state = On; on_load(ar); } + void save(cereal::BinaryOutputArchive& ar) const { on_save(ar); } + std::string get_name() const { return on_get_name(); } int get_group_id() const { return m_group_id; } @@ -144,6 +148,8 @@ public: protected: virtual bool on_init() = 0; + virtual void on_load(cereal::BinaryInputArchive& ar) {} + virtual void on_save(cereal::BinaryOutputArchive& ar) const {} virtual std::string on_get_name() const = 0; virtual void on_set_state() {} virtual void on_set_hover_id() {} diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index b1c295098..52174d2d6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -28,7 +28,6 @@ GLGizmoCut::GLGizmoCut(GLCanvas3D& parent, const std::string& icon_filename, uns , m_rotate_lower(false) {} - bool GLGizmoCut::on_init() { m_grabbers.emplace_back(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 79a7c58e2..628bcf508 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -27,6 +27,8 @@ public: protected: virtual bool on_init(); + virtual void on_load(cereal::BinaryInputArchive& ar) { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } + virtual void on_save(cereal::BinaryOutputArchive& ar) const { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } virtual std::string on_get_name() const; virtual void on_set_state(); virtual bool on_is_activable(const Selection& selection) const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 602235d4a..5922f9600 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -15,7 +15,8 @@ namespace GUI { const float GLGizmosManager::Default_Icons_Size = 64; GLGizmosManager::GLGizmosManager() - : m_enabled(false) + : m_parent(nullptr) + , m_enabled(false) , m_icons_texture_dirty(true) , m_current(Undefined) , m_overlay_icons_size(Default_Icons_Size) @@ -23,6 +24,7 @@ GLGizmosManager::GLGizmosManager() , m_overlay_border(5.0f) , m_overlay_gap_y(5.0f) , m_tooltip("") + , m_serializing(false) { } @@ -33,6 +35,8 @@ GLGizmosManager::~GLGizmosManager() bool GLGizmosManager::init(GLCanvas3D& parent) { + m_parent = &parent; + m_background_texture.metadata.filename = "toolbar_background.png"; m_background_texture.metadata.left = 16; m_background_texture.metadata.top = 16; @@ -135,12 +139,18 @@ void GLGizmosManager::set_overlay_scale(float scale) } } -void GLGizmosManager::refresh_on_off_state(const Selection& selection) +void GLGizmosManager::refresh_on_off_state() { + if (m_parent == nullptr) + return; + + if (m_serializing) + return; + GizmosMap::iterator it = m_gizmos.find(m_current); if ((it != m_gizmos.end()) && (it->second != nullptr)) { - if (!it->second->is_activable(selection)) + if (!it->second->is_activable(m_parent->get_selection())) { it->second->set_state(GLGizmoBase::Off); m_current = Undefined; @@ -153,6 +163,9 @@ void GLGizmosManager::reset_all_states() if (!m_enabled) return; + if (m_serializing) + return; + for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { if (it->second != nullptr) @@ -192,22 +205,22 @@ void GLGizmosManager::enable_grabber(EType type, unsigned int id, bool enable) } } -void GLGizmosManager::update(const Linef3& mouse_ray, const Selection& selection, const Point* mouse_pos) +void GLGizmosManager::update(const Linef3& mouse_ray, const Point* mouse_pos) { - if (!m_enabled) + if (!m_enabled || (m_parent == nullptr)) return; GLGizmoBase* curr = get_current(); if (curr != nullptr) - curr->update(GLGizmoBase::UpdateData(mouse_ray, mouse_pos), selection); + curr->update(GLGizmoBase::UpdateData(mouse_ray, mouse_pos), m_parent->get_selection()); } -void GLGizmosManager::update_data(GLCanvas3D& canvas) +void GLGizmosManager::update_data() { - if (!m_enabled) + if (!m_enabled || (m_parent == nullptr)) return; - const Selection& selection = canvas.get_selection(); + const Selection& selection = m_parent->get_selection(); bool is_wipe_tower = selection.is_wipe_tower(); enable_grabber(Move, 2, !is_wipe_tower); @@ -228,7 +241,7 @@ void GLGizmosManager::update_data(GLCanvas3D& canvas) set_rotation(Vec3d::Zero()); ModelObject* model_object = selection.get_model()->objects[selection.get_object_idx()]; set_flattening_data(model_object); - set_sla_support_data(model_object, selection); + set_sla_support_data(model_object); } else if (selection.is_single_volume() || selection.is_single_modifier()) { @@ -236,7 +249,7 @@ void GLGizmosManager::update_data(GLCanvas3D& canvas) set_scale(volume->get_volume_scaling_factor()); set_rotation(Vec3d::Zero()); set_flattening_data(nullptr); - set_sla_support_data(nullptr, selection); + set_sla_support_data(nullptr); } else if (is_wipe_tower) { @@ -244,14 +257,14 @@ void GLGizmosManager::update_data(GLCanvas3D& canvas) set_scale(Vec3d::Ones()); set_rotation(Vec3d(0., 0., (M_PI/180.) * dynamic_cast(config.option("wipe_tower_rotation_angle"))->value)); set_flattening_data(nullptr); - set_sla_support_data(nullptr, selection); + set_sla_support_data(nullptr); } else { set_scale(Vec3d::Ones()); set_rotation(Vec3d::Zero()); set_flattening_data(selection.is_from_single_object() ? selection.get_model()->objects[selection.get_object_idx()] : nullptr); - set_sla_support_data(nullptr, selection); + set_sla_support_data(nullptr); } } @@ -264,9 +277,13 @@ bool GLGizmosManager::is_running() const return (curr != nullptr) ? (curr->get_state() == GLGizmoBase::On) : false; } -bool GLGizmosManager::handle_shortcut(int key, const Selection& selection) +bool GLGizmosManager::handle_shortcut(int key) { - if (!m_enabled || selection.is_empty()) + if (!m_enabled || (m_parent == nullptr)) + return false; + + const Selection& selection = m_parent->get_selection(); + if (selection.is_empty()) return false; EType old_current = m_current; @@ -314,14 +331,14 @@ bool GLGizmosManager::is_dragging() const return (curr != nullptr) ? curr->is_dragging() : false; } -void GLGizmosManager::start_dragging(const Selection& selection) +void GLGizmosManager::start_dragging() { - if (!m_enabled) + if (!m_enabled || (m_parent == nullptr)) return; GLGizmoBase* curr = get_current(); if (curr != nullptr) - curr->start_dragging(selection); + curr->start_dragging(m_parent->get_selection()); } void GLGizmosManager::stop_dragging() @@ -409,14 +426,14 @@ void GLGizmosManager::set_flattening_data(const ModelObject* model_object) reinterpret_cast(it->second)->set_flattening_data(model_object); } -void GLGizmosManager::set_sla_support_data(ModelObject* model_object, const Selection& selection) +void GLGizmosManager::set_sla_support_data(ModelObject* model_object) { - if (!m_enabled) + if (!m_enabled || (m_parent == nullptr)) return; GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); if (it != m_gizmos.end()) - reinterpret_cast(it->second)->set_sla_support_data(model_object, selection); + reinterpret_cast(it->second)->set_sla_support_data(model_object, m_parent->get_selection()); } // Returns true if the gizmo used the event to do something, false otherwise. @@ -445,39 +462,42 @@ ClippingPlane GLGizmosManager::get_sla_clipping_plane() const } -void GLGizmosManager::render_current_gizmo(const Selection& selection) const +void GLGizmosManager::render_current_gizmo() const { - if (!m_enabled) + if (!m_enabled || (m_parent == nullptr)) return; GLGizmoBase* curr = get_current(); if (curr != nullptr) - curr->render(selection); + curr->render(m_parent->get_selection()); } -void GLGizmosManager::render_current_gizmo_for_picking_pass(const Selection& selection) const +void GLGizmosManager::render_current_gizmo_for_picking_pass() const { - if (!m_enabled) + if (!m_enabled || (m_parent == nullptr)) return; GLGizmoBase* curr = get_current(); if (curr != nullptr) - curr->render_for_picking(selection); + curr->render_for_picking(m_parent->get_selection()); } -void GLGizmosManager::render_overlay(const GLCanvas3D& canvas, const Selection& selection) const +void GLGizmosManager::render_overlay() const { - if (!m_enabled) + if (!m_enabled || (m_parent == nullptr)) return; if (m_icons_texture_dirty) generate_icons_texture(); - do_render_overlay(canvas, selection); + do_render_overlay(); } -bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt, GLCanvas3D& canvas) +bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt) { + if (m_parent == nullptr) + return false; + bool processed = false; if (m_current == SlaSupports) { @@ -489,14 +509,15 @@ bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt, GLCanvas3D& canvas) return processed; } - - -bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas) +bool GLGizmosManager::on_mouse(wxMouseEvent& evt) { + if (m_parent == nullptr) + return false; + Point pos(evt.GetX(), evt.GetY()); Vec2d mouse_pos((double)evt.GetX(), (double)evt.GetY()); - Selection& selection = canvas.get_selection(); + Selection& selection = m_parent->get_selection(); int selected_object_idx = selection.get_object_idx(); bool processed = false; @@ -512,7 +533,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas) // mouse anywhere if (evt.Moving()) - m_tooltip = update_hover_state(canvas, mouse_pos); + m_tooltip = update_hover_state(mouse_pos); else if (evt.LeftUp()) m_mouse_capture.left = false; else if (evt.MiddleUp()) @@ -523,7 +544,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas) // if the button down was done on this toolbar, prevent from dragging into the scene processed = true; - if (!overlay_contains_mouse(canvas, mouse_pos)) + if (!overlay_contains_mouse(mouse_pos)) { // mouse is outside the toolbar m_tooltip = ""; @@ -535,40 +556,40 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas) processed = true; else if (!selection.is_empty() && grabber_contains_mouse()) { - update_data(canvas); + update_data(); selection.start_dragging(); - start_dragging(selection); + start_dragging(); if (m_current == Flatten) { // Rotate the object so the normal points downward: - canvas.do_flatten(get_flattening_normal(), "Place on Face"); + m_parent->do_flatten(get_flattening_normal(), "Gizmo - Place on Face"); wxGetApp().obj_manipul()->set_dirty(); } - canvas.set_as_dirty(); + m_parent->set_as_dirty(); processed = true; } } else if (evt.RightDown() && (selected_object_idx != -1) && (m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::RightDown)) // event was taken care of by the SlaSupports gizmo processed = true; - else if (evt.Dragging() && (canvas.get_move_volume_id() != -1) && (m_current == SlaSupports)) + else if (evt.Dragging() && (m_parent->get_move_volume_id() != -1) && (m_current == SlaSupports)) // don't allow dragging objects with the Sla gizmo on processed = true; else if (evt.Dragging() && (m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) { // the gizmo got the event and took some action, no need to do anything more here - canvas.set_as_dirty(); + m_parent->set_as_dirty(); processed = true; } else if (evt.Dragging() && is_dragging()) { - if (!canvas.get_wxglcanvas()->HasCapture()) - canvas.get_wxglcanvas()->CaptureMouse(); + if (!m_parent->get_wxglcanvas()->HasCapture()) + m_parent->get_wxglcanvas()->CaptureMouse(); - canvas.set_mouse_as_dragging(); - update(canvas.mouse_ray(pos), selection, &pos); + m_parent->set_mouse_as_dragging(); + update(m_parent->mouse_ray(pos), &pos); switch (m_current) { @@ -605,7 +626,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas) break; } - canvas.set_as_dirty(); + m_parent->set_as_dirty(); processed = true; } else if (evt.LeftUp() && is_dragging()) @@ -614,18 +635,18 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas) { case Move: { - canvas.disable_regenerate_volumes(); - canvas.do_move("Gizmo-Move Object"); + m_parent->disable_regenerate_volumes(); + m_parent->do_move("Gizmo-Move Object"); break; } case Scale: { - canvas.do_scale("Gizmo-Scale Object"); + m_parent->do_scale("Gizmo-Scale Object"); break; } case Rotate: { - canvas.do_rotate("Gizmo-Rotate Object"); + m_parent->do_rotate("Gizmo-Rotate Object"); break; } default: @@ -633,25 +654,25 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas) } stop_dragging(); - update_data(canvas); + update_data(); wxGetApp().obj_manipul()->set_dirty(); // Let the platter know that the dragging finished, so a delayed refresh // of the scene with the background processing data should be performed. - canvas.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); + m_parent->post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); // updates camera target constraints - canvas.refresh_camera_scene_box(); + m_parent->refresh_camera_scene_box(); processed = true; } - else if (evt.LeftUp() && (m_current == SlaSupports) && !canvas.is_mouse_dragging()) + else if (evt.LeftUp() && (m_current == SlaSupports) && !m_parent->is_mouse_dragging()) { // in case SLA gizmo is selected, we just pass the LeftUp event and stop processing - neither // object moving or selecting is suppressed in that case gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown()); processed = true; } - else if (evt.LeftUp() && (m_current == Flatten) && ((canvas.get_first_hover_volume_idx() != -1) || grabber_contains_mouse())) + else if (evt.LeftUp() && (m_current == Flatten) && ((m_parent->get_first_hover_volume_idx() != -1) || grabber_contains_mouse())) { // to avoid to loose the selection when user clicks an object while the Flatten gizmo is active processed = true; @@ -663,24 +684,24 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas) if (evt.LeftDown() || evt.LeftDClick()) { m_mouse_capture.left = true; - m_mouse_capture.parent = &canvas; + m_mouse_capture.parent = m_parent; processed = true; if (!selection.is_empty()) { - update_on_off_state(canvas, mouse_pos, selection); - update_data(canvas); - canvas.set_as_dirty(); + update_on_off_state(mouse_pos); + update_data(); + m_parent->set_as_dirty(); } } else if (evt.MiddleDown()) { m_mouse_capture.middle = true; - m_mouse_capture.parent = &canvas; + m_mouse_capture.parent = m_parent; } else if (evt.RightDown()) { m_mouse_capture.right = true; - m_mouse_capture.parent = &canvas; + m_mouse_capture.parent = m_parent; } else if (evt.LeftUp()) processed = true; @@ -689,8 +710,11 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas) return processed; } -bool GLGizmosManager::on_char(wxKeyEvent& evt, GLCanvas3D& canvas) +bool GLGizmosManager::on_char(wxKeyEvent& evt) { + if (m_parent == nullptr) + return false; + // see include/wx/defs.h enum wxKeyCode int keyCode = evt.GetKeyCode(); int ctrlMask = wxMOD_CONTROL; @@ -797,21 +821,24 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt, GLCanvas3D& canvas) if (!processed && !evt.HasModifiers()) { - if (handle_shortcut(keyCode, canvas.get_selection())) + if (handle_shortcut(keyCode)) { - update_data(canvas); + update_data(); processed = true; } } if (processed) - canvas.set_as_dirty(); + m_parent->set_as_dirty(); return processed; } -bool GLGizmosManager::on_key(wxKeyEvent& evt, GLCanvas3D& canvas) +bool GLGizmosManager::on_key(wxKeyEvent& evt) { + if (m_parent == nullptr) + return false; + const int keyCode = evt.GetKeyCode(); bool processed = false; @@ -836,23 +863,29 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt, GLCanvas3D& canvas) } // if (processed) -// canvas.set_cursor(GLCanvas3D::Standard); +// m_parent->set_cursor(GLCanvas3D::Standard); } else if (evt.GetEventType() == wxEVT_KEY_DOWN) { if ((m_current == SlaSupports) && ((keyCode == WXK_SHIFT) || (keyCode == WXK_ALT)) && reinterpret_cast(get_current())->is_in_editing_mode()) { -// canvas.set_cursor(GLCanvas3D::Cross); +// m_parent->set_cursor(GLCanvas3D::Cross); processed = true; } } if (processed) - canvas.set_as_dirty(); + m_parent->set_as_dirty(); return processed; } +void GLGizmosManager::update_after_undo_redo() +{ + update_data(); + m_serializing = false; +} + void GLGizmosManager::reset() { for (GizmosMap::value_type& gizmo : m_gizmos) @@ -864,14 +897,16 @@ void GLGizmosManager::reset() m_gizmos.clear(); } -void GLGizmosManager::do_render_overlay(const GLCanvas3D& canvas, const Selection& selection) const +void GLGizmosManager::do_render_overlay() const { - if (m_gizmos.empty()) + if ((m_parent == nullptr) || m_gizmos.empty()) return; - float cnv_w = (float)canvas.get_canvas_size().get_width(); - float cnv_h = (float)canvas.get_canvas_size().get_height(); - float zoom = (float)canvas.get_camera().get_zoom(); + const Selection& selection = m_parent->get_selection(); + + float cnv_w = (float)m_parent->get_canvas_size().get_width(); + float cnv_h = (float)m_parent->get_canvas_size().get_height(); + float zoom = (float)m_parent->get_camera().get_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; float height = get_total_overlay_height(); @@ -984,7 +1019,7 @@ void GLGizmosManager::do_render_overlay(const GLCanvas3D& canvas, const Selectio GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + scaled_icons_size, top_y - scaled_icons_size, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } }); if (it->second->get_state() == GLGizmoBase::On) { - float toolbar_top = (float)cnv_h - canvas.get_view_toolbar_height(); + float toolbar_top = (float)cnv_h - m_parent->get_view_toolbar_height(); it->second->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection); } top_y -= scaled_stride_y; @@ -1047,12 +1082,14 @@ bool GLGizmosManager::generate_icons_texture() const return res; } -void GLGizmosManager::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection) +void GLGizmosManager::update_on_off_state(const Vec2d& mouse_pos) { - if (!m_enabled) + if (!m_enabled || (m_parent == nullptr)) return; - float cnv_h = (float)canvas.get_canvas_size().get_height(); + const Selection& selection = m_parent->get_selection(); + + float cnv_h = (float)m_parent->get_canvas_size().get_height(); float height = get_total_overlay_height(); float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; @@ -1091,16 +1128,16 @@ void GLGizmosManager::update_on_off_state(const GLCanvas3D& canvas, const Vec2d& it->second->set_state(GLGizmoBase::On); } -std::string GLGizmosManager::update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos) +std::string GLGizmosManager::update_hover_state(const Vec2d& mouse_pos) { std::string name = ""; - if (!m_enabled) + if (!m_enabled || (m_parent == nullptr)) return name; - const Selection& selection = canvas.get_selection(); + const Selection& selection = m_parent->get_selection(); - float cnv_h = (float)canvas.get_canvas_size().get_height(); + float cnv_h = (float)m_parent->get_canvas_size().get_height(); float height = get_total_overlay_height(); float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; float scaled_border = m_overlay_border * m_overlay_scale; @@ -1126,12 +1163,12 @@ std::string GLGizmosManager::update_hover_state(const GLCanvas3D& canvas, const return name; } -bool GLGizmosManager::overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const +bool GLGizmosManager::overlay_contains_mouse(const Vec2d& mouse_pos) const { - if (!m_enabled) + if (!m_enabled || (m_parent == nullptr)) return false; - float cnv_h = (float)canvas.get_canvas_size().get_height(); + float cnv_h = (float)m_parent->get_canvas_size().get_height(); float height = get_total_overlay_height(); float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 87be7da41..db73345ae 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -4,14 +4,13 @@ #include "slic3r/GUI/GLTexture.hpp" #include "slic3r/GUI/GLToolbar.hpp" #include "slic3r/GUI/Gizmos/GLGizmos.hpp" +#include "libslic3r/ObjectID.hpp" #include namespace Slic3r { namespace GUI { -class Selection; -class GLGizmoBase; class GLCanvas3D; class ClippingPlane; @@ -43,7 +42,7 @@ public: float get_height() const { return m_top - m_bottom; } }; -class GLGizmosManager +class GLGizmosManager : public Slic3r::ObjectBase { public: static const float Default_Icons_Size; @@ -61,6 +60,7 @@ public: }; private: + GLCanvas3D* m_parent; bool m_enabled; typedef std::map GizmosMap; GizmosMap m_gizmos; @@ -89,6 +89,7 @@ private: MouseCapture m_mouse_capture; std::string m_tooltip; + bool m_serializing; public: GLGizmosManager(); @@ -96,29 +97,59 @@ public: bool init(GLCanvas3D& parent); + template + void load(Archive& ar) + { + if (!m_enabled) + return; + + m_serializing = true; + + ar(m_current); + + GLGizmoBase* curr = get_current(); + if (curr != nullptr) + { + curr->set_state(GLGizmoBase::On); + curr->load(ar); + } + } + + template + void save(Archive& ar) const + { + if (!m_enabled) + return; + + ar(m_current); + + GLGizmoBase* curr = get_current(); + if (curr != nullptr) + curr->save(ar); + } + bool is_enabled() const { return m_enabled; } void set_enabled(bool enable) { m_enabled = enable; } void set_overlay_icon_size(float size); void set_overlay_scale(float scale); - void refresh_on_off_state(const Selection& selection); + void refresh_on_off_state(); void reset_all_states(); void set_hover_id(int id); void enable_grabber(EType type, unsigned int id, bool enable); - void update(const Linef3& mouse_ray, const Selection& selection, const Point* mouse_pos = nullptr); - void update_data(GLCanvas3D& canvas); + void update(const Linef3& mouse_ray, const Point* mouse_pos = nullptr); + void update_data(); - Rect get_reset_rect_viewport(const GLCanvas3D& canvas) const; EType get_current_type() const { return m_current; } bool is_running() const; - bool handle_shortcut(int key, const Selection& selection); + bool handle_shortcut(int key); bool is_dragging() const; - void start_dragging(const Selection& selection); + void start_dragging(); void stop_dragging(); Vec3d get_displacement() const; @@ -135,26 +166,28 @@ public: void set_flattening_data(const ModelObject* model_object); - void set_sla_support_data(ModelObject* model_object, const Selection& selection); + void set_sla_support_data(ModelObject* model_object); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false, bool alt_down = false, bool control_down = false); ClippingPlane get_sla_clipping_plane() const; - void render_current_gizmo(const Selection& selection) const; - void render_current_gizmo_for_picking_pass(const Selection& selection) const; + void render_current_gizmo() const; + void render_current_gizmo_for_picking_pass() const; - void render_overlay(const GLCanvas3D& canvas, const Selection& selection) const; + void render_overlay() const; const std::string& get_tooltip() const { return m_tooltip; } - bool on_mouse(wxMouseEvent& evt, GLCanvas3D& canvas); - bool on_mouse_wheel(wxMouseEvent& evt, GLCanvas3D& canvas); - bool on_char(wxKeyEvent& evt, GLCanvas3D& canvas); - bool on_key(wxKeyEvent& evt, GLCanvas3D& canvas); + bool on_mouse(wxMouseEvent& evt); + bool on_mouse_wheel(wxMouseEvent& evt); + bool on_char(wxKeyEvent& evt); + bool on_key(wxKeyEvent& evt); + + void update_after_undo_redo(); private: void reset(); - void do_render_overlay(const GLCanvas3D& canvas, const Selection& selection) const; + void do_render_overlay() const; float get_total_overlay_height() const; float get_total_overlay_width() const; @@ -163,13 +196,18 @@ private: bool generate_icons_texture() const; - void update_on_off_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos, const Selection& selection); - std::string update_hover_state(const GLCanvas3D& canvas, const Vec2d& mouse_pos); - bool overlay_contains_mouse(const GLCanvas3D& canvas, const Vec2d& mouse_pos) const; + void update_on_off_state(const Vec2d& mouse_pos); + std::string update_hover_state(const Vec2d& mouse_pos); + bool overlay_contains_mouse(const Vec2d& mouse_pos) const; bool grabber_contains_mouse() const; }; } // namespace GUI } // namespace Slic3r +namespace cereal +{ + template struct specialize {}; +} + #endif // slic3r_GUI_GLGizmosManager_hpp_ diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f5f245471..c5a4ccead 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1633,8 +1633,8 @@ struct Plater::priv if (this->m_prevent_snapshots > 0) return; assert(this->m_prevent_snapshots >= 0); - this->undo_redo_stack.take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection()); - } + this->undo_redo_stack.take_snapshot(snapshot_name, model, view3D->get_canvas3d()->get_selection(), view3D->get_canvas3d()->get_gizmos_manager()); + } void take_snapshot(const wxString& snapshot_name) { this->take_snapshot(std::string(snapshot_name.ToUTF8().data())); } int get_active_snapshot_index(); void undo(); @@ -3611,34 +3611,35 @@ int Plater::priv::get_active_snapshot_index() void Plater::priv::undo() { - if (this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection())) - this->update_after_undo_redo(); + if (this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection(), this->view3D->get_canvas3d()->get_gizmos_manager())) + this->update_after_undo_redo(); } void Plater::priv::redo() { - if (this->undo_redo_stack.redo(model)) - this->update_after_undo_redo(); + if (this->undo_redo_stack.redo(model, this->view3D->get_canvas3d()->get_gizmos_manager())) + this->update_after_undo_redo(); } void Plater::priv::undo_to(size_t time_to_load) { - if (this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection(), time_to_load)) - this->update_after_undo_redo(); + if (this->undo_redo_stack.undo(model, this->view3D->get_canvas3d()->get_selection(), this->view3D->get_canvas3d()->get_gizmos_manager(), time_to_load)) + this->update_after_undo_redo(); } void Plater::priv::redo_to(size_t time_to_load) { - if (this->undo_redo_stack.redo(model, time_to_load)) - this->update_after_undo_redo(); + if (this->undo_redo_stack.redo(model, this->view3D->get_canvas3d()->get_gizmos_manager(), time_to_load)) + this->update_after_undo_redo(); } void Plater::priv::update_after_undo_redo() { - this->view3D->get_canvas3d()->get_selection().clear(); + this->view3D->get_canvas3d()->get_selection().clear(); this->update(false); // update volumes from the deserializd model //YS_FIXME update obj_list from the deserialized model (maybe store ObjectIDs into the tree?) (no selections at this point of time) this->view3D->get_canvas3d()->get_selection().set_deserialized(GUI::Selection::EMode(this->undo_redo_stack.selection_deserialized().mode), this->undo_redo_stack.selection_deserialized().volumes_and_instances); + this->view3D->get_canvas3d()->get_gizmos_manager().update_after_undo_redo(); wxGetApp().obj_list()->update_after_undo_redo(); @@ -3884,7 +3885,7 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_uppe return; } - this->take_snapshot(_(L("Cut"))); + this->take_snapshot(_(L("Gizmo - Cut"))); wxBusyCursor wait; const auto new_objects = object->cut(instance_idx, z, keep_upper, keep_lower, rotate_lower); diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index 058062502..69d60603e 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -17,8 +17,6 @@ #define CEREAL_FUTURE_EXPERIMENTAL #include -#include - #include #ifndef NDEBUG @@ -358,18 +356,14 @@ public: // Stack needs to be initialized. An empty stack is not valid, there must be a "New Project" status stored at the beginning. StackImpl() : m_active_snapshot_time(0), m_current_time(0) {} - // The Undo / Redo stack is being initialized with an empty model and an empty selection. - // The first snapshot cannot be removed. - void initialize(const Slic3r::Model &model, const Slic3r::GUI::Selection &selection); - - // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time. - void take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection); - void load_snapshot(size_t timestamp, Slic3r::Model &model); + // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time. + void take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos); + void load_snapshot(size_t timestamp, Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos); bool has_undo_snapshot() const; bool has_redo_snapshot() const; - bool undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection, size_t jump_to_time); - bool redo(Slic3r::Model &model, size_t jump_to_time); + bool undo(Slic3r::Model& model, const Slic3r::GUI::Selection& selection, Slic3r::GUI::GLGizmosManager& gizmos, size_t jump_to_time); + bool redo(Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos, size_t jump_to_time); // Snapshot history (names with timestamps). const std::vector& snapshots() const { return m_snapshots; } @@ -551,6 +545,7 @@ namespace cereal #include #include #include +#include namespace Slic3r { namespace UndoRedo { @@ -632,7 +627,7 @@ template void StackImpl::load_mutable_object(const Sl } // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time. -void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) +void StackImpl::take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos) { // Release old snapshot data. assert(m_active_snapshot_time <= m_current_time); @@ -650,7 +645,8 @@ void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Mo for (unsigned int volume_idx : selection.get_volume_idxs()) m_selection.volumes_and_instances.emplace_back(selection.get_volume(volume_idx)->geometry_id); this->save_mutable_object(m_selection); - // Save the snapshot info. + this->save_mutable_object(gizmos); + // Save the snapshot info. m_snapshots.emplace_back(snapshot_name, m_current_time ++, model.id().id); m_active_snapshot_time = m_current_time; // Save snapshot info of the last "current" aka "top most" state, that is only being serialized @@ -665,7 +661,7 @@ void StackImpl::take_snapshot(const std::string &snapshot_name, const Slic3r::Mo #endif /* SLIC3R_UNDOREDO_DEBUG */ } -void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model) +void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos) { // Find the snapshot by time. It must exist. const auto it_snapshot = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(timestamp)); @@ -679,7 +675,9 @@ void StackImpl::load_snapshot(size_t timestamp, Slic3r::Model &model) model.update_links_bottom_up_recursive(); m_selection.volumes_and_instances.clear(); this->load_mutable_object(m_selection.id(), m_selection); - // Sort the volumes so that we may use binary search. + gizmos.reset_all_states(); + this->load_mutable_object(gizmos.id(), gizmos); + // Sort the volumes so that we may use binary search. std::sort(m_selection.volumes_and_instances.begin(), m_selection.volumes_and_instances.end()); this->m_active_snapshot_time = timestamp; assert(this->valid()); @@ -699,8 +697,8 @@ bool StackImpl::has_redo_snapshot() const return ++ it != m_snapshots.end(); } -bool StackImpl::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection, size_t time_to_load) -{ +bool StackImpl::undo(Slic3r::Model& model, const Slic3r::GUI::Selection& selection, Slic3r::GUI::GLGizmosManager& gizmos, size_t time_to_load) +{ assert(this->valid()); if (time_to_load == SIZE_MAX) { auto it_current = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); @@ -712,8 +710,8 @@ bool StackImpl::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selecti assert(std::binary_search(m_snapshots.begin(), m_snapshots.end(), Snapshot(time_to_load))); if (m_active_snapshot_time == m_snapshots.back().timestamp && ! m_snapshots.back().is_topmost_captured()) { // The current state is temporary. The current state needs to be captured to be redoable. - this->take_snapshot(topmost_snapsnot_name, model, selection); - // The line above entered another topmost_snapshot_name. + this->take_snapshot(topmost_snapsnot_name, model, selection, gizmos); + // The line above entered another topmost_snapshot_name. assert(m_snapshots.back().is_topmost()); assert(! m_snapshots.back().is_topmost_captured()); // Pop it back, it is not needed as there is now a captured topmost state. @@ -723,7 +721,7 @@ bool StackImpl::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selecti assert(m_snapshots.back().is_topmost()); assert(m_snapshots.back().is_topmost_captured()); } - this->load_snapshot(time_to_load, model); + this->load_snapshot(time_to_load, model, gizmos); #ifdef SLIC3R_UNDOREDO_DEBUG std::cout << "After undo" << std::endl; this->print(); @@ -731,8 +729,8 @@ bool StackImpl::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selecti return true; } -bool StackImpl::redo(Slic3r::Model &model, size_t time_to_load) -{ +bool StackImpl::redo(Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos, size_t time_to_load) +{ assert(this->valid()); if (time_to_load == SIZE_MAX) { auto it_current = std::lower_bound(m_snapshots.begin(), m_snapshots.end(), Snapshot(m_active_snapshot_time)); @@ -742,7 +740,7 @@ bool StackImpl::redo(Slic3r::Model &model, size_t time_to_load) } assert(time_to_load > m_active_snapshot_time); assert(std::binary_search(m_snapshots.begin(), m_snapshots.end(), Snapshot(time_to_load))); - this->load_snapshot(time_to_load, model); + this->load_snapshot(time_to_load, model, gizmos); #ifdef SLIC3R_UNDOREDO_DEBUG std::cout << "After redo" << std::endl; this->print(); @@ -767,11 +765,11 @@ void StackImpl::collect_garbage() // Wrappers of the private implementation. Stack::Stack() : pimpl(new StackImpl()) {} Stack::~Stack() {} -void Stack::take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection) { pimpl->take_snapshot(snapshot_name, model, selection); } +void Stack::take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos) { pimpl->take_snapshot(snapshot_name, model, selection, gizmos); } bool Stack::has_undo_snapshot() const { return pimpl->has_undo_snapshot(); } bool Stack::has_redo_snapshot() const { return pimpl->has_redo_snapshot(); } -bool Stack::undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection, size_t time_to_load) { return pimpl->undo(model, selection, time_to_load); } -bool Stack::redo(Slic3r::Model &model, size_t time_to_load) { return pimpl->redo(model, time_to_load); } +bool Stack::undo(Slic3r::Model& model, const Slic3r::GUI::Selection& selection, Slic3r::GUI::GLGizmosManager& gizmos, size_t time_to_load) { return pimpl->undo(model, selection, gizmos, time_to_load); } +bool Stack::redo(Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos, size_t time_to_load) { return pimpl->redo(model, gizmos, time_to_load); } const Selection& Stack::selection_deserialized() const { return pimpl->selection_deserialized(); } const std::vector& Stack::snapshots() const { return pimpl->snapshots(); } diff --git a/src/slic3r/Utils/UndoRedo.hpp b/src/slic3r/Utils/UndoRedo.hpp index f8bfda08c..236970b68 100644 --- a/src/slic3r/Utils/UndoRedo.hpp +++ b/src/slic3r/Utils/UndoRedo.hpp @@ -15,6 +15,7 @@ class Model; namespace GUI { class Selection; + class GLGizmosManager; } // namespace GUI namespace UndoRedo { @@ -56,7 +57,7 @@ public: ~Stack(); // Store the current application state onto the Undo / Redo stack, remove all snapshots after m_active_snapshot_time. - void take_snapshot(const std::string &snapshot_name, const Slic3r::Model &model, const Slic3r::GUI::Selection &selection); + void take_snapshot(const std::string& snapshot_name, const Slic3r::Model& model, const Slic3r::GUI::Selection& selection, const Slic3r::GUI::GLGizmosManager& gizmos); // To be queried to enable / disable the Undo / Redo buttons at the UI. bool has_undo_snapshot() const; @@ -64,10 +65,10 @@ public: // Roll back the time. If time_to_load is SIZE_MAX, the previous snapshot is activated. // Undoing an action may need to take a snapshot of the current application state, so that redo to the current state is possible. - bool undo(Slic3r::Model &model, const Slic3r::GUI::Selection &selection, size_t time_to_load = SIZE_MAX); + bool undo(Slic3r::Model& model, const Slic3r::GUI::Selection& selection, Slic3r::GUI::GLGizmosManager& gizmos, size_t time_to_load = SIZE_MAX); // Jump forward in time. If time_to_load is SIZE_MAX, the next snapshot is activated. - bool redo(Slic3r::Model &model, size_t time_to_load = SIZE_MAX); + bool redo(Slic3r::Model& model, Slic3r::GUI::GLGizmosManager& gizmos, size_t time_to_load = SIZE_MAX); // Snapshot history (names with timestamps). // Each snapshot indicates start of an interval in which this operation is performed. From 0a530ab7bc3913d3b1d3333c110d0dafd18393de Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 17 Jul 2019 10:03:00 +0200 Subject: [PATCH 70/72] Added undo/redo snapshot for layers height editing --- src/slic3r/GUI/GLCanvas3D.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 9e05f60ad..9bb9c2158 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -604,6 +604,7 @@ void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas) { if (last_object_id >= 0) { if (m_layer_height_profile_modified) { + wxGetApp().plater()->take_snapshot(_(L("Layers heights"))); const_cast(m_model_object)->layer_height_profile = m_layer_height_profile; canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } From da1fa0b6e339163836cc7a8165c0a05b58c5f0d3 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 17 Jul 2019 12:06:23 +0200 Subject: [PATCH 71/72] Refactoring of GLGizmosXX classes to cleanup their interface --- src/slic3r/GUI/Gizmos/GLGizmoBase.cpp | 8 ++--- src/slic3r/GUI/Gizmos/GLGizmoBase.hpp | 30 ++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoCut.cpp | 24 ++++++++------ src/slic3r/GUI/Gizmos/GLGizmoCut.hpp | 12 +++---- src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp | 17 ++++++---- src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp | 9 +++--- src/slic3r/GUI/Gizmos/GLGizmoMove.cpp | 22 +++++++------ src/slic3r/GUI/Gizmos/GLGizmoMove.hpp | 12 ++++--- src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp | 34 +++++++++++--------- src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp | 26 +++++++-------- src/slic3r/GUI/Gizmos/GLGizmoScale.cpp | 30 ++++++++++------- src/slic3r/GUI/Gizmos/GLGizmoScale.hpp | 14 ++++---- src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp | 23 +++++++------ src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp | 12 +++---- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 31 +++++++----------- src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 2 +- 16 files changed, 165 insertions(+), 141 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index ba0ea4825..4b975e87e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -180,7 +180,7 @@ void GLGizmoBase::disable_grabber(unsigned int id) on_disable_grabber(id); } -void GLGizmoBase::start_dragging(const Selection& selection) +void GLGizmoBase::start_dragging() { m_dragging = true; @@ -189,7 +189,7 @@ void GLGizmoBase::start_dragging(const Selection& selection) m_grabbers[i].dragging = (m_hover_id == i); } - on_start_dragging(selection); + on_start_dragging(); } void GLGizmoBase::stop_dragging() @@ -204,10 +204,10 @@ void GLGizmoBase::stop_dragging() on_stop_dragging(); } -void GLGizmoBase::update(const UpdateData& data, const Selection& selection) +void GLGizmoBase::update(const UpdateData& data) { if (m_hover_id != -1) - on_update(data, selection); + on_update(data); } std::array GLGizmoBase::picking_color_component(unsigned int id) const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index 88927aee2..b84442b94 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -76,10 +76,10 @@ public: struct UpdateData { - const Linef3 mouse_ray; - const Point* mouse_pos; + const Linef3& mouse_ray; + const Point& mouse_pos; - UpdateData(const Linef3& mouse_ray, const Point* mouse_pos = nullptr) + UpdateData(const Linef3& mouse_ray, const Point& mouse_pos) : mouse_ray(mouse_ray), mouse_pos(mouse_pos) {} }; @@ -122,7 +122,7 @@ public: const std::string& get_icon_filename() const { return m_icon_filename; } - bool is_activable(const Selection& selection) const { return on_is_activable(selection); } + bool is_activable() const { return on_is_activable(); } bool is_selectable() const { return on_is_selectable(); } unsigned int get_sprite_id() const { return m_sprite_id; } @@ -135,16 +135,16 @@ public: void enable_grabber(unsigned int id); void disable_grabber(unsigned int id); - void start_dragging(const Selection& selection); + void start_dragging(); void stop_dragging(); bool is_dragging() const { return m_dragging; } - void update(const UpdateData& data, const Selection& selection); + void update(const UpdateData& data); - void render(const Selection& selection) const { on_render(selection); } - void render_for_picking(const Selection& selection) const { on_render_for_picking(selection); } - void render_input_window(float x, float y, float bottom_limit, const Selection& selection) { on_render_input_window(x, y, bottom_limit, selection); } + void render() const { on_render(); } + void render_for_picking() const { on_render_for_picking(); } + void render_input_window(float x, float y, float bottom_limit) { on_render_input_window(x, y, bottom_limit); } protected: virtual bool on_init() = 0; @@ -153,16 +153,16 @@ protected: virtual std::string on_get_name() const = 0; virtual void on_set_state() {} virtual void on_set_hover_id() {} - virtual bool on_is_activable(const Selection& selection) const { return true; } + virtual bool on_is_activable() const { return true; } virtual bool on_is_selectable() const { return true; } virtual void on_enable_grabber(unsigned int id) {} virtual void on_disable_grabber(unsigned int id) {} - virtual void on_start_dragging(const Selection& selection) {} + virtual void on_start_dragging() {} virtual void on_stop_dragging() {} - virtual void on_update(const UpdateData& data, const Selection& selection) = 0; - virtual void on_render(const Selection& selection) const = 0; - virtual void on_render_for_picking(const Selection& selection) const = 0; - virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) {} + virtual void on_update(const UpdateData& data) {} + virtual void on_render() const = 0; + virtual void on_render_for_picking() const = 0; + virtual void on_render_input_window(float x, float y, float bottom_limit) {} // Returns the picking color for the given id, based on the BASE_ID constant // No check is made for clashing with other picking color (i.e. GLVolumes) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp index 52174d2d6..39399fc0d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.cpp @@ -48,15 +48,18 @@ void GLGizmoCut::on_set_state() } } -bool GLGizmoCut::on_is_activable(const Selection& selection) const +bool GLGizmoCut::on_is_activable() const { + const Selection& selection = m_parent.get_selection(); return selection.is_single_full_instance() && !selection.is_wipe_tower(); } -void GLGizmoCut::on_start_dragging(const Selection& selection) +void GLGizmoCut::on_start_dragging() { - if (m_hover_id == -1) { return; } + if (m_hover_id == -1) + return; + const Selection& selection = m_parent.get_selection(); const BoundingBoxf3& box = selection.get_bounding_box(); m_start_z = m_cut_z; update_max_z(selection); @@ -65,19 +68,21 @@ void GLGizmoCut::on_start_dragging(const Selection& selection) m_drag_center(2) = m_cut_z; } -void GLGizmoCut::on_update(const UpdateData& data, const Selection& selection) +void GLGizmoCut::on_update(const UpdateData& data) { if (m_hover_id != -1) { set_cut_z(m_start_z + calc_projection(data.mouse_ray)); } } -void GLGizmoCut::on_render(const Selection& selection) const +void GLGizmoCut::on_render() const { if (m_grabbers[0].dragging) { set_tooltip("Z: " + format(m_cut_z, 2)); } + const Selection& selection = m_parent.get_selection(); + update_max_z(selection); const BoundingBoxf3& box = selection.get_bounding_box(); @@ -123,14 +128,13 @@ void GLGizmoCut::on_render(const Selection& selection) const m_grabbers[0].render(m_hover_id == 0, (float)((box.size()(0) + box.size()(1) + box.size()(2)) / 3.0)); } -void GLGizmoCut::on_render_for_picking(const Selection& selection) const +void GLGizmoCut::on_render_for_picking() const { glsafe(::glDisable(GL_DEPTH_TEST)); - - render_grabbers_for_picking(selection.get_bounding_box()); + render_grabbers_for_picking(m_parent.get_selection().get_bounding_box()); } -void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) +void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit) { const float approx_height = m_imgui->scaled(11.0f); y = std::min(y, bottom_limit - approx_height); @@ -153,7 +157,7 @@ void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit, co m_imgui->end(); if (cut_clicked && (m_keep_upper || m_keep_lower)) { - perform_cut(selection); + perform_cut(m_parent.get_selection()); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp index 628bcf508..5bfeda526 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoCut.hpp @@ -31,12 +31,12 @@ protected: virtual void on_save(cereal::BinaryOutputArchive& ar) const { ar(m_cut_z, m_keep_upper, m_keep_lower, m_rotate_lower); } virtual std::string on_get_name() const; virtual void on_set_state(); - virtual bool on_is_activable(const Selection& selection) const; - virtual void on_start_dragging(const Selection& selection); - virtual void on_update(const UpdateData& data, const Selection& selection); - virtual void on_render(const Selection& selection) const; - virtual void on_render_for_picking(const Selection& selection) const; - virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection); + virtual bool on_is_activable() const; + virtual void on_start_dragging(); + virtual void on_update(const UpdateData& data); + virtual void on_render() const; + virtual void on_render_for_picking() const; + virtual void on_render_input_window(float x, float y, float bottom_limit); private: void update_max_z(const Selection& selection) const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index 6bb81a703..78f5da58b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -1,5 +1,6 @@ // Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. #include "GLGizmoFlatten.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" #include @@ -27,23 +28,25 @@ std::string GLGizmoFlatten::on_get_name() const return (_(L("Place on face")) + " [F]").ToUTF8().data(); } -bool GLGizmoFlatten::on_is_activable(const Selection& selection) const +bool GLGizmoFlatten::on_is_activable() const { - return selection.is_single_full_instance(); + return m_parent.get_selection().is_single_full_instance(); } -void GLGizmoFlatten::on_start_dragging(const Selection& selection) +void GLGizmoFlatten::on_start_dragging() { if (m_hover_id != -1) { assert(m_planes_valid); m_normal = m_planes[m_hover_id].normal; - m_starting_center = selection.get_bounding_box().center(); + m_starting_center = m_parent.get_selection().get_bounding_box().center(); } } -void GLGizmoFlatten::on_render(const Selection& selection) const +void GLGizmoFlatten::on_render() const { + const Selection& selection = m_parent.get_selection(); + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); glsafe(::glEnable(GL_DEPTH_TEST)); @@ -78,8 +81,10 @@ void GLGizmoFlatten::on_render(const Selection& selection) const glsafe(::glDisable(GL_BLEND)); } -void GLGizmoFlatten::on_render_for_picking(const Selection& selection) const +void GLGizmoFlatten::on_render_for_picking() const { + const Selection& selection = m_parent.get_selection(); + glsafe(::glDisable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_BLEND)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp index 926dc3457..c8bd056bc 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp @@ -45,11 +45,10 @@ public: protected: virtual bool on_init(); virtual std::string on_get_name() const; - virtual bool on_is_activable(const Selection& selection) const; - virtual void on_start_dragging(const Selection& selection); - virtual void on_update(const UpdateData& data, const Selection& selection) {} - virtual void on_render(const Selection& selection) const; - virtual void on_render_for_picking(const Selection& selection) const; + virtual bool on_is_activable() const; + virtual void on_start_dragging(); + virtual void on_render() const; + virtual void on_render_for_picking() const; virtual void on_set_state() { if (m_state == On && is_plane_update_necessary()) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index b97f578b3..11bdcd4f8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -1,5 +1,6 @@ // Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. #include "GLGizmoMove.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" #include @@ -47,12 +48,12 @@ std::string GLGizmoMove3D::on_get_name() const return (_(L("Move")) + " [M]").ToUTF8().data(); } -void GLGizmoMove3D::on_start_dragging(const Selection& selection) +void GLGizmoMove3D::on_start_dragging() { if (m_hover_id != -1) { m_displacement = Vec3d::Zero(); - const BoundingBoxf3& box = selection.get_bounding_box(); + const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); m_starting_drag_position = m_grabbers[m_hover_id].center; m_starting_box_center = box.center(); m_starting_box_bottom_center = box.center(); @@ -65,7 +66,7 @@ void GLGizmoMove3D::on_stop_dragging() m_displacement = Vec3d::Zero(); } -void GLGizmoMove3D::on_update(const UpdateData& data, const Selection& selection) +void GLGizmoMove3D::on_update(const UpdateData& data) { if (m_hover_id == 0) m_displacement(0) = calc_projection(data); @@ -75,8 +76,10 @@ void GLGizmoMove3D::on_update(const UpdateData& data, const Selection& selection m_displacement(2) = calc_projection(data); } -void GLGizmoMove3D::on_render(const Selection& selection) const +void GLGizmoMove3D::on_render() const { + const Selection& selection = m_parent.get_selection(); + bool show_position = selection.is_single_full_instance(); const Vec3d& position = selection.get_bounding_box().center(); @@ -152,20 +155,21 @@ void GLGizmoMove3D::on_render(const Selection& selection) const } } -void GLGizmoMove3D::on_render_for_picking(const Selection& selection) const +void GLGizmoMove3D::on_render_for_picking() const { glsafe(::glDisable(GL_DEPTH_TEST)); - const BoundingBoxf3& box = selection.get_bounding_box(); + const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); render_grabbers_for_picking(box); render_grabber_extension(X, box, true); render_grabber_extension(Y, box, true); render_grabber_extension(Z, box, true); } -void GLGizmoMove3D::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) -{ #if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI +void GLGizmoMove3D::on_render_input_window(float x, float y, float bottom_limit) +{ + const Selection& selection = m_parent.get_selection(); bool show_position = selection.is_single_full_instance(); const Vec3d& position = selection.get_bounding_box().center(); @@ -178,8 +182,8 @@ void GLGizmoMove3D::on_render_input_window(float x, float y, float bottom_limit, m_imgui->input_vec3("", displacement, 100.0f, "%.2f"); m_imgui->end(); -#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI } +#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI double GLGizmoMove3D::calc_projection(const UpdateData& data) const { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp index 771496780..21b1d397b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp @@ -33,12 +33,14 @@ public: protected: virtual bool on_init(); virtual std::string on_get_name() const; - virtual void on_start_dragging(const Selection& selection); + virtual void on_start_dragging(); virtual void on_stop_dragging(); - virtual void on_update(const UpdateData& data, const Selection& selection); - virtual void on_render(const Selection& selection) const; - virtual void on_render_for_picking(const Selection& selection) const; - virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection); + virtual void on_update(const UpdateData& data); + virtual void on_render() const; + virtual void on_render_for_picking() const; +#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI + virtual void on_render_input_window(float x, float y, float bottom_limit); +#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI private: double calc_projection(const UpdateData& data) const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index f84c3886d..f481bb5d7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -1,5 +1,6 @@ // Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. #include "GLGizmoRotate.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" #include @@ -72,9 +73,9 @@ bool GLGizmoRotate::on_init() return true; } -void GLGizmoRotate::on_start_dragging(const Selection& selection) +void GLGizmoRotate::on_start_dragging() { - const BoundingBoxf3& box = selection.get_bounding_box(); + const BoundingBoxf3& box = m_parent.get_selection().get_bounding_box(); m_center = box.center(); m_radius = Offset + box.radius(); m_snap_coarse_in_radius = m_radius / 3.0f; @@ -83,9 +84,9 @@ void GLGizmoRotate::on_start_dragging(const Selection& selection) m_snap_fine_out_radius = m_snap_fine_in_radius + m_radius * ScaleLongTooth; } -void GLGizmoRotate::on_update(const UpdateData& data, const Selection& selection) +void GLGizmoRotate::on_update(const UpdateData& data) { - Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(data.mouse_ray, selection)); + Vec2d mouse_pos = to_2d(mouse_position_in_local_plane(data.mouse_ray, m_parent.get_selection())); Vec2d orig_dir = Vec2d::UnitX(); Vec2d new_dir = mouse_pos.normalized(); @@ -118,11 +119,12 @@ void GLGizmoRotate::on_update(const UpdateData& data, const Selection& selection m_angle = theta; } -void GLGizmoRotate::on_render(const Selection& selection) const +void GLGizmoRotate::on_render() const { if (!m_grabbers[0].enabled) return; + const Selection& selection = m_parent.get_selection(); const BoundingBoxf3& box = selection.get_bounding_box(); std::string axis; @@ -175,8 +177,10 @@ void GLGizmoRotate::on_render(const Selection& selection) const glsafe(::glPopMatrix()); } -void GLGizmoRotate::on_render_for_picking(const Selection& selection) const +void GLGizmoRotate::on_render_for_picking() const { + const Selection& selection = m_parent.get_selection(); + glsafe(::glDisable(GL_DEPTH_TEST)); glsafe(::glPushMatrix()); @@ -445,10 +449,10 @@ std::string GLGizmoRotate3D::on_get_name() const return (_(L("Rotate")) + " [R]").ToUTF8().data(); } -void GLGizmoRotate3D::on_start_dragging(const Selection& selection) +void GLGizmoRotate3D::on_start_dragging() { if ((0 <= m_hover_id) && (m_hover_id < 3)) - m_gizmos[m_hover_id].start_dragging(selection); + m_gizmos[m_hover_id].start_dragging(); } void GLGizmoRotate3D::on_stop_dragging() @@ -457,23 +461,23 @@ void GLGizmoRotate3D::on_stop_dragging() m_gizmos[m_hover_id].stop_dragging(); } -void GLGizmoRotate3D::on_render(const Selection& selection) const +void GLGizmoRotate3D::on_render() const { glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); if ((m_hover_id == -1) || (m_hover_id == 0)) - m_gizmos[X].render(selection); + m_gizmos[X].render(); if ((m_hover_id == -1) || (m_hover_id == 1)) - m_gizmos[Y].render(selection); + m_gizmos[Y].render(); if ((m_hover_id == -1) || (m_hover_id == 2)) - m_gizmos[Z].render(selection); + m_gizmos[Z].render(); } -void GLGizmoRotate3D::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) -{ #if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI +void GLGizmoRotate3D::on_render_input_window(float x, float y, float bottom_limit) +{ Vec3d rotation(Geometry::rad2deg(m_gizmos[0].get_angle()), Geometry::rad2deg(m_gizmos[1].get_angle()), Geometry::rad2deg(m_gizmos[2].get_angle())); wxString label = _(L("Rotation (deg)")); @@ -482,8 +486,8 @@ void GLGizmoRotate3D::on_render_input_window(float x, float y, float bottom_limi m_imgui->begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); m_imgui->input_vec3("", rotation, 100.0f, "%.2f"); m_imgui->end(); -#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI } +#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index d2e564966..7846edb22 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -52,10 +52,10 @@ public: protected: virtual bool on_init(); virtual std::string on_get_name() const { return ""; } - virtual void on_start_dragging(const Selection& selection); - virtual void on_update(const UpdateData& data, const Selection& selection); - virtual void on_render(const Selection& selection) const; - virtual void on_render_for_picking(const Selection& selection) const; + virtual void on_start_dragging(); + virtual void on_update(const UpdateData& data); + virtual void on_render() const; + virtual void on_render_for_picking() const; private: void render_circle() const; @@ -98,7 +98,6 @@ protected: m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1); } } - virtual bool on_is_activable(const Selection& selection) const { return true; } virtual void on_enable_grabber(unsigned int id) { if ((0 <= id) && (id < 3)) @@ -109,25 +108,26 @@ protected: if ((0 <= id) && (id < 3)) m_gizmos[id].disable_grabber(0); } - virtual void on_start_dragging(const Selection& selection); + virtual void on_start_dragging(); virtual void on_stop_dragging(); - virtual void on_update(const UpdateData& data, const Selection& selection) + virtual void on_update(const UpdateData& data) { for (GLGizmoRotate& g : m_gizmos) { - g.update(data, selection); + g.update(data); } } - virtual void on_render(const Selection& selection) const; - virtual void on_render_for_picking(const Selection& selection) const + virtual void on_render() const; + virtual void on_render_for_picking() const { for (const GLGizmoRotate& g : m_gizmos) { - g.render_for_picking(selection); + g.render_for_picking(); } } - - virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection); +#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI + virtual void on_render_input_window(float x, float y, float bottom_limit); +#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index d30ceb092..7dc38b801 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -1,7 +1,6 @@ - - // Include GLGizmoBase.hpp before I18N.hpp as it includes some libigl code, which overrides our localization "L" macro. #include "GLGizmoScale.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" #include @@ -48,13 +47,18 @@ std::string GLGizmoScale3D::on_get_name() const return (_(L("Scale")) + " [S]").ToUTF8().data(); } -void GLGizmoScale3D::on_start_dragging(const Selection& selection) +bool GLGizmoScale3D::on_is_activable() const +{ + return !m_parent.get_selection().is_wipe_tower(); +} + +void GLGizmoScale3D::on_start_dragging() { if (m_hover_id != -1) { m_starting.drag_position = m_grabbers[m_hover_id].center; m_starting.ctrl_down = wxGetKeyState(WXK_CONTROL); - m_starting.box = (m_starting.ctrl_down && (m_hover_id < 6)) ? m_box : selection.get_bounding_box(); + m_starting.box = (m_starting.ctrl_down && (m_hover_id < 6)) ? m_box : m_parent.get_selection().get_bounding_box(); const Vec3d& center = m_starting.box.center(); m_starting.pivots[0] = m_transform * Vec3d(m_starting.box.max(0), center(1), center(2)); @@ -66,7 +70,7 @@ void GLGizmoScale3D::on_start_dragging(const Selection& selection) } } -void GLGizmoScale3D::on_update(const UpdateData& data, const Selection& selection) +void GLGizmoScale3D::on_update(const UpdateData& data) { if ((m_hover_id == 0) || (m_hover_id == 1)) do_scale_along_axis(X, data); @@ -78,8 +82,10 @@ void GLGizmoScale3D::on_update(const UpdateData& data, const Selection& selectio do_scale_uniform(data); } -void GLGizmoScale3D::on_render(const Selection& selection) const +void GLGizmoScale3D::on_render() const { + const Selection& selection = m_parent.get_selection(); + bool single_instance = selection.is_single_full_instance(); bool single_volume = selection.is_single_modifier() || selection.is_single_volume(); bool single_selection = single_instance || single_volume; @@ -272,16 +278,16 @@ void GLGizmoScale3D::on_render(const Selection& selection) const } } -void GLGizmoScale3D::on_render_for_picking(const Selection& selection) const +void GLGizmoScale3D::on_render_for_picking() const { glsafe(::glDisable(GL_DEPTH_TEST)); - - render_grabbers_for_picking(selection.get_bounding_box()); + render_grabbers_for_picking(m_parent.get_selection().get_bounding_box()); } -void GLGizmoScale3D::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) -{ #if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI +void GLGizmoScale3D::on_render_input_window(float x, float y, float bottom_limit) +{ + const Selection& selection = m_parent.get_selection(); bool single_instance = selection.is_single_full_instance(); wxString label = _(L("Scale (%)")); @@ -290,8 +296,8 @@ void GLGizmoScale3D::on_render_input_window(float x, float y, float bottom_limit m_imgui->begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse); m_imgui->input_vec3("", m_scale * 100.f, 100.0f, "%.2f"); m_imgui->end(); -#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI } +#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int id_2) const { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp index 16307165f..7b77fe559 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp @@ -45,12 +45,14 @@ public: protected: virtual bool on_init(); virtual std::string on_get_name() const; - virtual bool on_is_activable(const Selection& selection) const { return !selection.is_wipe_tower(); } - virtual void on_start_dragging(const Selection& selection); - virtual void on_update(const UpdateData& data, const Selection& selection); - virtual void on_render(const Selection& selection) const; - virtual void on_render_for_picking(const Selection& selection) const; - virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection); + virtual bool on_is_activable() const; + virtual void on_start_dragging(); + virtual void on_update(const UpdateData& data); + virtual void on_render() const; + virtual void on_render_for_picking() const; +#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI + virtual void on_render_input_window(float x, float y, float bottom_limit); +#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI private: void render_grabbers_connection(unsigned int id_1, unsigned int id_2) const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index fdc15e928..3e6530d30 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -94,8 +94,10 @@ void GLGizmoSlaSupports::set_sla_support_data(ModelObject* model_object, const S } } -void GLGizmoSlaSupports::on_render(const Selection& selection) const +void GLGizmoSlaSupports::on_render() const { + const Selection& selection = m_parent.get_selection(); + // If current m_model_object does not match selection, ask GLCanvas3D to turn us off if (m_state == On && (m_model_object != selection.get_model()->objects[selection.get_object_idx()] @@ -252,8 +254,9 @@ void GLGizmoSlaSupports::render_clipping_plane(const Selection& selection) const } -void GLGizmoSlaSupports::on_render_for_picking(const Selection& selection) const +void GLGizmoSlaSupports::on_render_for_picking() const { + const Selection& selection = m_parent.get_selection(); #if ENABLE_RENDER_PICKING_PASS m_z_shift = selection.get_volume(*selection.get_volume_idxs().begin())->get_sla_shift_z(); #endif @@ -702,12 +705,12 @@ void GLGizmoSlaSupports::delete_selected_points(bool force) //m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); } -void GLGizmoSlaSupports::on_update(const UpdateData& data, const Selection& selection) +void GLGizmoSlaSupports::on_update(const UpdateData& data) { - if (m_editing_mode && m_hover_id != -1 && data.mouse_pos && (!m_editing_mode_cache[m_hover_id].support_point.is_new_island || !m_lock_unique_islands)) { + if (m_editing_mode && m_hover_id != -1 && (!m_editing_mode_cache[m_hover_id].support_point.is_new_island || !m_lock_unique_islands)) { std::pair pos_and_normal; try { - pos_and_normal = unproject_on_mesh(Vec2d((*data.mouse_pos)(0), (*data.mouse_pos)(1))); + pos_and_normal = unproject_on_mesh(data.mouse_pos.cast()); } catch (...) { return; } m_editing_mode_cache[m_hover_id].support_point.pos = pos_and_normal.first; @@ -822,7 +825,7 @@ void GLGizmoSlaSupports::make_line_segments() const */ -void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) +void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_limit) { if (!m_model_object) return; @@ -1002,11 +1005,13 @@ RENDER_AGAIN: m_parent.set_as_dirty(); } -bool GLGizmoSlaSupports::on_is_activable(const Selection& selection) const +bool GLGizmoSlaSupports::on_is_activable() const { + const Selection& selection = m_parent.get_selection(); + if (wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA || !selection.is_from_single_instance()) - return false; + return false; // Check that none of the selected volumes is outside. Only SLA auxiliaries (supports) are allowed outside. const Selection::IndicesList& list = selection.get_volume_idxs(); @@ -1075,7 +1080,7 @@ void GLGizmoSlaSupports::on_set_state() -void GLGizmoSlaSupports::on_start_dragging(const Selection& selection) +void GLGizmoSlaSupports::on_start_dragging() { if (m_hover_id != -1) { select_point(NoPoints); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index 5e50eae0b..b0a505b82 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -70,9 +70,9 @@ public: private: bool on_init(); - void on_update(const UpdateData& data, const Selection& selection); - virtual void on_render(const Selection& selection) const; - virtual void on_render_for_picking(const Selection& selection) const; + void on_update(const UpdateData& data); + virtual void on_render() const; + virtual void on_render_for_picking() const; //void render_selection_rectangle() const; void render_points(const Selection& selection, bool picking = false) const; @@ -133,11 +133,11 @@ protected: if ((int)m_editing_mode_cache.size() <= m_hover_id) m_hover_id = -1; } - void on_start_dragging(const Selection& selection) override; - virtual void on_render_input_window(float x, float y, float bottom_limit, const Selection& selection) override; + void on_start_dragging() override; + virtual void on_render_input_window(float x, float y, float bottom_limit) override; virtual std::string on_get_name() const; - virtual bool on_is_activable(const Selection& selection) const; + virtual bool on_is_activable() const; virtual bool on_is_selectable() const; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 5922f9600..22b49ee5d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -150,7 +150,7 @@ void GLGizmosManager::refresh_on_off_state() GizmosMap::iterator it = m_gizmos.find(m_current); if ((it != m_gizmos.end()) && (it->second != nullptr)) { - if (!it->second->is_activable(m_parent->get_selection())) + if (!it->second->is_activable()) { it->second->set_state(GLGizmoBase::Off); m_current = Undefined; @@ -205,14 +205,14 @@ void GLGizmosManager::enable_grabber(EType type, unsigned int id, bool enable) } } -void GLGizmosManager::update(const Linef3& mouse_ray, const Point* mouse_pos) +void GLGizmosManager::update(const Linef3& mouse_ray, const Point& mouse_pos) { if (!m_enabled || (m_parent == nullptr)) return; GLGizmoBase* curr = get_current(); if (curr != nullptr) - curr->update(GLGizmoBase::UpdateData(mouse_ray, mouse_pos), m_parent->get_selection()); + curr->update(GLGizmoBase::UpdateData(mouse_ray, mouse_pos)); } void GLGizmosManager::update_data() @@ -282,8 +282,7 @@ bool GLGizmosManager::handle_shortcut(int key) if (!m_enabled || (m_parent == nullptr)) return false; - const Selection& selection = m_parent->get_selection(); - if (selection.is_empty()) + if (m_parent->get_selection().is_empty()) return false; EType old_current = m_current; @@ -295,7 +294,7 @@ bool GLGizmosManager::handle_shortcut(int key) int it_key = it->second->get_shortcut_key(); - if (it->second->is_activable(selection) && ((it_key == key - 64) || (it_key == key - 96))) + if (it->second->is_activable() && ((it_key == key - 64) || (it_key == key - 96))) { if ((it->second->get_state() == GLGizmoBase::On)) { @@ -338,7 +337,7 @@ void GLGizmosManager::start_dragging() GLGizmoBase* curr = get_current(); if (curr != nullptr) - curr->start_dragging(m_parent->get_selection()); + curr->start_dragging(); } void GLGizmosManager::stop_dragging() @@ -469,7 +468,7 @@ void GLGizmosManager::render_current_gizmo() const GLGizmoBase* curr = get_current(); if (curr != nullptr) - curr->render(m_parent->get_selection()); + curr->render(); } void GLGizmosManager::render_current_gizmo_for_picking_pass() const @@ -479,7 +478,7 @@ void GLGizmosManager::render_current_gizmo_for_picking_pass() const GLGizmoBase* curr = get_current(); if (curr != nullptr) - curr->render_for_picking(m_parent->get_selection()); + curr->render_for_picking(); } void GLGizmosManager::render_overlay() const @@ -589,7 +588,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) m_parent->get_wxglcanvas()->CaptureMouse(); m_parent->set_mouse_as_dragging(); - update(m_parent->mouse_ray(pos), &pos); + update(m_parent->mouse_ray(pos), pos); switch (m_current) { @@ -902,8 +901,6 @@ void GLGizmosManager::do_render_overlay() const if ((m_parent == nullptr) || m_gizmos.empty()) return; - const Selection& selection = m_parent->get_selection(); - float cnv_w = (float)m_parent->get_canvas_size().get_width(); float cnv_h = (float)m_parent->get_canvas_size().get_height(); float zoom = (float)m_parent->get_camera().get_zoom(); @@ -1020,7 +1017,7 @@ void GLGizmosManager::do_render_overlay() const GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + scaled_icons_size, top_y - scaled_icons_size, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } }); if (it->second->get_state() == GLGizmoBase::On) { float toolbar_top = (float)cnv_h - m_parent->get_view_toolbar_height(); - it->second->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top, selection); + it->second->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top); } top_y -= scaled_stride_y; } @@ -1087,8 +1084,6 @@ void GLGizmosManager::update_on_off_state(const Vec2d& mouse_pos) if (!m_enabled || (m_parent == nullptr)) return; - const Selection& selection = m_parent->get_selection(); - float cnv_h = (float)m_parent->get_canvas_size().get_height(); float height = get_total_overlay_height(); @@ -1104,7 +1099,7 @@ void GLGizmosManager::update_on_off_state(const Vec2d& mouse_pos) continue; bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); - if (it->second->is_activable(selection) && inside) + if (it->second->is_activable() && inside) { if ((it->second->get_state() == GLGizmoBase::On)) { @@ -1135,8 +1130,6 @@ std::string GLGizmosManager::update_hover_state(const Vec2d& mouse_pos) if (!m_enabled || (m_parent == nullptr)) return name; - const Selection& selection = m_parent->get_selection(); - float cnv_h = (float)m_parent->get_canvas_size().get_height(); float height = get_total_overlay_height(); float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; @@ -1154,7 +1147,7 @@ std::string GLGizmosManager::update_hover_state(const Vec2d& mouse_pos) if (inside) name = it->second->get_name(); - if (it->second->is_activable(selection) && (it->second->get_state() != GLGizmoBase::On)) + if (it->second->is_activable() && (it->second->get_state() != GLGizmoBase::On)) it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); top_y += scaled_stride_y; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index db73345ae..f41bed1e6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -140,7 +140,7 @@ public: void set_hover_id(int id); void enable_grabber(EType type, unsigned int id, bool enable); - void update(const Linef3& mouse_ray, const Point* mouse_pos = nullptr); + void update(const Linef3& mouse_ray, const Point& mouse_pos); void update_data(); EType get_current_type() const { return m_current; } From 401707a6fed24f44e468b10538092e74bd17d9ab Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Wed, 17 Jul 2019 12:43:27 +0200 Subject: [PATCH 72/72] Another refactoring of GLGizmosManager --- src/slic3r/GUI/GLCanvas3D.cpp | 3 +- src/slic3r/GUI/Gizmos/GLGizmosManager.cpp | 133 ++++++++++------------ src/slic3r/GUI/Gizmos/GLGizmosManager.hpp | 6 +- src/slic3r/GUI/Plater.cpp | 2 +- 4 files changed, 64 insertions(+), 80 deletions(-) diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 9bb9c2158..192112ba7 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1203,6 +1203,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar , m_camera(camera) , m_view_toolbar(view_toolbar) , m_toolbar(GLToolbar::Normal, "Top") + , m_gizmos(*this) , m_use_clipping_planes(false) , m_sidebar_field("") , m_keep_dirty(false) @@ -1319,7 +1320,7 @@ bool GLCanvas3D::init() // if (!m_volumes.empty()) // m_volumes.finalize_geometry(); - if (m_gizmos.is_enabled() && !m_gizmos.init(*this)) + if (m_gizmos.is_enabled() && !m_gizmos.init()) std::cout << "Unable to initialize gizmos: please, check that all the required textures are available" << std::endl; if (!_init_toolbar()) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 22b49ee5d..622fdb2d8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -14,8 +14,8 @@ namespace GUI { const float GLGizmosManager::Default_Icons_Size = 64; -GLGizmosManager::GLGizmosManager() - : m_parent(nullptr) +GLGizmosManager::GLGizmosManager(GLCanvas3D& parent) + : m_parent(parent) , m_enabled(false) , m_icons_texture_dirty(true) , m_current(Undefined) @@ -33,10 +33,8 @@ GLGizmosManager::~GLGizmosManager() reset(); } -bool GLGizmosManager::init(GLCanvas3D& parent) +bool GLGizmosManager::init() { - m_parent = &parent; - m_background_texture.metadata.filename = "toolbar_background.png"; m_background_texture.metadata.left = 16; m_background_texture.metadata.top = 16; @@ -52,7 +50,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent) } } - GLGizmoBase* gizmo = new GLGizmoMove3D(parent, "move.svg", 0); + GLGizmoBase* gizmo = new GLGizmoMove3D(m_parent, "move.svg", 0); if (gizmo == nullptr) return false; @@ -61,7 +59,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent) m_gizmos.insert(GizmosMap::value_type(Move, gizmo)); - gizmo = new GLGizmoScale3D(parent, "scale.svg", 1); + gizmo = new GLGizmoScale3D(m_parent, "scale.svg", 1); if (gizmo == nullptr) return false; @@ -70,7 +68,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent) m_gizmos.insert(GizmosMap::value_type(Scale, gizmo)); - gizmo = new GLGizmoRotate3D(parent, "rotate.svg", 2); + gizmo = new GLGizmoRotate3D(m_parent, "rotate.svg", 2); if (gizmo == nullptr) { reset(); @@ -85,7 +83,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent) m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo)); - gizmo = new GLGizmoFlatten(parent, "place.svg", 3); + gizmo = new GLGizmoFlatten(m_parent, "place.svg", 3); if (gizmo == nullptr) return false; @@ -96,7 +94,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent) m_gizmos.insert(GizmosMap::value_type(Flatten, gizmo)); - gizmo = new GLGizmoCut(parent, "cut.svg", 4); + gizmo = new GLGizmoCut(m_parent, "cut.svg", 4); if (gizmo == nullptr) return false; @@ -107,7 +105,7 @@ bool GLGizmosManager::init(GLCanvas3D& parent) m_gizmos.insert(GizmosMap::value_type(Cut, gizmo)); - gizmo = new GLGizmoSlaSupports(parent, "sla_supports.svg", 5); + gizmo = new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 5); if (gizmo == nullptr) return false; @@ -141,9 +139,6 @@ void GLGizmosManager::set_overlay_scale(float scale) void GLGizmosManager::refresh_on_off_state() { - if (m_parent == nullptr) - return; - if (m_serializing) return; @@ -207,7 +202,7 @@ void GLGizmosManager::enable_grabber(EType type, unsigned int id, bool enable) void GLGizmosManager::update(const Linef3& mouse_ray, const Point& mouse_pos) { - if (!m_enabled || (m_parent == nullptr)) + if (!m_enabled) return; GLGizmoBase* curr = get_current(); @@ -217,10 +212,10 @@ void GLGizmosManager::update(const Linef3& mouse_ray, const Point& mouse_pos) void GLGizmosManager::update_data() { - if (!m_enabled || (m_parent == nullptr)) + if (!m_enabled) return; - const Selection& selection = m_parent->get_selection(); + const Selection& selection = m_parent.get_selection(); bool is_wipe_tower = selection.is_wipe_tower(); enable_grabber(Move, 2, !is_wipe_tower); @@ -279,10 +274,10 @@ bool GLGizmosManager::is_running() const bool GLGizmosManager::handle_shortcut(int key) { - if (!m_enabled || (m_parent == nullptr)) + if (!m_enabled) return false; - if (m_parent->get_selection().is_empty()) + if (m_parent.get_selection().is_empty()) return false; EType old_current = m_current; @@ -332,7 +327,7 @@ bool GLGizmosManager::is_dragging() const void GLGizmosManager::start_dragging() { - if (!m_enabled || (m_parent == nullptr)) + if (!m_enabled) return; GLGizmoBase* curr = get_current(); @@ -427,12 +422,12 @@ void GLGizmosManager::set_flattening_data(const ModelObject* model_object) void GLGizmosManager::set_sla_support_data(ModelObject* model_object) { - if (!m_enabled || (m_parent == nullptr)) + if (!m_enabled) return; GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); if (it != m_gizmos.end()) - reinterpret_cast(it->second)->set_sla_support_data(model_object, m_parent->get_selection()); + reinterpret_cast(it->second)->set_sla_support_data(model_object, m_parent.get_selection()); } // Returns true if the gizmo used the event to do something, false otherwise. @@ -463,7 +458,7 @@ ClippingPlane GLGizmosManager::get_sla_clipping_plane() const void GLGizmosManager::render_current_gizmo() const { - if (!m_enabled || (m_parent == nullptr)) + if (!m_enabled) return; GLGizmoBase* curr = get_current(); @@ -473,7 +468,7 @@ void GLGizmosManager::render_current_gizmo() const void GLGizmosManager::render_current_gizmo_for_picking_pass() const { - if (!m_enabled || (m_parent == nullptr)) + if (!m_enabled) return; GLGizmoBase* curr = get_current(); @@ -483,7 +478,7 @@ void GLGizmosManager::render_current_gizmo_for_picking_pass() const void GLGizmosManager::render_overlay() const { - if (!m_enabled || (m_parent == nullptr)) + if (!m_enabled) return; if (m_icons_texture_dirty) @@ -494,9 +489,6 @@ void GLGizmosManager::render_overlay() const bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt) { - if (m_parent == nullptr) - return false; - bool processed = false; if (m_current == SlaSupports) { @@ -510,13 +502,10 @@ bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt) bool GLGizmosManager::on_mouse(wxMouseEvent& evt) { - if (m_parent == nullptr) - return false; - Point pos(evt.GetX(), evt.GetY()); Vec2d mouse_pos((double)evt.GetX(), (double)evt.GetY()); - Selection& selection = m_parent->get_selection(); + Selection& selection = m_parent.get_selection(); int selected_object_idx = selection.get_object_idx(); bool processed = false; @@ -562,33 +551,33 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) if (m_current == Flatten) { // Rotate the object so the normal points downward: - m_parent->do_flatten(get_flattening_normal(), "Gizmo - Place on Face"); + m_parent.do_flatten(get_flattening_normal(), "Gizmo-Place on Face"); wxGetApp().obj_manipul()->set_dirty(); } - m_parent->set_as_dirty(); + m_parent.set_as_dirty(); processed = true; } } else if (evt.RightDown() && (selected_object_idx != -1) && (m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::RightDown)) // event was taken care of by the SlaSupports gizmo processed = true; - else if (evt.Dragging() && (m_parent->get_move_volume_id() != -1) && (m_current == SlaSupports)) - // don't allow dragging objects with the Sla gizmo on + else if (evt.Dragging() && (m_parent.get_move_volume_id() != -1) && (m_current == SlaSupports)) + // don't allow dragging objects with the Sla gizmo on processed = true; else if (evt.Dragging() && (m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) { // the gizmo got the event and took some action, no need to do anything more here - m_parent->set_as_dirty(); + m_parent.set_as_dirty(); processed = true; } else if (evt.Dragging() && is_dragging()) { - if (!m_parent->get_wxglcanvas()->HasCapture()) - m_parent->get_wxglcanvas()->CaptureMouse(); + if (!m_parent.get_wxglcanvas()->HasCapture()) + m_parent.get_wxglcanvas()->CaptureMouse(); - m_parent->set_mouse_as_dragging(); - update(m_parent->mouse_ray(pos), pos); + m_parent.set_mouse_as_dragging(); + update(m_parent.mouse_ray(pos), pos); switch (m_current) { @@ -625,7 +614,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) break; } - m_parent->set_as_dirty(); + m_parent.set_as_dirty(); processed = true; } else if (evt.LeftUp() && is_dragging()) @@ -634,18 +623,18 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) { case Move: { - m_parent->disable_regenerate_volumes(); - m_parent->do_move("Gizmo-Move Object"); + m_parent.disable_regenerate_volumes(); + m_parent.do_move("Gizmo-Move"); break; } case Scale: { - m_parent->do_scale("Gizmo-Scale Object"); + m_parent.do_scale("Gizmo-Scale"); break; } case Rotate: { - m_parent->do_rotate("Gizmo-Rotate Object"); + m_parent.do_rotate("Gizmo-Rotate"); break; } default: @@ -658,20 +647,20 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) wxGetApp().obj_manipul()->set_dirty(); // Let the platter know that the dragging finished, so a delayed refresh // of the scene with the background processing data should be performed. - m_parent->post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED)); // updates camera target constraints - m_parent->refresh_camera_scene_box(); + m_parent.refresh_camera_scene_box(); processed = true; } - else if (evt.LeftUp() && (m_current == SlaSupports) && !m_parent->is_mouse_dragging()) + else if (evt.LeftUp() && (m_current == SlaSupports) && !m_parent.is_mouse_dragging()) { // in case SLA gizmo is selected, we just pass the LeftUp event and stop processing - neither // object moving or selecting is suppressed in that case gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown()); processed = true; } - else if (evt.LeftUp() && (m_current == Flatten) && ((m_parent->get_first_hover_volume_idx() != -1) || grabber_contains_mouse())) + else if (evt.LeftUp() && (m_current == Flatten) && ((m_parent.get_first_hover_volume_idx() != -1) || grabber_contains_mouse())) { // to avoid to loose the selection when user clicks an object while the Flatten gizmo is active processed = true; @@ -683,24 +672,24 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) if (evt.LeftDown() || evt.LeftDClick()) { m_mouse_capture.left = true; - m_mouse_capture.parent = m_parent; + m_mouse_capture.parent = &m_parent; processed = true; if (!selection.is_empty()) { update_on_off_state(mouse_pos); update_data(); - m_parent->set_as_dirty(); + m_parent.set_as_dirty(); } } else if (evt.MiddleDown()) { m_mouse_capture.middle = true; - m_mouse_capture.parent = m_parent; + m_mouse_capture.parent = &m_parent; } else if (evt.RightDown()) { m_mouse_capture.right = true; - m_mouse_capture.parent = m_parent; + m_mouse_capture.parent = &m_parent; } else if (evt.LeftUp()) processed = true; @@ -711,9 +700,6 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) bool GLGizmosManager::on_char(wxKeyEvent& evt) { - if (m_parent == nullptr) - return false; - // see include/wx/defs.h enum wxKeyCode int keyCode = evt.GetKeyCode(); int ctrlMask = wxMOD_CONTROL; @@ -828,16 +814,13 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) } if (processed) - m_parent->set_as_dirty(); + m_parent.set_as_dirty(); return processed; } bool GLGizmosManager::on_key(wxKeyEvent& evt) { - if (m_parent == nullptr) - return false; - const int keyCode = evt.GetKeyCode(); bool processed = false; @@ -862,19 +845,19 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) } // if (processed) -// m_parent->set_cursor(GLCanvas3D::Standard); +// m_parent.set_cursor(GLCanvas3D::Standard); } else if (evt.GetEventType() == wxEVT_KEY_DOWN) { if ((m_current == SlaSupports) && ((keyCode == WXK_SHIFT) || (keyCode == WXK_ALT)) && reinterpret_cast(get_current())->is_in_editing_mode()) { -// m_parent->set_cursor(GLCanvas3D::Cross); +// m_parent.set_cursor(GLCanvas3D::Cross); processed = true; } } if (processed) - m_parent->set_as_dirty(); + m_parent.set_as_dirty(); return processed; } @@ -898,12 +881,12 @@ void GLGizmosManager::reset() void GLGizmosManager::do_render_overlay() const { - if ((m_parent == nullptr) || m_gizmos.empty()) + if (m_gizmos.empty()) return; - float cnv_w = (float)m_parent->get_canvas_size().get_width(); - float cnv_h = (float)m_parent->get_canvas_size().get_height(); - float zoom = (float)m_parent->get_camera().get_zoom(); + float cnv_w = (float)m_parent.get_canvas_size().get_width(); + float cnv_h = (float)m_parent.get_canvas_size().get_height(); + float zoom = (float)m_parent.get_camera().get_zoom(); float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f; float height = get_total_overlay_height(); @@ -1016,7 +999,7 @@ void GLGizmosManager::do_render_overlay() const GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + scaled_icons_size, top_y - scaled_icons_size, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } }); if (it->second->get_state() == GLGizmoBase::On) { - float toolbar_top = (float)cnv_h - m_parent->get_view_toolbar_height(); + float toolbar_top = (float)cnv_h - m_parent.get_view_toolbar_height(); it->second->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top); } top_y -= scaled_stride_y; @@ -1081,10 +1064,10 @@ bool GLGizmosManager::generate_icons_texture() const void GLGizmosManager::update_on_off_state(const Vec2d& mouse_pos) { - if (!m_enabled || (m_parent == nullptr)) + if (!m_enabled) return; - float cnv_h = (float)m_parent->get_canvas_size().get_height(); + float cnv_h = (float)m_parent.get_canvas_size().get_height(); float height = get_total_overlay_height(); float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; @@ -1127,10 +1110,10 @@ std::string GLGizmosManager::update_hover_state(const Vec2d& mouse_pos) { std::string name = ""; - if (!m_enabled || (m_parent == nullptr)) + if (!m_enabled) return name; - float cnv_h = (float)m_parent->get_canvas_size().get_height(); + float cnv_h = (float)m_parent.get_canvas_size().get_height(); float height = get_total_overlay_height(); float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; float scaled_border = m_overlay_border * m_overlay_scale; @@ -1158,10 +1141,10 @@ std::string GLGizmosManager::update_hover_state(const Vec2d& mouse_pos) bool GLGizmosManager::overlay_contains_mouse(const Vec2d& mouse_pos) const { - if (!m_enabled || (m_parent == nullptr)) + if (!m_enabled) return false; - float cnv_h = (float)m_parent->get_canvas_size().get_height(); + float cnv_h = (float)m_parent.get_canvas_size().get_height(); float height = get_total_overlay_height(); float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index f41bed1e6..803613ec7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -60,7 +60,7 @@ public: }; private: - GLCanvas3D* m_parent; + GLCanvas3D& m_parent; bool m_enabled; typedef std::map GizmosMap; GizmosMap m_gizmos; @@ -92,10 +92,10 @@ private: bool m_serializing; public: - GLGizmosManager(); + explicit GLGizmosManager(GLCanvas3D& parent); ~GLGizmosManager(); - bool init(GLCanvas3D& parent); + bool init(); template void load(Archive& ar) diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index c5a4ccead..8fefca00c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -3885,7 +3885,7 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_uppe return; } - this->take_snapshot(_(L("Gizmo - Cut"))); + this->take_snapshot(_(L("Gizmo-Cut"))); wxBusyCursor wait; const auto new_objects = object->cut(instance_idx, z, keep_upper, keep_lower, rotate_lower);