From 1b3b4c5694815ebb9a5db754b2cecdd1592d5f4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= <hejl.lukas@gmail.com> Date: Mon, 22 Mar 2021 10:30:49 +0100 Subject: [PATCH 01/12] Added missing include (GCC 9.3) --- src/slic3r/GUI/GCodeViewer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 17e3ac68c..ed887d062 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -25,6 +25,7 @@ #include <boost/algorithm/string/split.hpp> #endif // ENABLE_GCODE_WINDOW #include <boost/nowide/cstdio.hpp> +#include <boost/nowide/fstream.hpp> #include <wx/progdlg.h> #include <wx/numformatter.h> From f5d3866847b6a292709a0c6fba9d05b09fc4eaa4 Mon Sep 17 00:00:00 2001 From: YuSanka <yusanka@gmail.com> Date: Mon, 22 Mar 2021 11:40:46 +0100 Subject: [PATCH 02/12] Follow-up https://github.com/prusa3d/PrusaSlicer/commit/995512f2807d1e026533c814f6fabf655a7610ab DoubleSlider improvements: Fixed a case when wipe tower is used to the end of print and there is one layer which is not marked in layers_times statistics --- src/slic3r/GUI/DoubleSlider.cpp | 9 ++++++++- src/slic3r/GUI/DoubleSlider.hpp | 2 +- src/slic3r/GUI/GUI_Preview.cpp | 6 ++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index de7a944f2..26a4f75a1 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -387,7 +387,7 @@ void Control::SetTicksValues(const Info& custom_gcode_per_print_z) Update(); } -void Control::SetLayersTimes(const std::vector<float>& layers_times) +void Control::SetLayersTimes(const std::vector<float>& layers_times, float total_time) { m_layers_times.clear(); if (layers_times.empty()) @@ -405,11 +405,18 @@ void Control::SetLayersTimes(const std::vector<float>& layers_times) m_layers_values = m_values; sort(m_layers_values.begin(), m_layers_values.end()); m_layers_values.erase(unique(m_layers_values.begin(), m_layers_values.end()), m_layers_values.end()); + + // When whipe tower is used to the end of print, there is one layer which is not marked in layers_times + // So, add this value from the total print time value + if (m_layers_values.size() != m_layers_times.size()) + for (size_t i = m_layers_times.size(); i < m_layers_values.size(); i++) + m_layers_times.push_back(total_time); } } void Control::SetLayersTimes(const std::vector<double>& layers_times) { + m_is_smart_wipe_tower = false; m_layers_times = layers_times; for (size_t i = 1; i < m_layers_times.size(); i++) m_layers_times[i] += m_layers_times[i - 1]; diff --git a/src/slic3r/GUI/DoubleSlider.hpp b/src/slic3r/GUI/DoubleSlider.hpp index de50bb0a2..24199b7ff 100644 --- a/src/slic3r/GUI/DoubleSlider.hpp +++ b/src/slic3r/GUI/DoubleSlider.hpp @@ -223,7 +223,7 @@ public: Info GetTicksValues() const; void SetTicksValues(const Info &custom_gcode_per_print_z); - void SetLayersTimes(const std::vector<float>& layers_times); + void SetLayersTimes(const std::vector<float>& layers_times, float total_time); void SetLayersTimes(const std::vector<double>& layers_times); void SetDrawMode(bool is_sla_print, bool is_sequential_print); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 0042c0702..5e2f8e8ed 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -639,8 +639,10 @@ void Preview::update_layers_slider(const std::vector<double>& layers_z, bool kee m_layers_slider->SetDrawMode(sla_print_technology, sequential_print); if (sla_print_technology) m_layers_slider->SetLayersTimes(plater->sla_print().print_statistics().layers_times); - else - m_layers_slider->SetLayersTimes(m_gcode_result->time_statistics.modes.front().layers_times); + else { + auto print_mode_stat = m_gcode_result->time_statistics.modes.front(); + m_layers_slider->SetLayersTimes(print_mode_stat.layers_times, print_mode_stat.time); + } // Suggest the auto color change, if model looks like sign if (m_layers_slider->IsNewPrint()) From 1c2d26457092f0d0e92bf0d40e6c7796505b6eb0 Mon Sep 17 00:00:00 2001 From: YuSanka <yusanka@gmail.com> Date: Mon, 22 Mar 2021 15:46:02 +0100 Subject: [PATCH 03/12] Fix for https://github.com/prusa3d/PrusaSlicer/commit/f5d3866847b6a292709a0c6fba9d05b09fc4eaa4 --- src/slic3r/GUI/DoubleSlider.cpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 26a4f75a1..87eb38925 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -659,7 +659,7 @@ void Control::draw_tick_on_mouse_position(wxDC& dc) } } -static std::string short_and_splitted_time(const std::string& time) +static wxString short_and_splitted_time(const std::string& time) { // Parse the dhms time format. int days = 0; @@ -697,7 +697,7 @@ static std::string short_and_splitted_time(const std::string& time) } else ::sprintf(buffer, "%ds", seconds); - return buffer; + return wxString::FromUTF8(buffer); } wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer*/) const @@ -719,9 +719,13 @@ wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer auto get_layer_number = [this](int value) { double layer_print_z = m_values[is_wipe_tower_layer(value) ? std::max<int>(value - 1, 0) : value]; auto it = std::lower_bound(m_layers_values.begin(), m_layers_values.end(), layer_print_z - epsilon()); - if (it == m_layers_values.end()) - return -1; - return int(it - m_layers_values.begin()); + if (it == m_layers_values.end()) { + it = std::lower_bound(m_values.begin(), m_values.end(), layer_print_z - epsilon()); + if (it == m_values.end()) + return size_t(-1); + return m_layers_values.size(); + } + return size_t(it - m_layers_values.begin()); }; #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER @@ -737,10 +741,10 @@ wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer else { if (label_type == ltEstimatedTime) { if (m_is_smart_wipe_tower) { - int layer_number = get_layer_number(value); - return layer_number < 0 ? "" : short_and_splitted_time(get_time_dhms(m_layers_times[layer_number])); + size_t layer_number = get_layer_number(value); + return layer_number == size_t(-1) ? wxEmptyString : short_and_splitted_time(get_time_dhms(m_layers_times[layer_number])); } - return value < m_layers_times.size() ? short_and_splitted_time(get_time_dhms(m_layers_times[value])) : ""; + return value < m_layers_times.size() ? short_and_splitted_time(get_time_dhms(m_layers_times[value])) : wxEmptyString; } wxString str = m_values.empty() ? wxString::Format("%.*f", 2, m_label_koef * value) : @@ -748,7 +752,7 @@ wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer if (label_type == ltHeight) return str; if (label_type == ltHeightWithLayer) { - size_t layer_number = m_is_smart_wipe_tower ? (size_t)get_layer_number(value) : (m_values.empty() ? value : value + 1); + size_t layer_number = m_is_smart_wipe_tower ? get_layer_number(value) : (m_values.empty() ? value : value + 1); return format_wxstr("%1%\n(%2%)", str, layer_number); } } From 00295919bf5323262fbc9118c9b9724a0e5129e8 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik <bubnikv@gmail.com> Date: Tue, 23 Mar 2021 09:59:08 +0100 Subject: [PATCH 04/12] Fixes of MutablePolygon --- src/libslic3r/MutablePolygon.cpp | 47 ++++++++++++++++++++++++++------ src/libslic3r/MutablePolygon.hpp | 8 +++--- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/libslic3r/MutablePolygon.cpp b/src/libslic3r/MutablePolygon.cpp index 104b4c6ee..54ce10b49 100644 --- a/src/libslic3r/MutablePolygon.cpp +++ b/src/libslic3r/MutablePolygon.cpp @@ -122,7 +122,7 @@ static bool clip_narrow_corner( } else { assert(backward == Free); p02 = it0.prev()->cast<int64_t>(); - if (cross2(p0 - p2, p02 - p2) > 0) + if (cross2(p02 - p2, p0 - p2) > 0) backward = Blocked; else { // New clipping edge lenght. @@ -141,14 +141,37 @@ static bool clip_narrow_corner( } } + assert(dist2_current <= shortcut_length2); + assert(polygon.size() >= 2); + assert(polygon.size() == 2 || forward == Blocked || forward == Far); + assert(polygon.size() == 2 || backward == Blocked || backward == Far); + if (polygon.size() <= 3) { // A hole degenerated to an empty polygon, or a tiny triangle remained. - assert(polygon.size() < 3 || (forward == Blocked && backward == Blocked) || (forward == Far && backward == Far)); - if (polygon.size() < 3 || forward == Far) { - assert(polygon.size() < 3 || dist2_current <= shortcut_length2); + bool blocked = forward == Blocked || backward == Blocked; + assert(polygon.size() < 3 || + // Remaining triangle is CCW oriented. Both sides must be "blocked", but the other side may have not been + // updated after the the p02 / p22 became united into a single point. + blocked || + // Remaining triangle is concave, however both of its arms are long. + (forward == Far && backward == Far)); +#ifndef _NDEBUG + if (polygon.size() == 3) { + // Verify that the remaining triangle is CCW or CW. + p02 = it0.prev()->cast<int64_t>(); + p22 = it2.next()->cast<int64_t>(); + assert(p02 == p22); + auto orient1 = cross2(p02 - p2, p0 - p2); + auto orient2 = cross2(p2 - p0, p22 - p0); + assert(orient1 > 0 == blocked); + assert(orient2 > 0 == blocked); + } +#endif // _NDEBUG + if (polygon.size() < 3 || (forward == Far && backward == Far)) { polygon.clear(); } else { // The remaining triangle is CCW oriented, keep it. + assert(forward == Blocked || backward == Blocked); } return true; } @@ -168,7 +191,7 @@ static bool clip_narrow_corner( } else if (forward == Blocked || backward == Blocked) { // One side is far, the other blocked. assert(forward == Far || backward == Far); - if (backward == Far) { + if (forward == Far) { // Sort, so we will clip the 1st edge. std::swap(p0, p2); std::swap(p02, p22); @@ -177,6 +200,10 @@ static bool clip_narrow_corner( // Circle intersects a line at two points, however because |p2 - p0| < shortcut_length, // only the second intersection is valid. Because |p2 - p02| > shortcut_length, such // intersection should always be found on (p0, p02). +#ifndef NDEBUG + auto dfar2 = (p02 - p2).squaredNorm(); + assert(dfar2 >= shortcut_length2); +#endif // NDEBUG const Vec2d v = (p02 - p0).cast<double>(); const Vec2d d = (p0 - p2).cast<double>(); const double a = v.squaredNorm(); @@ -273,15 +300,17 @@ void smooth_outward(MutablePolygon &polygon, coord_t clip_dist_scaled) Point pt_new = p1 + (t * v2d).cast<coord_t>(); #ifndef NDEBUG double d2new = (pt_new - (swap ? p2 : p0)).cast<double>().squaredNorm(); - assert(std::abs(d2new - clip_dist_scaled2) < sqr(10. * SCALED_EPSILON)); + assert(std::abs(d2new - clip_dist_scaled2) < 1e-5 * clip_dist_scaled2); #endif // NDEBUG it2.insert(pt_new); } else { // Cut the corner with a line perpendicular to the bisector. double t = sqrt(0.25 * clip_dist_scaled2 / d2); - assert(t > 0. && t < 1.); - Point p0 = p1 + (v1d * t).cast<coord_t>(); - Point p2 = p1 + (v2d * (t * lv2 / lv1)).cast<coord_t>(); + double t2 = t * lv1 / lv2; + assert(t > 0. && t < 1.); + assert(t2 > 0. && t2 < 1.); + Point p0 = p1 + (v1d * t ).cast<coord_t>(); + Point p2 = p1 + (v2d * t2).cast<coord_t>(); if (swap) std::swap(p0, p2); it2.insert(p2).insert(p0); diff --git a/src/libslic3r/MutablePolygon.hpp b/src/libslic3r/MutablePolygon.hpp index a601f19e3..ef2b61221 100644 --- a/src/libslic3r/MutablePolygon.hpp +++ b/src/libslic3r/MutablePolygon.hpp @@ -85,7 +85,7 @@ public: } void advance_front() { - assert(!this->empty()); + assert(! this->empty()); if (m_begin == m_end) this->make_empty(); else @@ -93,7 +93,7 @@ public: } void retract_back() { - assert(!this->empty()); + assert(! this->empty()); if (m_begin == m_end) this->make_empty(); else @@ -101,13 +101,13 @@ public: } MutablePolygon::iterator remove_front(MutablePolygon::iterator it) { - if (m_begin == it) + if (! this->empty() && m_begin == it) this->advance_front(); return it.remove(); } MutablePolygon::iterator remove_back(MutablePolygon::iterator it) { - if (m_end == it) + if (! this->empty() && m_end == it) this->retract_back(); return it.remove(); } From af9c7c967f08fcf22ffa628c324c90eda65230f1 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik <bubnikv@gmail.com> Date: Tue, 23 Mar 2021 11:06:31 +0100 Subject: [PATCH 05/12] Implementing a new switch for the shape of support towers: expanded to a grid (the old way) vs. snug (like the upstream Slic3r, Cura or Ideamaker). Snug supports suffered from the degeneracies when merging overhang islands over a large number of layers when projecting the support towers down. We borrowed the idea & a bit of code from Cura by simplifying the support polygons by closing the concave cracks, see the smooth_outward() function and the MutablePolygon class. Fixes Support problems with models with hole in the walls. #555 Fixes Support in the Air #740 Fixes [Bug] Supports generated beyond bed edges (X<0 and X>250) and where none are needed. #902 Fixes Unable to remove support material/can't change support "inflation distance" #2708 Fixes FR: support inflation and support conform to boundary #4783 Fixes Support blocker not working on this model #1346 Fixes Unnecessary support material #1993 Fixes support blocker enforcer issue #6240 --- src/libslic3r/Preset.cpp | 2 +- src/libslic3r/PrintConfig.cpp | 14 ++ src/libslic3r/PrintConfig.hpp | 15 ++ src/libslic3r/PrintObject.cpp | 1 + src/libslic3r/SupportMaterial.cpp | 318 ++++++++++++++------------ src/slic3r/GUI/ConfigManipulation.cpp | 2 +- src/slic3r/GUI/Field.cpp | 2 + src/slic3r/GUI/GUI.cpp | 2 + src/slic3r/GUI/GUI_Factories.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 1 + 10 files changed, 208 insertions(+), 151 deletions(-) diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 7dac34342..ecb20a18e 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -427,7 +427,7 @@ const std::vector<std::string>& Preset::print_options() "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield", "min_skirt_length", "brim_width", "brim_offset", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers", "raft_layers", "raft_first_layer_density", "raft_first_layer_expansion", "raft_contact_distance", "raft_expansion", - "support_material_pattern", "support_material_with_sheath", "support_material_spacing", + "support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_style", "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_bottom_interface_layers", "support_material_interface_pattern", "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", "support_material_bottom_contact_distance", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 692b4c47a..2c6918acf 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2419,6 +2419,20 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionFloat(60)); + def = this->add("support_material_style", coEnum); + def->label = L("Style"); + def->category = L("Support material"); + def->tooltip = L("Style and shape of the support towers. Projecting the supports into a regular grid " + "will create more stable supports, while snug support towers will save material and reduce " + "object scarring."); + def->enum_keys_map = &ConfigOptionEnum<SupportMaterialStyle>::get_enum_values(); + def->enum_values.push_back("grid"); + def->enum_values.push_back("snug"); + def->enum_labels.push_back(L("Grid")); + def->enum_labels.push_back(L("Snug")); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionEnum<SupportMaterialStyle>(smsGrid)); + def = this->add("support_material_synchronize_layers", coBool); def->label = L("Synchronize with object layers"); def->category = L("Support material"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index abeb29d4b..10f3b11d5 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -65,6 +65,10 @@ enum SupportMaterialPattern { smpRectilinear, smpRectilinearGrid, smpHoneycomb, }; +enum SupportMaterialStyle { + smsGrid, smsSnug, +}; + enum SupportMaterialInterfacePattern { smipAuto, smipRectilinear, smipConcentric, }; @@ -211,6 +215,15 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<SupportMaterialPa return keys_map; } +template<> inline const t_config_enum_values& ConfigOptionEnum<SupportMaterialStyle>::get_enum_values() { + static t_config_enum_values keys_map; + if (keys_map.empty()) { + keys_map["grid"] = smsGrid; + keys_map["snug"] = smsSnug; + } + return keys_map; +} + template<> inline const t_config_enum_values& ConfigOptionEnum<SupportMaterialInterfacePattern>::get_enum_values() { static t_config_enum_values keys_map; if (keys_map.empty()) { @@ -519,6 +532,7 @@ public: // Spacing between support material lines (the hatching distance). ConfigOptionFloat support_material_spacing; ConfigOptionFloat support_material_speed; + ConfigOptionEnum<SupportMaterialStyle> support_material_style; ConfigOptionBool support_material_synchronize_layers; // Overhang angle threshold. ConfigOptionInt support_material_threshold; @@ -570,6 +584,7 @@ protected: OPT_PTR(support_material_interface_pattern); OPT_PTR(support_material_spacing); OPT_PTR(support_material_speed); + OPT_PTR(support_material_style); OPT_PTR(support_material_synchronize_layers); OPT_PTR(support_material_xy_spacing); OPT_PTR(support_material_threshold); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index e86eb2c9c..1989b1884 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -588,6 +588,7 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "support_material_interface_extruder" || opt_key == "support_material_interface_spacing" || opt_key == "support_material_pattern" + || opt_key == "support_material_style" || opt_key == "support_material_xy_spacing" || opt_key == "support_material_spacing" || opt_key == "support_material_synchronize_layers" diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index c01da8f91..56ae04bce 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -6,6 +6,7 @@ #include "Fill/FillBase.hpp" #include "Geometry.hpp" #include "Point.hpp" +#include "MutablePolygon.hpp" #include <cmath> #include <memory> @@ -667,17 +668,19 @@ Polygons collect_slices_outer(const Layer &layer) struct SupportGridParams { SupportGridParams(const PrintObjectConfig &object_config, const Flow &support_material_flow) : + style(object_config.support_material_style.value), grid_resolution(object_config.support_material_spacing.value + support_material_flow.spacing()), support_angle(Geometry::deg2rad(object_config.support_material_angle.value)), extrusion_width(support_material_flow.spacing()), expansion_to_slice(coord_t(support_material_flow.scaled_spacing() / 2 + 5)), expansion_to_propagate(-3) {} - double grid_resolution; - double support_angle; - double extrusion_width; - coord_t expansion_to_slice; - coord_t expansion_to_propagate; + SupportMaterialStyle style; + double grid_resolution; + double support_angle; + double extrusion_width; + coord_t expansion_to_slice; + coord_t expansion_to_propagate; }; class SupportGridPattern @@ -689,75 +692,88 @@ public: // Trimming polygons, to trim the stretched support islands. support_polygons were already trimmed with trimming_polygons. const Polygons *trimming_polygons, const SupportGridParams ¶ms) : + m_style(params.style), m_support_polygons(support_polygons), m_trimming_polygons(trimming_polygons), m_support_spacing(params.grid_resolution), m_support_angle(params.support_angle) { - if (m_support_angle != 0.) { - // Create a copy of the rotated contours. - m_support_polygons_rotated = *support_polygons; - m_trimming_polygons_rotated = *trimming_polygons; - m_support_polygons = &m_support_polygons_rotated; - m_trimming_polygons = &m_trimming_polygons_rotated; - polygons_rotate(m_support_polygons_rotated, - params.support_angle); - polygons_rotate(m_trimming_polygons_rotated, - params.support_angle); - } - - // Resolution of the sparse support grid. - coord_t grid_resolution = coord_t(scale_(m_support_spacing)); - BoundingBox bbox = get_extents(*m_support_polygons); - bbox.offset(20); - // Align the bounding box with the sparse support grid. - bbox.align_to_grid(grid_resolution); - -#ifdef SUPPORT_USE_AGG_RASTERIZER - m_bbox = bbox; - // Oversample the grid to avoid leaking of supports through or around the object walls. - int oversampling = std::min(8, int(scale_(m_support_spacing) / (scale_(params.extrusion_width) + 100))); - m_pixel_size = scale_(m_support_spacing / oversampling); - assert(scale_(params.extrusion_width) + 20 < m_pixel_size); - // Add one empty column / row boundaries. - m_bbox.offset(m_pixel_size); - // Grid size fitting the support polygons plus one pixel boundary around the polygons. - Vec2i grid_size_raw(int(ceil((m_bbox.max.x() - m_bbox.min.x()) / m_pixel_size)), - int(ceil((m_bbox.max.y() - m_bbox.min.y()) / m_pixel_size))); - // Overlay macro blocks of (oversampling x oversampling) over the grid. - Vec2i grid_blocks((grid_size_raw.x() + oversampling - 1 - 2) / oversampling, - (grid_size_raw.y() + oversampling - 1 - 2) / oversampling); - // and resize the grid to fit the macro blocks + one pixel boundary. - m_grid_size = grid_blocks * oversampling + Vec2i(2, 2); - assert(m_grid_size.x() >= grid_size_raw.x()); - assert(m_grid_size.y() >= grid_size_raw.y()); - m_grid2 = rasterize_polygons(m_grid_size, m_pixel_size, m_bbox.min, *m_support_polygons); - - seed_fill_block(m_grid2, m_grid_size, - dilate_trimming_region(rasterize_polygons(m_grid_size, m_pixel_size, m_bbox.min, *m_trimming_polygons), m_grid_size), - grid_blocks, oversampling); - -#ifdef SLIC3R_DEBUG + switch (m_style) { + case smsGrid: { - static int irun; - Slic3r::png::write_gray_to_file_scaled(debug_out_path("support-rasterizer-%d.png", irun++), m_grid_size.x(), m_grid_size.y(), m_grid2.data(), 4); - } -#endif // SLIC3R_DEBUG + // Prepare the grid data, it will be reused when extracting support structures. + if (m_support_angle != 0.) { + // Create a copy of the rotated contours. + m_support_polygons_rotated = *support_polygons; + m_trimming_polygons_rotated = *trimming_polygons; + m_support_polygons = &m_support_polygons_rotated; + m_trimming_polygons = &m_trimming_polygons_rotated; + polygons_rotate(m_support_polygons_rotated, - params.support_angle); + polygons_rotate(m_trimming_polygons_rotated, - params.support_angle); + } -#else // SUPPORT_USE_AGG_RASTERIZER - // Create an EdgeGrid, initialize it with projection, initialize signed distance field. - m_grid.set_bbox(bbox); - m_grid.create(*m_support_polygons, grid_resolution); -#if 0 - if (m_grid.has_intersecting_edges()) { - // EdgeGrid fails to produce valid signed distance function for self-intersecting polygons. - m_support_polygons_rotated = simplify_polygons(*m_support_polygons); - m_support_polygons = &m_support_polygons_rotated; + // Resolution of the sparse support grid. + coord_t grid_resolution = coord_t(scale_(m_support_spacing)); + BoundingBox bbox = get_extents(*m_support_polygons); + bbox.offset(20); + // Align the bounding box with the sparse support grid. + bbox.align_to_grid(grid_resolution); + + #ifdef SUPPORT_USE_AGG_RASTERIZER + m_bbox = bbox; + // Oversample the grid to avoid leaking of supports through or around the object walls. + int oversampling = std::min(8, int(scale_(m_support_spacing) / (scale_(params.extrusion_width) + 100))); + m_pixel_size = scale_(m_support_spacing / oversampling); + assert(scale_(params.extrusion_width) + 20 < m_pixel_size); + // Add one empty column / row boundaries. + m_bbox.offset(m_pixel_size); + // Grid size fitting the support polygons plus one pixel boundary around the polygons. + Vec2i grid_size_raw(int(ceil((m_bbox.max.x() - m_bbox.min.x()) / m_pixel_size)), + int(ceil((m_bbox.max.y() - m_bbox.min.y()) / m_pixel_size))); + // Overlay macro blocks of (oversampling x oversampling) over the grid. + Vec2i grid_blocks((grid_size_raw.x() + oversampling - 1 - 2) / oversampling, + (grid_size_raw.y() + oversampling - 1 - 2) / oversampling); + // and resize the grid to fit the macro blocks + one pixel boundary. + m_grid_size = grid_blocks * oversampling + Vec2i(2, 2); + assert(m_grid_size.x() >= grid_size_raw.x()); + assert(m_grid_size.y() >= grid_size_raw.y()); + m_grid2 = rasterize_polygons(m_grid_size, m_pixel_size, m_bbox.min, *m_support_polygons); + + seed_fill_block(m_grid2, m_grid_size, + dilate_trimming_region(rasterize_polygons(m_grid_size, m_pixel_size, m_bbox.min, *m_trimming_polygons), m_grid_size), + grid_blocks, oversampling); + + #ifdef SLIC3R_DEBUG + { + static int irun; + Slic3r::png::write_gray_to_file_scaled(debug_out_path("support-rasterizer-%d.png", irun++), m_grid_size.x(), m_grid_size.y(), m_grid2.data(), 4); + } + #endif // SLIC3R_DEBUG + + #else // SUPPORT_USE_AGG_RASTERIZER + // Create an EdgeGrid, initialize it with projection, initialize signed distance field. m_grid.set_bbox(bbox); m_grid.create(*m_support_polygons, grid_resolution); -// assert(! m_grid.has_intersecting_edges()); - printf("SupportGridPattern: fixing polygons with intersection %s\n", - m_grid.has_intersecting_edges() ? "FAILED" : "SUCCEEDED"); + #if 0 + if (m_grid.has_intersecting_edges()) { + // EdgeGrid fails to produce valid signed distance function for self-intersecting polygons. + m_support_polygons_rotated = simplify_polygons(*m_support_polygons); + m_support_polygons = &m_support_polygons_rotated; + m_grid.set_bbox(bbox); + m_grid.create(*m_support_polygons, grid_resolution); + // assert(! m_grid.has_intersecting_edges()); + printf("SupportGridPattern: fixing polygons with intersection %s\n", + m_grid.has_intersecting_edges() ? "FAILED" : "SUCCEEDED"); + } + #endif + m_grid.calculate_sdf(); + #endif // SUPPORT_USE_AGG_RASTERIZER + break; + } + + case smsSnug: + default: + // nothing to prepare + break; } -#endif - m_grid.calculate_sdf(); -#endif // SUPPORT_USE_AGG_RASTERIZER } // Extract polygons from the grid, offsetted by offset_in_grid, @@ -770,97 +786,102 @@ public: #endif ) { -#ifdef SUPPORT_USE_AGG_RASTERIZER - Polygons support_polygons_simplified = contours_simplified(m_grid_size, m_pixel_size, m_bbox.min, m_grid2, offset_in_grid, fill_holes); -#else // SUPPORT_USE_AGG_RASTERIZER - // Generate islands, so each island may be tested for overlap with island_samples. - assert(std::abs(2 * offset_in_grid) < m_grid.resolution()); - Polygons support_polygons_simplified = m_grid.contours_simplified(offset_in_grid, fill_holes); -#endif // SUPPORT_USE_AGG_RASTERIZER + switch (m_style) { + case smsGrid: + { + #ifdef SUPPORT_USE_AGG_RASTERIZER + Polygons support_polygons_simplified = contours_simplified(m_grid_size, m_pixel_size, m_bbox.min, m_grid2, offset_in_grid, fill_holes); + #else // SUPPORT_USE_AGG_RASTERIZER + // Generate islands, so each island may be tested for overlap with island_samples. + assert(std::abs(2 * offset_in_grid) < m_grid.resolution()); + Polygons support_polygons_simplified = m_grid.contours_simplified(offset_in_grid, fill_holes); + #endif // SUPPORT_USE_AGG_RASTERIZER - ExPolygons islands = diff_ex(support_polygons_simplified, *m_trimming_polygons, false); + ExPolygons islands = diff_ex(support_polygons_simplified, *m_trimming_polygons, false); - // Extract polygons, which contain some of the island_samples. - Polygons out; -#if 0 - out = to_polygons(std::move(islands)); -#else + // Extract polygons, which contain some of the island_samples. + Polygons out; - // Sample a single point per input support polygon, keep it as a reference to maintain corresponding - // polygons if ever these polygons get split into parts by the trimming polygons. - // As offset_in_grid may be negative, m_support_polygons may stick slightly outside of islands. - // Trim ti with islands. - Points samples = island_samples( - offset_in_grid > 0 ? - // Expanding, thus m_support_polygons are all inside islands. - union_ex(*m_support_polygons) : - // Shrinking, thus m_support_polygons may be trimmed a tiny bit by islands. - intersection_ex(*m_support_polygons, to_polygons(islands))); + // Sample a single point per input support polygon, keep it as a reference to maintain corresponding + // polygons if ever these polygons get split into parts by the trimming polygons. + // As offset_in_grid may be negative, m_support_polygons may stick slightly outside of islands. + // Trim ti with islands. + Points samples = island_samples( + offset_in_grid > 0 ? + // Expanding, thus m_support_polygons are all inside islands. + union_ex(*m_support_polygons) : + // Shrinking, thus m_support_polygons may be trimmed a tiny bit by islands. + intersection_ex(*m_support_polygons, to_polygons(islands))); - std::vector<std::pair<Point,bool>> samples_inside; - for (ExPolygon &island : islands) { - BoundingBox bbox = get_extents(island.contour); - // Samples are sorted lexicographically. - auto it_lower = std::lower_bound(samples.begin(), samples.end(), Point(bbox.min - Point(1, 1))); - auto it_upper = std::upper_bound(samples.begin(), samples.end(), Point(bbox.max + Point(1, 1))); - samples_inside.clear(); - for (auto it = it_lower; it != it_upper; ++ it) - if (bbox.contains(*it)) - samples_inside.push_back(std::make_pair(*it, false)); - if (! samples_inside.empty()) { - // For all samples_inside count the boundary crossing. - for (size_t i_contour = 0; i_contour <= island.holes.size(); ++ i_contour) { - Polygon &contour = (i_contour == 0) ? island.contour : island.holes[i_contour - 1]; - Points::const_iterator i = contour.points.begin(); - Points::const_iterator j = contour.points.end() - 1; - for (; i != contour.points.end(); j = i ++) { - //FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point(1) well. - // Does the ray with y == point(1) intersect this line segment? - for (auto &sample_inside : samples_inside) { - if (((*i)(1) > sample_inside.first(1)) != ((*j)(1) > sample_inside.first(1))) { - double x1 = (double)sample_inside.first(0); - double x2 = (double)(*i)(0) + (double)((*j)(0) - (*i)(0)) * (double)(sample_inside.first(1) - (*i)(1)) / (double)((*j)(1) - (*i)(1)); - if (x1 < x2) - sample_inside.second = !sample_inside.second; + std::vector<std::pair<Point,bool>> samples_inside; + for (ExPolygon &island : islands) { + BoundingBox bbox = get_extents(island.contour); + // Samples are sorted lexicographically. + auto it_lower = std::lower_bound(samples.begin(), samples.end(), Point(bbox.min - Point(1, 1))); + auto it_upper = std::upper_bound(samples.begin(), samples.end(), Point(bbox.max + Point(1, 1))); + samples_inside.clear(); + for (auto it = it_lower; it != it_upper; ++ it) + if (bbox.contains(*it)) + samples_inside.push_back(std::make_pair(*it, false)); + if (! samples_inside.empty()) { + // For all samples_inside count the boundary crossing. + for (size_t i_contour = 0; i_contour <= island.holes.size(); ++ i_contour) { + Polygon &contour = (i_contour == 0) ? island.contour : island.holes[i_contour - 1]; + Points::const_iterator i = contour.points.begin(); + Points::const_iterator j = contour.points.end() - 1; + for (; i != contour.points.end(); j = i ++) { + //FIXME this test is not numerically robust. Particularly, it does not handle horizontal segments at y == point(1) well. + // Does the ray with y == point(1) intersect this line segment? + for (auto &sample_inside : samples_inside) { + if (((*i)(1) > sample_inside.first(1)) != ((*j)(1) > sample_inside.first(1))) { + double x1 = (double)sample_inside.first(0); + double x2 = (double)(*i)(0) + (double)((*j)(0) - (*i)(0)) * (double)(sample_inside.first(1) - (*i)(1)) / (double)((*j)(1) - (*i)(1)); + if (x1 < x2) + sample_inside.second = !sample_inside.second; + } } } } + // If any of the sample is inside this island, add this island to the output. + for (auto &sample_inside : samples_inside) + if (sample_inside.second) { + polygons_append(out, std::move(island)); + island.clear(); + break; + } } - // If any of the sample is inside this island, add this island to the output. - for (auto &sample_inside : samples_inside) - if (sample_inside.second) { - polygons_append(out, std::move(island)); - island.clear(); - break; - } } + + #ifdef SLIC3R_DEBUG + BoundingBox bbox = get_extents(*m_trimming_polygons); + if (! islands.empty()) + bbox.merge(get_extents(islands)); + if (!out.empty()) + bbox.merge(get_extents(out)); + if (!support_polygons_simplified.empty()) + bbox.merge(get_extents(support_polygons_simplified)); + SVG svg(debug_out_path("extract_support_from_grid_trimmed-%s-%d-%d-%lf.svg", step_name, iRun, layer_id, print_z).c_str(), bbox); + svg.draw(union_ex(support_polygons_simplified), "gray", 0.25f); + svg.draw(islands, "red", 0.5f); + svg.draw(union_ex(out), "green", 0.5f); + svg.draw(union_ex(*m_support_polygons), "blue", 0.5f); + svg.draw_outline(islands, "red", "red", scale_(0.05)); + svg.draw_outline(union_ex(out), "green", "green", scale_(0.05)); + svg.draw_outline(union_ex(*m_support_polygons), "blue", "blue", scale_(0.05)); + for (const Point &pt : samples) + svg.draw(pt, "black", coord_t(scale_(0.15))); + svg.Close(); + #endif /* SLIC3R_DEBUG */ + + if (m_support_angle != 0.) + polygons_rotate(out, m_support_angle); + return out; + } + case smsSnug: + // Just close the gaps. + float thr = scaled<float>(0.5); + return smooth_outward(offset(offset_ex(*m_support_polygons, thr), - thr), thr); } -#endif - -#ifdef SLIC3R_DEBUG - BoundingBox bbox = get_extents(*m_trimming_polygons); - if (! islands.empty()) - bbox.merge(get_extents(islands)); - if (!out.empty()) - bbox.merge(get_extents(out)); - if (!support_polygons_simplified.empty()) - bbox.merge(get_extents(support_polygons_simplified)); - SVG svg(debug_out_path("extract_support_from_grid_trimmed-%s-%d-%d-%lf.svg", step_name, iRun, layer_id, print_z).c_str(), bbox); - svg.draw(union_ex(support_polygons_simplified), "gray", 0.25f); - svg.draw(islands, "red", 0.5f); - svg.draw(union_ex(out), "green", 0.5f); - svg.draw(union_ex(*m_support_polygons), "blue", 0.5f); - svg.draw_outline(islands, "red", "red", scale_(0.05)); - svg.draw_outline(union_ex(out), "green", "green", scale_(0.05)); - svg.draw_outline(union_ex(*m_support_polygons), "blue", "blue", scale_(0.05)); - for (const Point &pt : samples) - svg.draw(pt, "black", coord_t(scale_(0.15))); - svg.Close(); -#endif /* SLIC3R_DEBUG */ - - if (m_support_angle != 0.) - polygons_rotate(out, m_support_angle); - return out; } #if defined(SLIC3R_DEBUG) && ! defined(SUPPORT_USE_AGG_RASTERIZER) @@ -1096,6 +1117,7 @@ private: return pts; } + SupportMaterialStyle m_style; const Polygons *m_support_polygons; const Polygons *m_trimming_polygons; Polygons m_support_polygons_rotated; @@ -1525,7 +1547,7 @@ static inline std::tuple<Polygons, Polygons, Polygons, float> detect_overhangs( ClipperLib::jtRound, // round mitter limit scale_(0.05)), - slices_margin_cached); + slices_margin.polygons); } #else diff_polygons = diff(diff_polygons, slices_margin.polygons); diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 6e0f13528..9334eea47 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -278,7 +278,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) bool have_support_material_auto = have_support_material && config->opt_bool("support_material_auto"); bool have_support_interface = config->opt_int("support_material_interface_layers") > 0; bool have_support_soluble = have_support_material && config->opt_float("support_material_contact_distance") == 0; - for (auto el : { "support_material_pattern", "support_material_with_sheath", + for (auto el : { "support_material_style", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_angle", "support_material_interface_pattern", "support_material_interface_layers", "dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance", diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index cbbe7f381..8e3fff06d 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -1224,6 +1224,8 @@ boost::any& Choice::get_value() m_value = static_cast<SupportMaterialPattern>(ret_enum); else if (m_opt_id.compare("support_material_interface_pattern") == 0) m_value = static_cast<SupportMaterialInterfacePattern>(ret_enum); + else if (m_opt_id.compare("support_material_style") == 0) + m_value = static_cast<SupportMaterialStyle>(ret_enum); else if (m_opt_id.compare("seam_position") == 0) m_value = static_cast<SeamPosition>(ret_enum); else if (m_opt_id.compare("host_type") == 0) diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 61ec4d233..1d3a85dc0 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -198,6 +198,8 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt config.set_key_value(opt_key, new ConfigOptionEnum<SupportMaterialPattern>(boost::any_cast<SupportMaterialPattern>(value))); else if (opt_key.compare("support_material_interface_pattern") == 0) config.set_key_value(opt_key, new ConfigOptionEnum<SupportMaterialInterfacePattern>(boost::any_cast<SupportMaterialInterfacePattern>(value))); + else if (opt_key.compare("support_material_style") == 0) + config.set_key_value(opt_key, new ConfigOptionEnum<SupportMaterialStyle>(boost::any_cast<SupportMaterialStyle>(value))); else if (opt_key.compare("seam_position") == 0) config.set_key_value(opt_key, new ConfigOptionEnum<SeamPosition>(boost::any_cast<SeamPosition>(value))); else if (opt_key.compare("host_type") == 0) diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index d8518a6c8..5ca8f8e2a 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -50,7 +50,7 @@ static SettingsFactory::Bundle FREQ_SETTINGS_BUNDLE_FFF = { L("Layers and Perimeters"), { "layer_height" , "perimeters", "top_solid_layers", "bottom_solid_layers" } }, { L("Infill") , { "fill_density", "fill_pattern" } }, { L("Support material") , { "support_material", "support_material_auto", "support_material_threshold", - "support_material_pattern", "support_material_interface_pattern", "support_material_buildplate_only", + "support_material_pattern", "support_material_pattern", "support_material_interface_pattern", "support_material_buildplate_only", "support_material_spacing" } }, { L("Wipe options") , { "wipe_into_infill", "wipe_into_objects" } } }; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index c68de4984..de2e84515 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1506,6 +1506,7 @@ void TabPrint::build() optgroup->append_single_option_line("raft_expansion"); optgroup = page->new_optgroup(L("Options for support material and raft")); + optgroup->append_single_option_line("support_material_style", category_path + "style"); optgroup->append_single_option_line("support_material_contact_distance", category_path + "contact-z-distance"); optgroup->append_single_option_line("support_material_bottom_contact_distance", category_path + "contact-z-distance"); optgroup->append_single_option_line("support_material_pattern", category_path + "pattern"); From cafa5b26a8b5900d87462237ee9401f9f823aafc Mon Sep 17 00:00:00 2001 From: Lukas Matena <lukasmatena@seznam.cz> Date: Tue, 23 Mar 2021 00:19:45 +0100 Subject: [PATCH 06/12] Compilation fixes --- src/libslic3r/SupportMaterial.cpp | 2 ++ src/slic3r/GUI/DoubleSlider.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 56ae04bce..bfd879e52 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -882,6 +882,8 @@ public: float thr = scaled<float>(0.5); return smooth_outward(offset(offset_ex(*m_support_polygons, thr), - thr), thr); } + assert(false); + return Polygons(); } #if defined(SLIC3R_DEBUG) && ! defined(SUPPORT_USE_AGG_RASTERIZER) diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index 87eb38925..80b74c437 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -742,9 +742,9 @@ wxString Control::get_label(int tick, LabelType label_type/* = ltHeightWithLayer if (label_type == ltEstimatedTime) { if (m_is_smart_wipe_tower) { size_t layer_number = get_layer_number(value); - return layer_number == size_t(-1) ? wxEmptyString : short_and_splitted_time(get_time_dhms(m_layers_times[layer_number])); + return layer_number == size_t(-1) ? "" : short_and_splitted_time(get_time_dhms(m_layers_times[layer_number])); } - return value < m_layers_times.size() ? short_and_splitted_time(get_time_dhms(m_layers_times[value])) : wxEmptyString; + return value < m_layers_times.size() ? short_and_splitted_time(get_time_dhms(m_layers_times[value])) : ""; } wxString str = m_values.empty() ? wxString::Format("%.*f", 2, m_label_koef * value) : From a0feb0f652ecc1062a7d239bf9e4b7b6d998b3cb Mon Sep 17 00:00:00 2001 From: enricoturri1966 <enricoturri@seznam.cz> Date: Tue, 23 Mar 2021 12:46:04 +0100 Subject: [PATCH 07/12] Preview and G-code viewer - Fixed synchronization between markers for pause print, color changes, custom g-code, retractions, deretractions and current line shown in g-code window --- src/libslic3r/GCode/GCodeProcessor.cpp | 2 +- src/slic3r/GUI/GCodeViewer.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index bd4e7a3ad..a3877845f 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -2572,7 +2572,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type) { MoveVertex vertex = { #if ENABLE_GCODE_LINES_ID_IN_H_SLIDER - m_line_id, + (type == EMoveType::Color_change || type == EMoveType::Pause_Print || type == EMoveType::Custom_GCode) ? m_line_id + 1 : m_line_id, #endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER type, m_extrusion_role, diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index ed887d062..66bdafb56 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -3151,7 +3151,7 @@ void GCodeViewer::refresh_render_paths(bool keep_sequential_current_first, bool const Path& path = buffer->paths[path_id]; #endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS const Path::Sub_Path& sub_path = path.sub_paths[sub_path_id]; - if (m_sequential_view.current.last <= sub_path.first.s_id || sub_path.last.s_id <= m_sequential_view.current.first) + if (m_sequential_view.current.last < sub_path.first.s_id || sub_path.last.s_id < m_sequential_view.current.first) continue; Color color; From 10c3e829178435203286a5843077af6f0467a5fb Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik <bubnikv@gmail.com> Date: Tue, 23 Mar 2021 13:50:41 +0100 Subject: [PATCH 08/12] Updated version number, alpha stores configs in alpha directory. --- src/slic3r/GUI/GUI_App.cpp | 4 ++-- version.inc | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index ecc5f46a7..b22cd6009 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -690,8 +690,8 @@ bool GUI_App::init_opengl() void GUI_App::init_app_config() { // Profiles for the alpha are stored into the PrusaSlicer-alpha directory to not mix with the current release. - SetAppName(SLIC3R_APP_KEY); -// SetAppName(SLIC3R_APP_KEY "-beta"); +// SetAppName(SLIC3R_APP_KEY); + SetAppName(SLIC3R_APP_KEY "-beta"); // SetAppDisplayName(SLIC3R_APP_NAME); // Set the Slic3r data directory at the Slic3r XS module. diff --git a/version.inc b/version.inc index 1a848cd6f..4e7aab235 100644 --- a/version.inc +++ b/version.inc @@ -3,7 +3,7 @@ set(SLIC3R_APP_NAME "PrusaSlicer") set(SLIC3R_APP_KEY "PrusaSlicer") -set(SLIC3R_VERSION "2.3.0") +set(SLIC3R_VERSION "2.4.0-alpha0") set(SLIC3R_BUILD_ID "PrusaSlicer-${SLIC3R_VERSION}+UNKNOWN") -set(SLIC3R_RC_VERSION "2,3,0,0") -set(SLIC3R_RC_VERSION_DOTS "2.3.0.0") +set(SLIC3R_RC_VERSION "2,4,0,0") +set(SLIC3R_RC_VERSION_DOTS "2.4.0.0") From 991fa67fd1e71dad6d2d5d10fc6e98ba714f8a9f Mon Sep 17 00:00:00 2001 From: YuSanka <yusanka@gmail.com> Date: Wed, 24 Mar 2021 10:45:37 +0100 Subject: [PATCH 09/12] OptionsSearcher improvements: Fixed a key for option() and groups_and_categories. It contains "preset_type;opt_key" now. This key helps to avoid a collisions by using a same options key from different type presets. Example: Option "elefant_foot_compensation" is in Print presets and SLA_printer presets --- src/slic3r/GUI/OptionsGroup.cpp | 2 +- src/slic3r/GUI/OptionsGroup.hpp | 8 +++-- src/slic3r/GUI/Plater.cpp | 2 +- src/slic3r/GUI/Search.cpp | 45 ++++++++++++++----------- src/slic3r/GUI/Search.hpp | 15 +++++---- src/slic3r/GUI/Tab.cpp | 6 ++-- src/slic3r/GUI/UnsavedChangesDialog.cpp | 22 +++++++----- 7 files changed, 59 insertions(+), 41 deletions(-) diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index c81f4c644..f70d678d2 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -535,7 +535,7 @@ Option ConfigOptionsGroup::get_option(const std::string& opt_key, int opt_index m_opt_map.emplace(opt_id, pair); if (m_use_custom_ctrl) // fill group and category values just for options from Settings Tab - wxGetApp().sidebar().get_searcher().add_key(opt_id, title, this->config_category()); + wxGetApp().sidebar().get_searcher().add_key(opt_id, static_cast<Preset::Type>(this->config_type()), title, this->config_category()); return Option(*m_config->def()->get(opt_key), opt_id); } diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index f19a3e033..11a52d648 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -224,10 +224,11 @@ public: ConfigOptionsGroup( wxWindow* parent) : OptionsGroup(parent, wxEmptyString, true, nullptr) {} - const std::string& config_category() const throw() { return m_config_category; } + const wxString& config_category() const throw() { return m_config_category; } + int config_type() const throw() { return m_config_type; } const t_opt_map& opt_map() const throw() { return m_opt_map; } - void set_config_category(const std::string &category) { this->m_config_category = category; } + void set_config_category_and_type(const wxString &category, int type) { this->m_config_category = category; this->m_config_type = type; } void set_config(DynamicPrintConfig* config) { m_config = config; m_modelconfig = nullptr; } Option get_option(const std::string& opt_key, int opt_index = -1); Line create_single_option_line(const std::string& title, const wxString& path = wxEmptyString, int idx = -1) /*const*/{ @@ -274,7 +275,8 @@ private: // If the config is modelconfig, then ModelConfig::touch() has to be called after value change. ModelConfig* m_modelconfig { nullptr }; t_opt_map m_opt_map; - std::string m_config_category; + wxString m_config_category; + int m_config_type; // Change an option on m_config, possibly call ModelConfig::touch(). void change_opt_value(const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index d7224cedc..f8ec2584c 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -992,7 +992,7 @@ void Sidebar::search() void Sidebar::jump_to_option(size_t selected) { const Search::Option& opt = p->searcher.get_option(selected); - wxGetApp().get_tab(opt.type)->activate_option(boost::nowide::narrow(opt.opt_key), boost::nowide::narrow(opt.category)); + wxGetApp().get_tab(opt.type)->activate_option(opt.opt_key(), boost::nowide::narrow(opt.category)); // Switch to the Settings NotePad // wxGetApp().mainframe->select_tab(); diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 03aa11eb6..9ff4e82a1 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -63,24 +63,29 @@ void change_opt_key(std::string& opt_key, DynamicPrintConfig* config, int& cnt) opt_key += "#" + std::to_string(0); } +static std::string get_key(const std::string& opt_key, Preset::Type type) +{ + return std::to_string(int(type)) + ";" + opt_key; +} + void OptionsSearcher::append_options(DynamicPrintConfig* config, Preset::Type type, ConfigOptionMode mode) { - auto emplace = [this, type](const std::string opt_key, const wxString& label) + auto emplace = [this, type](const std::string key, const wxString& label) { - const GroupAndCategory& gc = groups_and_categories[opt_key]; + const GroupAndCategory& gc = groups_and_categories[key]; if (gc.group.IsEmpty() || gc.category.IsEmpty()) return; wxString suffix; wxString suffix_local; if (gc.category == "Machine limits") { - suffix = opt_key.back()=='1' ? L("Stealth") : L("Normal"); + suffix = key.back()=='1' ? L("Stealth") : L("Normal"); suffix_local = " " + _(suffix); suffix = " " + suffix; } if (!label.IsEmpty()) - options.emplace_back(Option{ boost::nowide::widen(opt_key), type, + options.emplace_back(Option{ boost::nowide::widen(key), type, (label + suffix).ToStdWstring(), (_(label) + suffix_local).ToStdWstring(), gc.group.ToStdWstring(), _(gc.group).ToStdWstring(), gc.category.ToStdWstring(), GUI::Tab::translate_category(gc.category, type).ToStdWstring() }); @@ -108,12 +113,13 @@ void OptionsSearcher::append_options(DynamicPrintConfig* config, Preset::Type ty wxString label = opt.full_label.empty() ? opt.label : opt.full_label; + std::string key = get_key(opt_key, type); if (cnt == 0) - emplace(opt_key, label); + emplace(key, label); else for (int i = 0; i < cnt; ++i) // ! It's very important to use "#". opt_key#n is a real option key used in GroupAndCategory - emplace(opt_key + "#" + std::to_string(i), label); + emplace(key + "#" + std::to_string(i), label); } } @@ -237,10 +243,10 @@ bool OptionsSearcher::search(const std::string& search, bool force/* = false*/) int score2; matches.clear(); fuzzy_match(wsearch, label, score, matches); - if (fuzzy_match(wsearch, opt.opt_key, score2, matches2) && score2 > score) { + if (fuzzy_match(wsearch, opt.key, score2, matches2) && score2 > score) { for (fts::pos_type &pos : matches2) pos += label.size() + 1; - label += L"(" + opt.opt_key + L")"; + label += L"(" + opt.key + L")"; append(matches, matches2); score = score2; } @@ -318,9 +324,9 @@ const Option& OptionsSearcher::get_option(size_t pos_in_filter) const return options[found[pos_in_filter].option_idx]; } -const Option& OptionsSearcher::get_option(const std::string& opt_key) const +const Option& OptionsSearcher::get_option(const std::string& opt_key, Preset::Type type) const { - auto it = std::lower_bound(options.begin(), options.end(), Option({ boost::nowide::widen(opt_key) })); + auto it = std::lower_bound(options.begin(), options.end(), Option({ boost::nowide::widen(get_key(opt_key, type)) })); assert(it != options.end()); return options[it - options.begin()]; @@ -342,7 +348,7 @@ static Option create_option(const std::string& opt_key, const wxString& label, P category = wxString::Format("%s %d", "Extruder", atoi(opt_idx.c_str()) + 1); } - return Option{ boost::nowide::widen(opt_key), type, + return Option{ boost::nowide::widen(get_key(opt_key, type)), type, (label + suffix).ToStdWstring(), (_(label) + suffix_local).ToStdWstring(), gc.group.ToStdWstring(), _(gc.group).ToStdWstring(), gc.category.ToStdWstring(), GUI::Tab::translate_category(category, type).ToStdWstring() }; @@ -350,15 +356,16 @@ static Option create_option(const std::string& opt_key, const wxString& label, P Option OptionsSearcher::get_option(const std::string& opt_key, const wxString& label, Preset::Type type) const { - auto it = std::lower_bound(options.begin(), options.end(), Option({ boost::nowide::widen(opt_key) })); - if(it->opt_key == boost::nowide::widen(opt_key)) + std::string key = get_key(opt_key, type); + auto it = std::lower_bound(options.begin(), options.end(), Option({ boost::nowide::widen(key) })); + if(it->key == boost::nowide::widen(key)) return options[it - options.begin()]; - if (groups_and_categories.find(opt_key) == groups_and_categories.end()) { - size_t pos = opt_key.find('#'); + if (groups_and_categories.find(key) == groups_and_categories.end()) { + size_t pos = key.find('#'); if (pos == std::string::npos) return options[it - options.begin()]; - std::string zero_opt_key = opt_key.substr(0, pos + 1) + "0"; + std::string zero_opt_key = key.substr(0, pos + 1) + "0"; if(groups_and_categories.find(zero_opt_key) == groups_and_categories.end()) return options[it - options.begin()]; @@ -366,16 +373,16 @@ Option OptionsSearcher::get_option(const std::string& opt_key, const wxString& l return create_option(opt_key, label, type, groups_and_categories.at(zero_opt_key)); } - const GroupAndCategory& gc = groups_and_categories.at(opt_key); + const GroupAndCategory& gc = groups_and_categories.at(key); if (gc.group.IsEmpty() || gc.category.IsEmpty()) return options[it - options.begin()]; return create_option(opt_key, label, type, gc); } -void OptionsSearcher::add_key(const std::string& opt_key, const wxString& group, const wxString& category) +void OptionsSearcher::add_key(const std::string& opt_key, Preset::Type type, const wxString& group, const wxString& category) { - groups_and_categories[opt_key] = GroupAndCategory{group, category}; + groups_and_categories[get_key(opt_key, type)] = GroupAndCategory{group, category}; } diff --git a/src/slic3r/GUI/Search.hpp b/src/slic3r/GUI/Search.hpp index 1f2909564..30b969112 100644 --- a/src/slic3r/GUI/Search.hpp +++ b/src/slic3r/GUI/Search.hpp @@ -38,11 +38,11 @@ struct GroupAndCategory { struct Option { // bool operator<(const Option& other) const { return other.label > this->label; } - bool operator<(const Option& other) const { return other.opt_key > this->opt_key; } + bool operator<(const Option& other) const { return other.key > this->key; } // Fuzzy matching works at a character level. Thus matching with wide characters is a safer bet than with short characters, // though for some languages (Chinese?) it may not work correctly. - std::wstring opt_key; + std::wstring key; Preset::Type type {Preset::TYPE_INVALID}; std::wstring label; std::wstring label_local; @@ -50,6 +50,8 @@ struct Option { std::wstring group_local; std::wstring category; std::wstring category_local; + + std::string opt_key() const { return boost::nowide::narrow(key).substr(2); } }; struct FoundOption { @@ -110,13 +112,13 @@ public: bool search(); bool search(const std::string& search, bool force = false); - void add_key(const std::string& opt_key, const wxString& group, const wxString& category); + void add_key(const std::string& opt_key, Preset::Type type, const wxString& group, const wxString& category); size_t size() const { return found_size(); } const FoundOption& operator[](const size_t pos) const noexcept { return found[pos]; } const Option& get_option(size_t pos_in_filter) const; - const Option& get_option(const std::string& opt_key) const; + const Option& get_option(const std::string& opt_key, Preset::Type type) const; Option get_option(const std::string& opt_key, const wxString& label, Preset::Type type) const; const std::vector<FoundOption>& found_options() { return found; } @@ -125,10 +127,11 @@ public: void set_printer_technology(PrinterTechnology pt) { printer_technology = pt; } - void sort_options_by_opt_key() { + void sort_options_by_key() { std::sort(options.begin(), options.end(), [](const Option& o1, const Option& o2) { - return o1.opt_key < o2.opt_key; }); + return o1.key < o2.key; }); } + void sort_options_by_label() { sort_options(); } }; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index de2e84515..74b54478e 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -3802,8 +3802,8 @@ wxSizer* TabPrinter::create_bed_shape_widget(wxWindow* parent) { Search::OptionsSearcher& searcher = wxGetApp().sidebar().get_searcher(); const Search::GroupAndCategory& gc = searcher.get_group_and_category("bed_shape"); - searcher.add_key("bed_custom_texture", gc.group, gc.category); - searcher.add_key("bed_custom_model", gc.group, gc.category); + searcher.add_key("bed_custom_texture", m_type, gc.group, gc.category); + searcher.add_key("bed_custom_model", m_type, gc.group, gc.category); } return sizer; @@ -4044,7 +4044,6 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la { //! config_ have to be "right" ConfigOptionsGroupShp optgroup = std::make_shared<ConfigOptionsGroup>(m_parent, title, m_config, true); - optgroup->set_config_category(m_title.ToStdString()); if (noncommon_label_width >= 0) optgroup->label_width = noncommon_label_width; @@ -4053,6 +4052,7 @@ ConfigOptionsGroupShp Page::new_optgroup(const wxString& title, int noncommon_la #else auto tab = parent()->GetParent();// GetParent(); #endif + optgroup->set_config_category_and_type(m_title, static_cast<Tab*>(tab)->type()); optgroup->m_on_change = [tab](t_config_option_key opt_key, boost::any value) { //! This function will be called from OptionGroup. //! Using of CallAfter is redundant. diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index 7c0be76a4..0a0a3dc60 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -833,10 +833,10 @@ void UnsavedChangesDialog::build(Preset::Type type, PresetCollection* dependent_ (*btn)->Bind(wxEVT_LEAVE_WINDOW, [this](wxMouseEvent& e) { show_info_line(Action::Undef); e.Skip(); }); }; - const PresetCollection& printers = wxGetApp().preset_bundle->printers; - if (dependent_presets && (type == dependent_presets->type() ? + const PresetCollection* switched_presets = type == Preset::TYPE_INVALID ? nullptr : wxGetApp().get_tab(type)->get_presets(); + if (dependent_presets && switched_presets && (type == dependent_presets->type() ? dependent_presets->get_edited_preset().printer_technology() == dependent_presets->find_preset(new_selected_preset)->printer_technology() : - printers.get_edited_preset().printer_technology() == printers.find_preset(new_selected_preset)->printer_technology())) + switched_presets->get_edited_preset().printer_technology() == switched_presets->find_preset(new_selected_preset)->printer_technology())) add_btn(&m_transfer_btn, m_move_btn_id, "paste_menu", Action::Transfer, _L("Transfer")); add_btn(&m_discard_btn, m_continue_btn_id, dependent_presets ? "switch_presets" : "exit", Action::Discard, _L("Discard"), false); add_btn(&m_save_btn, m_save_btn_id, "save", Action::Save, _L("Save")); @@ -1185,7 +1185,7 @@ void UnsavedChangesDialog::update(Preset::Type type, PresetCollection* dependent void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* presets_) { Search::OptionsSearcher& searcher = wxGetApp().sidebar().get_searcher(); - searcher.sort_options_by_opt_key(); + searcher.sort_options_by_key(); // list of the presets with unsaved changes std::vector<PresetCollection*> presets_list; @@ -1227,8 +1227,8 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres } for (const std::string& opt_key : dirty_options) { - const Search::Option& option = searcher.get_option(opt_key); - if (option.opt_key != boost::nowide::widen(opt_key)) { + const Search::Option& option = searcher.get_option(opt_key, type); + if (option.opt_key() != opt_key) { // When founded option isn't the correct one. // It can be for dirty_options: "default_print_profile", "printer_model", "printer_settings_id", // because of they don't exist in searcher @@ -1239,6 +1239,9 @@ void UnsavedChangesDialog::update_tree(Preset::Type type, PresetCollection* pres get_string_value(opt_key, old_config), get_string_value(opt_key, new_config), category_icon_map.at(option.category)); } } + + // Revert sort of searcher back + searcher.sort_options_by_label(); } void UnsavedChangesDialog::on_dpi_changed(const wxRect& suggested_rect) @@ -1546,7 +1549,7 @@ void DiffPresetDialog::update_presets(Preset::Type type) void DiffPresetDialog::update_tree() { Search::OptionsSearcher& searcher = wxGetApp().sidebar().get_searcher(); - searcher.sort_options_by_opt_key(); + searcher.sort_options_by_key(); m_tree->Clear(); wxString bottom_info = ""; @@ -1617,7 +1620,7 @@ void DiffPresetDialog::update_tree() wxString right_val = get_string_value(opt_key, right_congig); Search::Option option = searcher.get_option(opt_key, get_full_label(opt_key, left_config), type); - if (option.opt_key != boost::nowide::widen(opt_key)) { + if (option.opt_key() != opt_key) { // temporary solution, just for testing m_tree->Append(opt_key, type, _L("Undef category"), _L("Undef group"), opt_key, left_val, right_val, "question"); // When founded option isn't the correct one. @@ -1642,6 +1645,9 @@ void DiffPresetDialog::update_tree() Fit(); Refresh(); } + + // Revert sort of searcher back + searcher.sort_options_by_label(); } void DiffPresetDialog::on_dpi_changed(const wxRect&) From 4496e2a8ce2371edfa9153e4f879b3b1e94a3b65 Mon Sep 17 00:00:00 2001 From: Oleksandra Yushchenko <yusanka@gmail.com> Date: Wed, 24 Mar 2021 11:20:57 +0100 Subject: [PATCH 10/12] Follow-up of 908c48ae6a1950eec1cf76840a06ee84e83ea155 -> Fixed update after switching tab after editing custom g-code in settings tabs (#6258) --- src/slic3r/GUI/MainFrame.cpp | 21 +++++++-------------- src/slic3r/GUI/Tab.cpp | 27 +++++++++------------------ src/slic3r/GUI/Tab.hpp | 1 + 3 files changed, 17 insertions(+), 32 deletions(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 858744229..a5a920f82 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -525,7 +525,13 @@ void MainFrame::init_tabpanel() m_tabpanel->Hide(); m_settings_dialog.set_tabpanel(m_tabpanel); - m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxEvent&) { + m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, [this](wxBookCtrlEvent& e) { +#if ENABLE_VALIDATE_CUSTOM_GCODE + Tab* old_tab = dynamic_cast<Tab*>(m_tabpanel->GetPage(e.GetOldSelection())); + if (old_tab) + old_tab->validate_custom_gcodes(); +#endif // ENABLE_VALIDATE_CUSTOM_GCODE + wxWindow* panel = m_tabpanel->GetCurrentPage(); Tab* tab = dynamic_cast<Tab*>(panel); @@ -544,19 +550,6 @@ void MainFrame::init_tabpanel() select_tab(size_t(0)); // select Plater }); -#if ENABLE_VALIDATE_CUSTOM_GCODE - m_tabpanel->Bind(wxEVT_NOTEBOOK_PAGE_CHANGING, [this](wxBookCtrlEvent& evt) { - wxWindow* panel = m_tabpanel->GetCurrentPage(); - if (panel != nullptr) { - Tab* tab = dynamic_cast<Tab*>(panel); - if (tab != nullptr) - tab->validate_custom_gcodes(); -// if (tab != nullptr && !tab->validate_custom_gcodes()) -// evt.Veto(); - } - }); -#endif // ENABLE_VALIDATE_CUSTOM_GCODE - m_plater = new Plater(this, this); m_plater->Hide(); diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 74b54478e..11c4875eb 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1738,7 +1738,7 @@ bool Tab::validate_custom_gcode(const wxString& title, const std::string& gcode) } static void validate_custom_gcode_cb(Tab* tab, ConfigOptionsGroupShp opt_group, const t_config_option_key& opt_key, const boost::any& value) { - Tab::validate_custom_gcode(opt_group->title, boost::any_cast<std::string>(value)); + tab->validate_custom_gcodes_was_shown = !Tab::validate_custom_gcode(opt_group->title, boost::any_cast<std::string>(value)); tab->update_dirty(); tab->on_value_change(opt_key, value); } @@ -3837,27 +3837,18 @@ bool Tab::validate_custom_gcodes() if (m_active_page->title() != L("Custom G-code")) return true; + // When we switch Settings tab after editing of the custom g-code, then warning message could ba already shown after KillFocus event + // and then it's no need to show it again + if (validate_custom_gcodes_was_shown) { + validate_custom_gcodes_was_shown = false; + return true; + } + bool valid = true; for (auto opt_group : m_active_page->m_optgroups) { assert(opt_group->opt_map().size() == 1); std::string key = opt_group->opt_map().begin()->first; - std::string value = boost::any_cast<std::string>(opt_group->get_value(key)); - std::string config_value = m_type == Preset::TYPE_FILAMENT ? m_config->opt_string(key, 0u) : m_config->opt_string(key); - valid &= validate_custom_gcode(opt_group->title, value); - Field* field = opt_group->get_field(key); - TextCtrl* text_ctrl = dynamic_cast<TextCtrl*>(field); - if (text_ctrl != nullptr && text_ctrl->m_on_change != nullptr && !text_ctrl->m_disable_change_event) { - Slic3r::GUI::t_change callback = opt_group->m_on_change; - // temporary disable the opt_group->m_on_change callback to avoid multiple validations - opt_group->m_on_change = nullptr; - text_ctrl->m_on_change(key, value); - // restore the opt_group->m_on_change callback - opt_group->m_on_change = callback; - - update_dirty(); - on_value_change(key, value); - } - + valid &= validate_custom_gcode(opt_group->title, boost::any_cast<std::string>(opt_group->get_value(key))); if (!valid) break; } diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index fcbe27eb7..8cbc6585a 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -351,6 +351,7 @@ public: #if ENABLE_VALIDATE_CUSTOM_GCODE static bool validate_custom_gcode(const wxString& title, const std::string& gcode); bool validate_custom_gcodes(); + bool validate_custom_gcodes_was_shown { false }; #endif // ENABLE_VALIDATE_CUSTOM_GCODE protected: From 8bc23c90fc59362374251967d2ba4c058d298aa0 Mon Sep 17 00:00:00 2001 From: YuSanka <yusanka@gmail.com> Date: Wed, 24 Mar 2021 11:31:50 +0100 Subject: [PATCH 11/12] Suppress to show Search window on the Plater using Ctrl+F shortcut, when we are at Preview mode --- src/slic3r/GUI/MainFrame.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index a5a920f82..35b1c16d8 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1151,7 +1151,7 @@ void MainFrame::init_menubar_as_editor() editMenu->AppendSeparator(); append_menu_item(editMenu, wxID_ANY, _L("Searc&h") + "\tCtrl+F", - _L("Search in settings"), [this](wxCommandEvent&) { m_plater->search(/*m_tabpanel->GetCurrentPage() == */m_plater->IsShown()); }, + _L("Search in settings"), [this](wxCommandEvent&) { m_plater->search(m_plater->IsShown()); }, "search", nullptr, []() {return true; }, this); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f8ec2584c..b4b025cd2 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5858,6 +5858,8 @@ void Plater::paste_from_clipboard() void Plater::search(bool plater_is_active) { if (plater_is_active) { + if (is_preview_shown()) + return; // plater should be focused for correct navigation inside search window this->SetFocus(); From 9c951b31089b764c1735fcc0491781e89f8cc32d Mon Sep 17 00:00:00 2001 From: enricoturri1966 <enricoturri@seznam.cz> Date: Wed, 24 Mar 2021 11:36:16 +0100 Subject: [PATCH 12/12] Fixed parsing of g-code files generated by newer versions of Simplify3D --- src/libslic3r/GCode/GCodeProcessor.cpp | 56 +++++++++++++++++--------- 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index a3877845f..06de1b24e 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -1491,89 +1491,109 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string_view comment) { // extrusion roles + // in older versions the comments did not contain the key 'feature' + std::string_view cmt = comment; + size_t pos = cmt.find(" feature"); + if (pos == 0) + cmt.remove_prefix(8); + // ; skirt - size_t pos = comment.find(" skirt"); + pos = cmt.find(" skirt"); if (pos == 0) { m_extrusion_role = erSkirt; return true; } // ; outer perimeter - pos = comment.find(" outer perimeter"); + pos = cmt.find(" outer perimeter"); if (pos == 0) { m_extrusion_role = erExternalPerimeter; return true; } // ; inner perimeter - pos = comment.find(" inner perimeter"); + pos = cmt.find(" inner perimeter"); if (pos == 0) { m_extrusion_role = erPerimeter; return true; } // ; gap fill - pos = comment.find(" gap fill"); + pos = cmt.find(" gap fill"); if (pos == 0) { m_extrusion_role = erGapFill; return true; } // ; infill - pos = comment.find(" infill"); + pos = cmt.find(" infill"); if (pos == 0) { m_extrusion_role = erInternalInfill; return true; } // ; solid layer - pos = comment.find(" solid layer"); + pos = cmt.find(" solid layer"); if (pos == 0) { - m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + m_extrusion_role = erSolidInfill; return true; } // ; bridge - pos = comment.find(" bridge"); + pos = cmt.find(" bridge"); if (pos == 0) { m_extrusion_role = erBridgeInfill; return true; } // ; support - pos = comment.find(" support"); + pos = cmt.find(" support"); if (pos == 0) { m_extrusion_role = erSupportMaterial; return true; } + // ; dense support + pos = cmt.find(" dense support"); + if (pos == 0) { + m_extrusion_role = erSupportMaterialInterface; + return true; + } + // ; prime pillar - pos = comment.find(" prime pillar"); + pos = cmt.find(" prime pillar"); if (pos == 0) { m_extrusion_role = erWipeTower; return true; } // ; ooze shield - pos = comment.find(" ooze shield"); + pos = cmt.find(" ooze shield"); if (pos == 0) { - m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< + m_extrusion_role = erNone; // Missing mapping return true; } // ; raft - pos = comment.find(" raft"); + pos = cmt.find(" raft"); if (pos == 0) { - m_extrusion_role = erSkirt; + m_extrusion_role = erSupportMaterial; + return true; + } + + // ; internal single extrusion + pos = cmt.find(" internal single extrusion"); + if (pos == 0) { + m_extrusion_role = erNone; // Missing mapping return true; } // geometry // ; tool std::string tag = " tool"; - pos = comment.find(tag); + pos = cmt.find(tag); if (pos == 0) { - const std::string_view data = comment.substr(pos + tag.length()); + const std::string_view data = cmt.substr(pos + tag.length()); std::string h_tag = "H"; size_t h_start = data.find(h_tag); size_t h_end = data.find_first_of(' ', h_start); @@ -1594,10 +1614,10 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string_view comment) // ; layer tag = " layer"; - pos = comment.find(tag); + pos = cmt.find(tag); if (pos == 0) { // skip lines "; layer end" - const std::string_view data = comment.substr(pos + tag.length()); + const std::string_view data = cmt.substr(pos + tag.length()); size_t end_start = data.find("end"); if (end_start == data.npos) ++m_layer_id;