From cbd25227c45d489e86121ef75f11d2062685e155 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 29 Mar 2023 16:10:00 +0200 Subject: [PATCH 01/13] More meaningful error message if slicing fails in SLA --- src/libslic3r/SLAPrintSteps.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index b73a8c4fb..3565f193a 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -511,8 +511,7 @@ void SLAPrint::Steps::slice_model(SLAPrintObject &po) if(slindex_it == po.m_slice_index.end()) //TRN To be shown at the status bar on SLA slicing error. throw Slic3r::RuntimeError( - _u8L("Slicing had to be stopped due to an internal error: " - "Inconsistent slice index.")); + format(_u8L("Model named: %s can not be sliced. Please check if the model is sane."), po.model_object()->name)); po.m_model_height_levels.clear(); po.m_model_height_levels.reserve(po.m_slice_index.size()); From 89b9f702d6cd59bc64c93687bc086f17a206cd0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Hejl?= Date: Wed, 29 Mar 2023 21:21:22 +0200 Subject: [PATCH 02/13] Fixed asserts and calculations in calculateExtrusionAreaDeviationError. The previous method worked just for near collinear edges. But it was also used for sharp corners, and for those sharp corners, there was an overflow in the computation of weighted width. Also, the computation of deviation error was wrong for those sharp corners. --- src/libslic3r/Arachne/utils/ExtrusionLine.cpp | 34 +++++++------------ src/libslic3r/Arachne/utils/ExtrusionLine.hpp | 3 +- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/Arachne/utils/ExtrusionLine.cpp b/src/libslic3r/Arachne/utils/ExtrusionLine.cpp index de5251639..8631d9e17 100644 --- a/src/libslic3r/Arachne/utils/ExtrusionLine.cpp +++ b/src/libslic3r/Arachne/utils/ExtrusionLine.cpp @@ -113,8 +113,7 @@ void ExtrusionLine::simplify(const int64_t smallest_line_segment_squared, const //h^2 = (L / b)^2 [square it] //h^2 = L^2 / b^2 [factor the divisor] const auto height_2 = int64_t(double(area_removed_so_far) * double(area_removed_so_far) / double(base_length_2)); - coord_t weighted_average_width; - const int64_t extrusion_area_error = calculateExtrusionAreaDeviationError(previous, current, next, weighted_average_width); + const int64_t extrusion_area_error = calculateExtrusionAreaDeviationError(previous, current, next); if ((height_2 <= scaled(0.001) //Almost exactly colinear (barring rounding errors). && Line::distance_to_infinite(current.p, previous.p, next.p) <= scaled(0.001)) // Make sure that height_2 is not small because of cancellation of positive and negative areas // We shouldn't remove middle junctions of colinear segments if the area changed for the C-P segment is exceeding the maximum allowed @@ -189,8 +188,7 @@ void ExtrusionLine::simplify(const int64_t smallest_line_segment_squared, const junctions = new_junctions; } -int64_t ExtrusionLine::calculateExtrusionAreaDeviationError(ExtrusionJunction A, ExtrusionJunction B, ExtrusionJunction C, coord_t& weighted_average_width) -{ +int64_t ExtrusionLine::calculateExtrusionAreaDeviationError(ExtrusionJunction A, ExtrusionJunction B, ExtrusionJunction C) { /* * A B C A C * --------------- ************** @@ -208,27 +206,19 @@ int64_t ExtrusionLine::calculateExtrusionAreaDeviationError(ExtrusionJunction A, * weighted-average width for the entire extrusion line. * * */ - const int64_t ab_length = (B - A).cast().norm(); - const int64_t bc_length = (C - B).cast().norm(); - const coord_t width_diff = std::max(std::abs(B.w - A.w), std::abs(C.w - B.w)); - if (width_diff > 1) - { + const int64_t ab_length = (B.p - A.p).cast().norm(); + const int64_t bc_length = (C.p - B.p).cast().norm(); + if (const coord_t width_diff = std::max(std::abs(B.w - A.w), std::abs(C.w - B.w)); width_diff > 1) { // Adjust the width only if there is a difference, or else the rounding errors may produce the wrong // weighted average value. - const int64_t ab_weight = (A.w + B.w) / 2; - const int64_t bc_weight = (B.w + C.w) / 2; - assert(((ab_length * ab_weight + bc_length * bc_weight) / (C - A).cast().norm()) <= std::numeric_limits::max()); - weighted_average_width = (ab_length * ab_weight + bc_length * bc_weight) / (C - A).cast().norm(); - assert((int64_t(std::abs(ab_weight - weighted_average_width)) * ab_length + int64_t(std::abs(bc_weight - weighted_average_width)) * bc_length) <= double(std::numeric_limits::max())); - return std::abs(ab_weight - weighted_average_width) * ab_length + std::abs(bc_weight - weighted_average_width) * bc_length; - } - else - { + const int64_t ab_weight = (A.w + B.w) / 2; + const int64_t bc_weight = (B.w + C.w) / 2; + const int64_t weighted_average_width = (ab_length * ab_weight + bc_length * bc_weight) / (ab_length + bc_length); + const int64_t ac_length = (C.p - A.p).cast().norm(); + return std::abs((ab_weight * ab_length + bc_weight * bc_length) - (weighted_average_width * ac_length)); + } else { // If the width difference is very small, then select the width of the segment that is longer - weighted_average_width = ab_length > bc_length ? A.w : B.w; - assert((int64_t(width_diff) * int64_t(bc_length)) <= std::numeric_limits::max()); - assert((int64_t(width_diff) * int64_t(ab_length)) <= std::numeric_limits::max()); - return ab_length > bc_length ? width_diff * bc_length : width_diff * ab_length; + return ab_length > bc_length ? int64_t(width_diff) * bc_length : int64_t(width_diff) * ab_length; } } diff --git a/src/libslic3r/Arachne/utils/ExtrusionLine.hpp b/src/libslic3r/Arachne/utils/ExtrusionLine.hpp index c0c5e7db1..d39e1e07b 100644 --- a/src/libslic3r/Arachne/utils/ExtrusionLine.hpp +++ b/src/libslic3r/Arachne/utils/ExtrusionLine.hpp @@ -186,9 +186,8 @@ struct ExtrusionLine * \param A Start point of the 3-point-straight line * \param B Intermediate point of the 3-point-straight line * \param C End point of the 3-point-straight line - * \param weighted_average_width The weighted average of the widths of the two colinear extrusion segments * */ - static int64_t calculateExtrusionAreaDeviationError(ExtrusionJunction A, ExtrusionJunction B, ExtrusionJunction C, coord_t& weighted_average_width); + static int64_t calculateExtrusionAreaDeviationError(ExtrusionJunction A, ExtrusionJunction B, ExtrusionJunction C); bool is_contour() const; From ec4c153dcfb528bddd311eb0e65fef3757fd52a6 Mon Sep 17 00:00:00 2001 From: kubispe1 <61363580+kubispe1@users.noreply.github.com> Date: Thu, 30 Mar 2023 10:24:18 +0200 Subject: [PATCH 03/13] Update config.yml Add customer support form --- .github/ISSUE_TEMPLATE/config.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index d373b69ee..a392acbb2 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,7 +1,10 @@ contact_links: - - name: PrusaSlicer Manual and Support + - name: Do you need Support? + url: https://www.prusa3d.com/page/prusaslicer-support-form_233563/ + about: If you are not sure whether what you are reporting is a bug, please contact our support team first. We are providing full 24/7 customer support. + - name: PrusaSlicer Manual url: https://help.prusa3d.com/en/article/customer-support_2287/ - about: If you are not sure that what you are reporting is really a bug, please, consult the manual first. + about: We have a comprehensive customer support page and help documentation that could be helpful for troubleshooting. - name: PrusaPrinters Forum url: https://forum.prusaprinters.org/forum/prusaslicer/ about: Please get in touch on our PrusaPrinters Community Forum! (Not an official support channel.) From 2b7cf2d877b58cb0a1ce42b968e63f89ff469337 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 30 Mar 2023 10:36:23 +0200 Subject: [PATCH 04/13] GCodeGenerator / custom G-codes: Added new read/only options providing the amount of total extruded material and per extruder extruded material up to the point the value is evaluated: "extruded_volume", "extruded_weight", "extruded_volume_total", "extruded_weight_total" --- src/libslic3r/GCode.cpp | 24 ++++++++++++++++++++++++ src/libslic3r/GCode.hpp | 4 ++++ 2 files changed, 28 insertions(+) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 4156fe4ee..bd4101dc9 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -438,6 +438,10 @@ void GCode::PlaceholderParserIntegration::reset() this->opt_e_position = nullptr; this->opt_e_retracted = nullptr; this->opt_e_restart_extra = nullptr; + this->opt_extruded_volume = nullptr; + this->opt_extruded_weight = nullptr; + this->opt_extruded_volume_total = nullptr; + this->opt_extruded_weight_total = nullptr; this->num_extruders = 0; this->position.clear(); this->e_position.clear(); @@ -463,6 +467,14 @@ void GCode::PlaceholderParserIntegration::init(const GCodeWriter &writer) this->output_config.set_key_value("e_position", opt_e_position); } } + this->opt_extruded_volume = new ConfigOptionFloats(this->num_extruders, 0.f); + this->opt_extruded_weight = new ConfigOptionFloats(this->num_extruders, 0.f); + this->opt_extruded_volume_total = new ConfigOptionFloat(0.f); + this->opt_extruded_weight_total = new ConfigOptionFloat(0.f); + this->parser.set("extruded_volume", this->opt_extruded_volume); + this->parser.set("extruded_weight", this->opt_extruded_weight); + this->parser.set("extruded_volume_total", this->opt_extruded_volume_total); + this->parser.set("extruded_weight_total", this->opt_extruded_weight_total); // Reserve buffer for current position. this->position.assign(3, 0); @@ -484,10 +496,22 @@ void GCode::PlaceholderParserIntegration::update_from_gcodewriter(const GCodeWri assert(! extruders.empty() && num_extruders == extruders.back().id() + 1); this->e_retracted.assign(num_extruders, 0); this->e_restart_extra.assign(num_extruders, 0); + this->opt_extruded_volume->values.assign(num_extruders, 0); + this->opt_extruded_weight->values.assign(num_extruders, 0); + double total_volume = 0.; + double total_weight = 0.; for (const Extruder &e : extruders) { this->e_retracted[e.id()] = e.retracted(); this->e_restart_extra[e.id()] = e.restart_extra(); + double v = e.extruded_volume(); + double w = v * e.filament_density() * 0.001; + this->opt_extruded_volume->values[e.id()] = v; + this->opt_extruded_weight->values[e.id()] = w; + total_volume += v; + total_weight += w; } + opt_extruded_volume_total->value = total_volume; + opt_extruded_weight_total->value = total_weight; opt_e_retracted->values = this->e_retracted; opt_e_restart_extra->values = this->e_restart_extra; if (! writer.config.use_relative_e_distances) { diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 98f49095d..346ececba 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -362,6 +362,10 @@ private: ConfigOptionFloats *opt_e_position { nullptr }; ConfigOptionFloats *opt_e_retracted { nullptr }; ConfigOptionFloats *opt_e_restart_extra { nullptr }; + ConfigOptionFloats *opt_extruded_volume { nullptr }; + ConfigOptionFloats *opt_extruded_weight { nullptr }; + ConfigOptionFloat *opt_extruded_volume_total { nullptr }; + ConfigOptionFloat *opt_extruded_weight_total { nullptr }; // Caches of the data passed to the script. size_t num_extruders; std::vector position; From e4c479aba44271632ca282b8c404411cdc9c13a7 Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Thu, 30 Mar 2023 12:19:45 +0200 Subject: [PATCH 05/13] Added first layer print times to print statistics written at the end of a G-code export. ; estimated first layer printing time (normal mode) = xxx ; estimated first layer printing time (silent mode) = yyy --- src/libslic3r/GCode/GCodeProcessor.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index a2c867505..9b371e405 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -3508,6 +3508,17 @@ void GCodeProcessor::post_process() ret += buf; } } + for (size_t i = 0; i < static_cast(PrintEstimatedStatistics::ETimeMode::Count); ++i) { + const TimeMachine& machine = m_time_processor.machines[i]; + PrintEstimatedStatistics::ETimeMode mode = static_cast(i); + if (mode == PrintEstimatedStatistics::ETimeMode::Normal || machine.enabled) { + char buf[128]; + sprintf(buf, "; estimated first layer printing time (%s mode) = %s\n", + (mode == PrintEstimatedStatistics::ETimeMode::Normal) ? "normal" : "silent", + get_time_dhms(machine.layers_time.empty() ? 0.f : machine.layers_time.front()).c_str()); + ret += buf; + } + } } } From 3a08097cd287fea71fe2867d67b7219665609a02 Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 30 Mar 2023 16:20:40 +0200 Subject: [PATCH 06/13] Improve performance of bridge over infill algorithm for very noisy/textured top surfaces improve bridging direction unification when briding areas start overlapping --- src/libslic3r/PrintObject.cpp | 118 ++++++++++++++++++++-------------- 1 file changed, 69 insertions(+), 49 deletions(-) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 26b359c0e..3f21a80c9 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1651,7 +1651,8 @@ void PrintObject::bridge_over_infill() unsupported_area = closing(unsupported_area, SCALED_EPSILON); // By expanding the lower layer solids, we avoid making bridges from the tiny internal overhangs that are (very likely) supported by previous layer solids // NOTE that we cannot filter out polygons worth bridging by their area, because sometimes there is a very small internal island that will grow into large hole - lower_layer_solids = expand(lower_layer_solids, 3 * spacing); + lower_layer_solids = shrink(lower_layer_solids, 1 * spacing); // first remove thin regions that will not support anything + lower_layer_solids = expand(lower_layer_solids, (1 + 3) * spacing); // then expand back (opening), and further for parts supported by internal solids // By shrinking the unsupported area, we avoid making bridges from narrow ensuring region along perimeters. unsupported_area = shrink(unsupported_area, 3 * spacing); unsupported_area = diff(unsupported_area, lower_layer_solids); @@ -1825,23 +1826,28 @@ void PrintObject::bridge_over_infill() std::map counted_directions; for (const Polygon &p : bridged_area) { + double acc_distance = 0; for (int point_idx = 0; point_idx < int(p.points.size()) - 1; ++point_idx) { Vec2d start = p.points[point_idx].cast(); Vec2d next = p.points[point_idx + 1].cast(); Vec2d v = next - start; // vector from next to current double dist_to_next = v.norm(); - v.normalize(); - int lines_count = int(std::ceil(dist_to_next / scaled(3.0))); - float step_size = dist_to_next / lines_count; - for (int i = 0; i < lines_count; ++i) { - Point a = (start + v * (i * step_size)).cast(); - auto [distance, index, p] = lines_tree.distance_from_lines_extra(a); - double angle = lines_tree.get_line(index).orientation(); - if (angle > PI) { - angle -= PI; + acc_distance += dist_to_next; + if (acc_distance > scaled(2.0)) { + acc_distance = 0.0; + v.normalize(); + int lines_count = int(std::ceil(dist_to_next / scaled(2.0))); + float step_size = dist_to_next / lines_count; + for (int i = 0; i < lines_count; ++i) { + Point a = (start + v * (i * step_size)).cast(); + auto [distance, index, p] = lines_tree.distance_from_lines_extra(a); + double angle = lines_tree.get_line(index).orientation(); + if (angle > PI) { + angle -= PI; + } + angle += PI * 0.5; + counted_directions[angle]++; } - angle += PI * 0.5; - counted_directions[angle]++; } } } @@ -2083,27 +2089,43 @@ void PrintObject::bridge_over_infill() }; return a.min.x() < b.min.x(); }); + if (surfaces_by_layer[lidx].size() > 2) { + Vec2d origin = get_extents(surfaces_by_layer[lidx].front().new_polys).max.cast(); + std::stable_sort(surfaces_by_layer[lidx].begin() + 1, surfaces_by_layer[lidx].end(), + [origin](const CandidateSurface &left, const CandidateSurface &right) { + auto a = get_extents(left.new_polys); + auto b = get_extents(right.new_polys); + + return (origin - a.min.cast()).squaredNorm() < + (origin - b.min.cast()).squaredNorm(); + }); + } // Gather deep infill areas, where thick bridges fit coordf_t spacing = surfaces_by_layer[lidx].front().region->flow(frSolidInfill, true).scaled_spacing(); coordf_t target_flow_height = surfaces_by_layer[lidx].front().region->flow(frSolidInfill, true).height() * target_flow_height_factor; Polygons deep_infill_area = gather_areas_w_depth(po, lidx, target_flow_height); - // Now also remove area that has been already filled on lower layers by bridging expansion - For this - // reason we did the clustering of layers per thread. - double bottom_z = layer->print_z - target_flow_height - EPSILON; - if (job_idx > 0) { - for (int lower_job_idx = job_idx - 1; lower_job_idx >= 0; lower_job_idx--) { - size_t lower_layer_idx = clustered_layers_for_threads[cluster_idx][lower_job_idx]; - const Layer *lower_layer = po->get_layer(lower_layer_idx); - if (lower_layer->print_z >= bottom_z) { - for (const auto &c : surfaces_by_layer[lower_layer_idx]) { - deep_infill_area = diff(deep_infill_area, c.new_polys); + { + // Now also remove area that has been already filled on lower layers by bridging expansion - For this + // reason we did the clustering of layers per thread. + Polygons filled_polyons_on_lower_layers; + double bottom_z = layer->print_z - target_flow_height - EPSILON; + if (job_idx > 0) { + for (int lower_job_idx = job_idx - 1; lower_job_idx >= 0; lower_job_idx--) { + size_t lower_layer_idx = clustered_layers_for_threads[cluster_idx][lower_job_idx]; + const Layer *lower_layer = po->get_layer(lower_layer_idx); + if (lower_layer->print_z >= bottom_z) { + for (const auto &c : surfaces_by_layer[lower_layer_idx]) { + filled_polyons_on_lower_layers.insert(filled_polyons_on_lower_layers.end(), c.new_polys.begin(), + c.new_polys.end()); + } + } else { + break; } - } else { - break; } } + deep_infill_area = diff(deep_infill_area, filled_polyons_on_lower_layers); } deep_infill_area = expand(deep_infill_area, spacing * 1.5); @@ -2123,9 +2145,8 @@ void PrintObject::bridge_over_infill() Polylines anchors = intersection_pl(infill_lines[lidx - 1], shrink(expansion_area, spacing)); #ifdef DEBUG_BRIDGE_OVER_INFILL - debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" + - "_total_area", - to_lines(total_fill_area), to_lines(expansion_area), to_lines(deep_infill_area), to_lines(anchors)); + debug_draw(std::to_string(lidx) + "_" + std::to_string(cluster_idx) + "_" + std::to_string(job_idx) + "_" + "_total_area", + to_lines(total_fill_area), to_lines(expansion_area), to_lines(deep_infill_area), to_lines(anchors)); #endif @@ -2186,6 +2207,7 @@ void PrintObject::bridge_over_infill() } bridging_area = opening(bridging_area, flow.scaled_spacing()); + bridging_area = closing(bridging_area, flow.scaled_spacing()); bridging_area = intersection(bridging_area, limiting_area); bridging_area = intersection(bridging_area, total_fill_area); expansion_area = diff(expansion_area, bridging_area); @@ -2220,35 +2242,33 @@ void PrintObject::bridge_over_infill() for (LayerRegion *region : layer->regions()) { Surfaces new_surfaces; + SurfacesPtr internal_infills = region->m_fill_surfaces.filter_by_type(stInternal); + ExPolygons new_internal_infills = diff_ex(internal_infills, cut_from_infill); + for (const ExPolygon &ep : new_internal_infills) { + new_surfaces.emplace_back(*internal_infills.front(), ep); + } + + SurfacesPtr internal_solids = region->m_fill_surfaces.filter_by_type(stInternalSolid); for (const CandidateSurface &cs : surfaces_by_layer.at(lidx)) { - for (Surface &surface : region->m_fill_surfaces.surfaces) { - if (cs.original_surface == &surface) { - Surface tmp(surface, {}); - for (const ExPolygon &expoly : diff_ex(surface.expolygon, cs.new_polys)) { - if (expoly.area() > region->flow(frSolidInfill).scaled_width() * scale_(4.0)) { - new_surfaces.emplace_back(tmp, expoly); - } - } + for (const Surface *surface : internal_solids) { + if (cs.original_surface == surface) { + Surface tmp{*surface, {}}; tmp.surface_type = stInternalBridge; tmp.bridge_angle = cs.bridge_angle; - for (const ExPolygon &expoly : union_ex(cs.new_polys)) { - new_surfaces.emplace_back(tmp, expoly); + for (const ExPolygon &ep : union_ex(cs.new_polys)) { + new_surfaces.emplace_back(tmp, ep); } - surface.clear(); - } else if (surface.surface_type == stInternal) { - Surface tmp(surface, {}); - for (const ExPolygon &expoly : diff_ex(surface.expolygon, cut_from_infill)) { - new_surfaces.emplace_back(tmp, expoly); - } - surface.clear(); + break; } } } - region->m_fill_surfaces.surfaces.insert(region->m_fill_surfaces.surfaces.end(), new_surfaces.begin(), new_surfaces.end()); - region->m_fill_surfaces.surfaces.erase(std::remove_if(region->m_fill_surfaces.surfaces.begin(), - region->m_fill_surfaces.surfaces.end(), - [](const Surface &s) { return s.empty(); }), - region->m_fill_surfaces.surfaces.end()); + ExPolygons new_internal_solids = diff_ex(internal_solids, cut_from_infill); + for (const ExPolygon &ep : new_internal_solids) { + new_surfaces.emplace_back(*internal_solids.front(), ep); + } + + region->m_fill_surfaces.remove_types({stInternalSolid, stInternal}); + region->m_fill_surfaces.append(new_surfaces); } } }); From d3037d325aeca1b001d3a3282c302efd5d87690b Mon Sep 17 00:00:00 2001 From: PavelMikus Date: Thu, 30 Mar 2023 16:56:47 +0200 Subject: [PATCH 07/13] Minor improvement of the special case of extra perimeters path ordering --- src/libslic3r/PerimeterGenerator.cpp | 12 ++++++++++++ src/libslic3r/SLAPrintSteps.cpp | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 7236f66ff..89add5978 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -1033,6 +1033,18 @@ std::tuple, Polygons> generate_extra_perimeters_over }; if (!first_overhang_is_closed_and_anchored) { std::reverse(overhang_region.begin(), overhang_region.end()); + } else { + size_t min_dist_idx = 0; + double min_dist = std::numeric_limits::max(); + for (size_t i = 0; i < overhang_region.front().polyline.size(); i++) { + Point p = overhang_region.front().polyline[i]; + if (double d = lower_layer_aabb_tree.distance_from_lines(p) < min_dist) { + min_dist = d; + min_dist_idx = i; + } + } + std::rotate(overhang_region.front().polyline.begin(), overhang_region.front().polyline.begin() + min_dist_idx, + overhang_region.front().polyline.end()); } auto first_unanchored = std::stable_partition(overhang_region.begin(), overhang_region.end(), is_anchored); int index_of_first_unanchored = first_unanchored - overhang_region.begin(); diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp index 3565f193a..0e505dfc2 100644 --- a/src/libslic3r/SLAPrintSteps.cpp +++ b/src/libslic3r/SLAPrintSteps.cpp @@ -31,7 +31,7 @@ #include "I18N.hpp" #include - +#include "format.hpp" namespace Slic3r { From d56f09a92a9464f96a2a1ccdafbdb6a399bb1168 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 31 Mar 2023 09:48:14 +0200 Subject: [PATCH 08/13] Fix removing neighbor duplicit point between first and last point in polygon mentioned by @BubnikV --- src/libslic3r/Emboss.cpp | 48 ++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index 65aa5a333..f24775e22 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -51,7 +51,7 @@ void remove_bad(ExPolygons &expolygons); // helpr for heal shape // Return true when erase otherwise false -bool remove_same_neighbor(Points &points); +bool remove_same_neighbor(Polygon &points); bool remove_same_neighbor(Polygons &polygons); bool remove_same_neighbor(ExPolygons &expolygons); @@ -272,14 +272,22 @@ void priv::remove_bad(ExPolygons &expolygons) { remove_bad(expolygon.holes); } -bool priv::remove_same_neighbor(Slic3r::Points &points) +bool priv::remove_same_neighbor(Slic3r::Polygon &polygon) { + Points &points = polygon.points; if (points.empty()) return false; auto last = std::unique(points.begin(), points.end()); - if (last == points.end()) return false; + + // remove first and last neighbor duplication + if (const Point& last_point = *(last - 1); + last_point == points.front()) { + --last; + } + + // no duplicits + if (last == points.end()) return false; + points.erase(last, points.end()); - // clear points without area - if (points.size() <= 2) points.clear(); return true; } @@ -287,34 +295,30 @@ bool priv::remove_same_neighbor(Polygons &polygons) { if (polygons.empty()) return false; bool exist = false; for (Polygon& polygon : polygons) - exist |= remove_same_neighbor(polygon.points); + exist |= remove_same_neighbor(polygon); // remove empty polygons polygons.erase( std::remove_if(polygons.begin(), polygons.end(), - [](const Polygon &p) { return p.empty(); }), + [](const Polygon &p) { return p.points.size() <= 2; }), polygons.end()); return exist; } bool priv::remove_same_neighbor(ExPolygons &expolygons) { if(expolygons.empty()) return false; - bool exist = false; + bool remove_from_holes = false; + bool remove_from_contour = false; for (ExPolygon &expoly : expolygons) { - exist |= remove_same_neighbor(expoly.contour.points); - Polygons &holes = expoly.holes; - for (Polygon &hole : holes) - exist |= remove_same_neighbor(hole.points); - holes.erase( - std::remove_if(holes.begin(), holes.end(), - [](const Polygon &p) { return p.size() < 3; }), - holes.end()); + remove_from_contour |= remove_same_neighbor(expoly.contour); + remove_from_holes |= remove_same_neighbor(expoly.holes); } - - // Removing of point could create polygon with less than 3 points - if (exist) - remove_bad(expolygons); - - return exist; + // Removing of expolygons without contour + if (remove_from_contour) + expolygons.erase( + std::remove_if(expolygons.begin(), expolygons.end(), + [](const ExPolygon &p) { return p.contour.points.size() <=2; }), + expolygons.end()); + return remove_from_holes || remove_from_contour; } Points priv::collect_close_points(const ExPolygons &expolygons, double distance) { From e1861d220c8709d129a806d233cd2afd248d0f81 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Mon, 20 Feb 2023 14:10:59 +0100 Subject: [PATCH 09/13] Revert "Try to handle fff supports in arrange after slicing" This reverts commit 92e0c1351319d2a8e2d4f5c399d3694a85e1be2d. --- src/slic3r/GUI/Jobs/ArrangeJob.cpp | 45 +----------------------------- 1 file changed, 1 insertion(+), 44 deletions(-) diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index 826dc2407..8115136a5 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -212,49 +212,6 @@ static coord_t brim_offset(const PrintObject &po, const ModelInstance &inst) return has_outer_brim ? scaled(brim_width + brim_separation) : 0; } -template -Polygon support_layers_chull (Points &pts, It from_lyr, It to_lyr) { - - size_t cap = 0; - for (auto it = from_lyr; it != to_lyr; ++it) { - for (const ExPolygon &expoly : (*it)->support_islands) - cap += expoly.contour.points.size(); - } - - pts.reserve(pts.size() + cap); - - for (auto it = from_lyr; it != to_lyr; ++it) { - for (const ExPolygon &expoly : (*it)->support_islands) - std::copy(expoly.contour.begin(), expoly.contour.end(), - std::back_inserter(pts)); - } - - Polygon ret = Geometry::convex_hull(pts); - - return ret; -} - -static void update_arrangepoly_fffprint(arrangement::ArrangePolygon &ret, - const PrintObject &po, - const ModelInstance &inst) -{ - auto laststep = po.last_completed_step(); - - coord_t infl = brim_offset(po, inst); - - if (laststep < posCount && laststep > posSupportMaterial) { - Points pts = std::move(ret.poly.contour.points); - Polygon poly = support_layers_chull(pts, - po.support_layers().begin(), - po.support_layers().end()); - - ret.poly.contour = std::move(poly); - ret.poly.holes = {}; - } - - ret.inflation = infl; -} - arrangement::ArrangePolygon ArrangeJob::get_arrange_poly_(ModelInstance *mi) { arrangement::ArrangePolygon ap = get_arrange_poly(mi, m_plater); @@ -442,7 +399,7 @@ arrangement::ArrangePolygon get_arrange_poly(ModelInstance *inst, plater->fff_print().get_print_object_by_model_object_id(obj_id); if (po) { - update_arrangepoly_fffprint(ap, *po, *inst); + ap.inflation = brim_offset(*po, *inst); } } From 6be2aa29dc830bddf22f30a54dcbc12e8a3613f1 Mon Sep 17 00:00:00 2001 From: David Kocik Date: Fri, 31 Mar 2023 13:10:56 +0200 Subject: [PATCH 10/13] Alternative eject function (IOCTL_STORAGE_EJECT_MEDIA) Detect if device has CARD READER configuration in Configuration Descriptor log --- src/slic3r/GUI/RemovableDriveManager.cpp | 315 ++++++++++++++++++++++- 1 file changed, 303 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/RemovableDriveManager.cpp b/src/slic3r/GUI/RemovableDriveManager.cpp index b6881f1ae..ecd8e0727 100644 --- a/src/slic3r/GUI/RemovableDriveManager.cpp +++ b/src/slic3r/GUI/RemovableDriveManager.cpp @@ -13,6 +13,11 @@ #include #include #include + +#include // include before devpropdef.h +#include +#include +#include #else // unix, linux & OSX includes #include @@ -73,6 +78,287 @@ std::vector RemovableDriveManager::search_for_removable_drives() cons } namespace { +int eject_alt(const std::wstring& volume_access_path) +{ + HANDLE handle = CreateFileW(volume_access_path.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr); + if (handle == INVALID_HANDLE_VALUE) { + BOOST_LOG_TRIVIAL(error) << "Alt Ejecting " << volume_access_path << " failed (handle == INVALID_HANDLE_VALUE): " << GetLastError(); + return 1; + } + DWORD deviceControlRetVal(0); + //these 3 commands should eject device safely but they dont, the device does disappear from file explorer but the "device was safely remove" notification doesnt trigger. + //sd cards does trigger WM_DEVICECHANGE messege, usb drives dont + BOOL e1 = DeviceIoControl(handle, FSCTL_LOCK_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr); + BOOST_LOG_TRIVIAL(debug) << "FSCTL_LOCK_VOLUME " << e1 << " ; " << deviceControlRetVal << " ; " << GetLastError(); + BOOL e2 = DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr); + BOOST_LOG_TRIVIAL(debug) << "FSCTL_DISMOUNT_VOLUME " << e2 << " ; " << deviceControlRetVal << " ; " << GetLastError(); + + // some implemenatations also calls IOCTL_STORAGE_MEDIA_REMOVAL here with FALSE as third parameter, which should set PreventMediaRemoval + BOOL error = DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr); + if (error == 0) { + CloseHandle(handle); + BOOST_LOG_TRIVIAL(error) << "Alt Ejecting " << volume_access_path << " failed (IOCTL_STORAGE_EJECT_MEDIA)" << deviceControlRetVal << " " << GetLastError(); + return 1; + } + CloseHandle(handle); + BOOST_LOG_TRIVIAL(info) << "Alt Ejecting finished"; + return 0; +} + + +// From https://github.com/microsoft/Windows-driver-samples/tree/main/usb/usbview +typedef struct _STRING_DESCRIPTOR_NODE +{ + struct _STRING_DESCRIPTOR_NODE* Next; + UCHAR DescriptorIndex; + USHORT LanguageID; + USB_STRING_DESCRIPTOR StringDescriptor[1]; +} STRING_DESCRIPTOR_NODE, * PSTRING_DESCRIPTOR_NODE; + +// Based at https://github.com/microsoft/Windows-driver-samples/tree/main/usb/usbview +PSTRING_DESCRIPTOR_NODE GetStringDescriptor( + HANDLE handle_hub_device, + ULONG connection_index, + UCHAR descriptor_index, + USHORT language_ID +) +{ + BOOL success = 0; + ULONG nbytes = 0; + ULONG nbytes_returned = 0; + UCHAR string_desc_req_buf[sizeof(USB_DESCRIPTOR_REQUEST) + MAXIMUM_USB_STRING_LENGTH]; + PUSB_DESCRIPTOR_REQUEST string_desc_req = NULL; + PUSB_STRING_DESCRIPTOR string_desc = NULL; + PSTRING_DESCRIPTOR_NODE string_desc_node = NULL; + + nbytes = sizeof(string_desc_req_buf); + string_desc_req = (PUSB_DESCRIPTOR_REQUEST)string_desc_req_buf; + string_desc = (PUSB_STRING_DESCRIPTOR)(string_desc_req + 1); + + // Zero fill the entire request structure + memset(string_desc_req, 0, nbytes); + + // Indicate the port from which the descriptor will be requested + string_desc_req->ConnectionIndex = connection_index; + + // USBHUB uses URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE to process this + // IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION request. + // + // USBD will automatically initialize these fields: + // bmRequest = 0x80 + // bRequest = 0x06 + // + // We must inititialize these fields: + // wValue = Descriptor Type (high) and Descriptor Index (low byte) + // wIndex = Zero (or Language ID for String Descriptors) + // wLength = Length of descriptor buffer + string_desc_req->SetupPacket.wValue = (USB_STRING_DESCRIPTOR_TYPE << 8) + | descriptor_index; + string_desc_req->SetupPacket.wIndex = language_ID; + string_desc_req->SetupPacket.wLength = (USHORT)(nbytes - sizeof(USB_DESCRIPTOR_REQUEST)); + + // Now issue the get descriptor request. + success = DeviceIoControl(handle_hub_device, + IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, + string_desc_req, + nbytes, + string_desc_req, + nbytes, + &nbytes_returned, + NULL); + + // Do some sanity checks on the return from the get descriptor request. + if (!success) { + return NULL; + } + if (nbytes_returned < 2) { + return NULL; + } + if (string_desc->bDescriptorType != USB_STRING_DESCRIPTOR_TYPE) { + return NULL; + } + if (string_desc->bLength != nbytes_returned - sizeof(USB_DESCRIPTOR_REQUEST)) { + return NULL; + } + if (string_desc->bLength % 2 != 0) { + return NULL; + } + + // Looks good, allocate some (zero filled) space for the string descriptor + // node and copy the string descriptor to it. + string_desc_node = (PSTRING_DESCRIPTOR_NODE)malloc(sizeof(STRING_DESCRIPTOR_NODE) + string_desc->bLength * sizeof(DWORD)); + if (string_desc_node == NULL) { + return NULL; + } + string_desc_node->Next = NULL; + string_desc_node->DescriptorIndex = descriptor_index; + string_desc_node->LanguageID = language_ID; + + memcpy(string_desc_node->StringDescriptor, + string_desc, + string_desc->bLength); + + return string_desc_node; +} + +// Based at https://github.com/microsoft/Windows-driver-samples/tree/main/usb/usbview +HRESULT GetStringDescriptors( + _In_ HANDLE handle_hub_device, + _In_ ULONG connection_index, + _In_ UCHAR descriptor_index, + _In_ ULONG num_language_IDs, + _In_reads_(num_language_IDs) USHORT* language_IDs, + _In_ PSTRING_DESCRIPTOR_NODE string_desc_node_head, + std::wstring& result +) +{ + PSTRING_DESCRIPTOR_NODE tail = NULL; + PSTRING_DESCRIPTOR_NODE trailing = NULL; + ULONG i = 0; + + // Go to the end of the linked list, searching for the requested index to + // see if we've already retrieved it + for (tail = string_desc_node_head; tail != NULL; tail = tail->Next) { + if (tail->DescriptorIndex == descriptor_index) { + // copy string descriptor to result + for(int i = 0; i < tail->StringDescriptor->bLength / 2 - 1; i++) { + result += tail->StringDescriptor->bString[i]; + } + return S_OK; + } + trailing = tail; + } + tail = trailing; + + // Get the next String Descriptor. If this is NULL, then we're done (return) + // Otherwise, loop through all Language IDs + for (i = 0; (tail != NULL) && (i < num_language_IDs); i++) { + tail->Next = GetStringDescriptor(handle_hub_device, + connection_index, + descriptor_index, + language_IDs[i]); + tail = tail->Next; + } + + if (tail == NULL) { + return E_FAIL; + } else { + // copy string descriptor to result + for (int i = 0; i < tail->StringDescriptor->bLength / 2 - 1; i++) { + result += tail->StringDescriptor->bString[i]; + } + return S_OK; + } +} + +bool get_handle_from_devinst(DEVINST devinst, HANDLE& handle) +{ + // create path consisting of device id and guid + wchar_t device_id[MAX_PATH]; + CM_Get_Device_ID(devinst, device_id, MAX_PATH, 0); + + //convert device id string to device path - https://stackoverflow.com/a/32641140/981766 + std::wstring dev_id_wstr(device_id); + dev_id_wstr = std::regex_replace(dev_id_wstr, std::wregex(LR"(\\)"), L"#"); // '\' is special for regex + dev_id_wstr = std::regex_replace(dev_id_wstr, std::wregex(L"^"), LR"(\\?\)", std::regex_constants::format_first_only); + dev_id_wstr = std::regex_replace(dev_id_wstr, std::wregex(L"$"), L"#", std::regex_constants::format_first_only); + + // guid + wchar_t guid_wchar[64];//guid is 32 chars+4 hyphens+2 paranthesis+null => 64 should be more than enough + StringFromGUID2(GUID_DEVINTERFACE_USB_HUB, guid_wchar, 64); + dev_id_wstr.append(guid_wchar); + + // get handle + std::wstring& usb_hub_path = dev_id_wstr; + handle = CreateFileW(usb_hub_path.c_str(), GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (handle == INVALID_HANDLE_VALUE) { + // Sometimes device is not GUID_DEVINTERFACE_USB_HUB, than we need to check parent recursively + DEVINST parent_devinst = 0; + if (CM_Get_Parent(&parent_devinst, devinst, 0) != CR_SUCCESS) + return false; + return get_handle_from_devinst(parent_devinst, handle); + } + return true; +} + +// Read Configuration Descriptor - configuration string indexed by iConfiguration and decide if card reader +bool is_card_reader(HDEVINFO h_dev_info, SP_DEVINFO_DATA& spdd) +{ + // First get port number of device. + + DEVINST parent_devinst = 0; + HANDLE handle; // usb hub handle + DWORD usb_port_number = 0; + DWORD required_size = 0; + // First we need handle for GUID_DEVINTERFACE_USB_HUB device. + if (CM_Get_Parent(&parent_devinst, spdd.DevInst, 0) != CR_SUCCESS) { + BOOST_LOG_TRIVIAL(warning) << "is_card_reader failed: Couldn't get parent DEVINST."; + return false; + } + if(!get_handle_from_devinst(parent_devinst, handle) || handle == INVALID_HANDLE_VALUE) { + BOOST_LOG_TRIVIAL(warning) << "is_card_reader failed: Couldn't get HANDLE for parent DEVINST."; + return false; + } + // Get port number to which the usb device is attached on the hub. + if (SetupDiGetDeviceRegistryProperty(h_dev_info, &spdd, SPDRP_ADDRESS, nullptr, (PBYTE)&usb_port_number, sizeof(usb_port_number), &required_size) == 0) { + BOOST_LOG_TRIVIAL(warning) << "is_card_reader failed: Couldn't get port number."; + return false; + } + + // Fill USB request packet to get iConfiguration value. + + int buffer_size = sizeof(USB_DESCRIPTOR_REQUEST) + sizeof(USB_CONFIGURATION_DESCRIPTOR); + BYTE* buffer = new BYTE[buffer_size]; + USB_DESCRIPTOR_REQUEST* request_packet = (USB_DESCRIPTOR_REQUEST*)buffer; + USB_CONFIGURATION_DESCRIPTOR* configuration_descriptor = (USB_CONFIGURATION_DESCRIPTOR*)((BYTE*)buffer + sizeof(USB_DESCRIPTOR_REQUEST)); + DWORD bytes_returned = 0; + // Fill information in packet. + request_packet->SetupPacket.bmRequest = 0x80; + request_packet->SetupPacket.bRequest = USB_REQUEST_GET_CONFIGURATION; + request_packet->ConnectionIndex = usb_port_number; + request_packet->SetupPacket.wValue = (USB_CONFIGURATION_DESCRIPTOR_TYPE << 8 | 0 /*Since only 1 device descriptor => index : 0*/); + request_packet->SetupPacket.wLength = sizeof(USB_CONFIGURATION_DESCRIPTOR); + // Issue ioctl. + if (DeviceIoControl(handle, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, buffer, buffer_size, buffer, buffer_size, &bytes_returned, nullptr) == 0) { + BOOST_LOG_TRIVIAL(warning) << "is_card_reader failed: Couldn't get Configuration Descriptor."; + return false; + } + // Nothing to read. + if (configuration_descriptor->iConfiguration == 0) { + BOOST_LOG_TRIVIAL(warning) << "is_card_reader failed: iConfiguration value is 0."; + return false; + } + + // Get string descriptor and read string on address given by iConfiguration index . + // Based at https://github.com/microsoft/Windows-driver-samples/tree/main/usb/usbview + + PSTRING_DESCRIPTOR_NODE supported_languages_string = NULL; + ULONG num_language_IDs = 0; + USHORT* language_IDs = NULL; + std::wstring configuration_string; + // Get languages. + supported_languages_string = GetStringDescriptor(handle, usb_port_number, 0, 0); + if (supported_languages_string == NULL) { + BOOST_LOG_TRIVIAL(warning) << "is_card_reader failed: Couldn't get language string descriptor."; + return false; + } + num_language_IDs = (supported_languages_string->StringDescriptor->bLength - 2) / 2; + language_IDs = (USHORT*)&supported_languages_string->StringDescriptor->bString[0]; + // Get configration string. + if (GetStringDescriptors(handle, usb_port_number, configuration_descriptor->iConfiguration, num_language_IDs, language_IDs, supported_languages_string, configuration_string) == E_FAIL) { + BOOST_LOG_TRIVIAL(warning) << "is_card_reader failed: Couldn't get configuration string descriptor."; + return false; + } + + // Final compare. + BOOST_LOG_TRIVIAL(error) << "Ejecting information: Retrieved configuration string: " << configuration_string; + if (configuration_string.find(L"CARD READER") != std::wstring::npos) { + BOOST_LOG_TRIVIAL(info) << "Detected external reader."; + return true; + } + return false; +} + // returns the device instance handle of a storage volume or 0 on error // called from eject_inner, based on https://stackoverflow.com/a/58848961 DEVINST get_dev_inst_by_device_number(long device_number, UINT drive_type, WCHAR* dos_device_name) @@ -80,7 +366,7 @@ DEVINST get_dev_inst_by_device_number(long device_number, UINT drive_type, WCHAR bool is_floppy = (wcsstr(dos_device_name, L"\\Floppy") != NULL); // TODO: could be tested better? if (drive_type != DRIVE_REMOVABLE || is_floppy) { - BOOST_LOG_TRIVIAL(debug) << "get_dev_inst_by_device_number failed: Drive is not removable."; + BOOST_LOG_TRIVIAL(warning) << "get_dev_inst_by_device_number failed: Drive is not removable."; return 0; } @@ -89,7 +375,7 @@ DEVINST get_dev_inst_by_device_number(long device_number, UINT drive_type, WCHAR HDEVINFO h_dev_info = SetupDiGetClassDevs(guid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (h_dev_info == INVALID_HANDLE_VALUE) { - BOOST_LOG_TRIVIAL(debug) << "get_dev_inst_by_device_number failed: Invalid dev info handle."; + BOOST_LOG_TRIVIAL(warning) << "get_dev_inst_by_device_number failed: Invalid dev info handle."; return 0; } @@ -135,13 +421,16 @@ DEVINST get_dev_inst_by_device_number(long device_number, UINT drive_type, WCHAR if (device_number != (long)sdn.DeviceNumber) { continue; } - // this is the drive, return the device instance + + // check if is sd card reader - if yes, indicate by returning invalid value. + bool reader = is_card_reader(h_dev_info, spdd); + SetupDiDestroyDeviceInfoList(h_dev_info); - return spdd.DevInst; + return !reader ? spdd.DevInst : 0; } SetupDiDestroyDeviceInfoList(h_dev_info); - BOOST_LOG_TRIVIAL(debug) << "get_dev_inst_by_device_number failed: Enmurating couldn't find the drive."; + BOOST_LOG_TRIVIAL(warning) << "get_dev_inst_by_device_number failed: Enmurating couldn't find the drive."; return 0; } @@ -194,10 +483,9 @@ int eject_inner(const std::string& path) // get the device instance handle of the storage volume by means of a SetupDi enum and matching the device number DEVINST dev_inst = get_dev_inst_by_device_number(device_number, drive_type, dos_device_name); - if (dev_inst == 0) { - BOOST_LOG_TRIVIAL(error) << GUI::format("Ejecting of %1% has failed: Invalid device instance handle.", path); - return 1; + BOOST_LOG_TRIVIAL(error) << GUI::format("Ejecting of %1%: Invalid device instance handle. Going to try alternative ejecting method.", path); + return eject_alt(volume_access_path); } PNP_VETO_TYPE veto_type = PNP_VetoTypeUnknown; @@ -249,7 +537,7 @@ int eject_inner(const std::string& path) return 1; } -} +} // namespace // Called from UI therefore it blocks the UI thread. // It also blocks updates at the worker thread. // Win32 implementation. @@ -268,18 +556,21 @@ void RemovableDriveManager::eject_drive() if (it_drive_data != m_current_drives.end()) { if (!eject_inner(m_last_save_path)) { // success - assert(m_callback_evt_handler); - if (m_callback_evt_handler) - wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair< DriveData, bool >(std::move(*it_drive_data), true))); + BOOST_LOG_TRIVIAL(info) << "Ejecting has succeeded."; + assert(m_callback_evt_handler); + if (m_callback_evt_handler) + wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair< DriveData, bool >(std::move(*it_drive_data), true))); } else { // failed to eject // this should not happen, throwing exception might be the way here + BOOST_LOG_TRIVIAL(error) << "Ejecting has failed."; assert(m_callback_evt_handler); if (m_callback_evt_handler) wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair(*it_drive_data, false))); } } else { // drive not found in m_current_drives + BOOST_LOG_TRIVIAL(error) << "Ejecting has failed. Drive not found in m_current_drives."; assert(m_callback_evt_handler); if (m_callback_evt_handler) wxPostEvent(m_callback_evt_handler, RemovableDriveEjectEvent(EVT_REMOVABLE_DRIVE_EJECTED, std::pair({"",""}, false))); From 4cf8d671cb721143fe50649c5660c26db7331dfb Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Fri, 31 Mar 2023 13:36:35 +0200 Subject: [PATCH 11/13] Change imgui dialog in emboss to wxWidget dialog for better styling. --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 50 ++++++++++++------------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index bab667676..2dedc1b9b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -2203,32 +2203,47 @@ void GLGizmoEmboss::draw_delete_style_button() { bool is_last = m_style_manager.get_styles().size() == 1; bool can_delete = is_stored && !is_last; - std::string title = _u8L("Remove style"); - const char * popup_id = title.c_str(); - static size_t next_style_index = std::numeric_limits::max(); if (draw_button(m_icons, IconType::erase, !can_delete)) { + std::string style_name = m_style_manager.get_style().name; // copy + wxString dialog_title = _L("Remove style"); + size_t next_style_index = std::numeric_limits::max(); + Plater *plater = wxGetApp().plater(); + bool exist_change = false; while (true) { // NOTE: can't use previous loaded activ index -> erase could change index size_t active_index = m_style_manager.get_style_index(); next_style_index = (active_index > 0) ? active_index - 1 : active_index + 1; + if (next_style_index >= m_style_manager.get_styles().size()) { - // can't remove last font style - // TODO: inform user + MessageDialog msg(plater, _L("Can't remove the last exising style."), dialog_title, wxICON_ERROR | wxOK); + msg.ShowModal(); break; } + // IMPROVE: add function can_load? // clean unactivable styles if (!m_style_manager.load_style(next_style_index)) { m_style_manager.erase(next_style_index); + exist_change = true; continue; } - // load back - m_style_manager.load_style(active_index); - ImGui::OpenPopup(popup_id); + wxString message = GUI::format_wxstr(_L("Are you sure,\nthat you want permanently and unrecoverable \nremove \"%1%\" style?"), style_name); + MessageDialog msg(plater, message, dialog_title, wxICON_WARNING | wxYES | wxNO); + if (msg.ShowModal() == wxID_YES) { + // delete style + m_style_manager.erase(active_index); + exist_change = true; + process(); + } else { + // load back style + m_style_manager.load_style(active_index); + } break; } + if (exist_change) + m_style_manager.store_styles_to_app_config(wxGetApp().app_config); } if (ImGui::IsItemHovered()) { @@ -2239,25 +2254,6 @@ void GLGizmoEmboss::draw_delete_style_button() { else/*if(!is_stored)*/ tooltip = GUI::format(_L("Can't delete temporary style \"%1%\"."), style_name); ImGui::SetTooltip("%s", tooltip.c_str()); } - - if (ImGui::BeginPopupModal(popup_id)) { - m_imgui->disable_background_fadeout_animation(); - const std::string &style_name = m_style_manager.get_style().name; - std::string text_in_popup = GUI::format(_L("Are you sure,\nthat you want permanently and unrecoverable \nremove style \"%1%\"?"), style_name); - ImGui::Text("%s", text_in_popup.c_str()); - if (ImGui::Button(_u8L("Yes").c_str())) { - size_t active_index = m_style_manager.get_style_index(); - m_style_manager.load_style(next_style_index); - m_style_manager.erase(active_index); - m_style_manager.store_styles_to_app_config(wxGetApp().app_config); - ImGui::CloseCurrentPopup(); - process(); - } - ImGui::SameLine(); - if (ImGui::Button(_u8L("No").c_str())) - ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); - } } // FIX IT: it should not change volume position before successfull change From 41026d47b6cfa92394034bf3836cc958f65c04fe Mon Sep 17 00:00:00 2001 From: YuSanka Date: Fri, 31 Mar 2023 14:31:22 +0200 Subject: [PATCH 12/13] EmbossGizmo: Fixed some phrases to improve localization --- src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp | 36 +++++++++++++------------ 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index 2dedc1b9b..31bd299e1 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -22,6 +22,7 @@ #include "libslic3r/NSVGUtils.hpp" #include "libslic3r/Model.hpp" +#include "libslic3r/Preset.hpp" #include "libslic3r/ClipperUtils.hpp" // union_ex #include "libslic3r/AppConfig.hpp" // store/load font list #include "libslic3r/Format/OBJ.hpp" // load obj file for default object @@ -1326,7 +1327,7 @@ void GLGizmoEmboss::draw_text_input() // show warning about incorrectness view of font std::string warning_tool_tip; if (!exist_font) { - warning_tool_tip = _u8L("Can't write text by selected font.Try to choose another font."); + warning_tool_tip = _u8L("The text cannot be written using the selected font. Please try choosing a different font."); } else { auto append_warning = [&warning_tool_tip](std::string t) { if (!warning_tool_tip.empty()) @@ -1335,15 +1336,15 @@ void GLGizmoEmboss::draw_text_input() }; if (priv::is_text_empty(m_text)) - append_warning(_u8L("Embossed text can NOT contain only white spaces.")); + append_warning(_u8L("Embossed text cannot contain only white spaces.")); if (m_text_contain_unknown_glyph) - append_warning(_u8L("Text contain character glyph (represented by '?') unknown by font.")); + append_warning(_u8L("Text contains character glyph (represented by '?') unknown by font.")); const FontProp &prop = m_style_manager.get_font_prop(); - if (prop.skew.has_value()) append_warning(_u8L("Text input do not show font skew.")); - if (prop.boldness.has_value()) append_warning(_u8L("Text input do not show font boldness.")); + if (prop.skew.has_value()) append_warning(_u8L("Text input doesn't show font skew.")); + if (prop.boldness.has_value()) append_warning(_u8L("Text input doesn't show font boldness.")); if (prop.line_gap.has_value()) - append_warning(_u8L("Text input do not show gap between lines.")); + append_warning(_u8L("Text input doesn't show gap between lines.")); auto &ff = m_style_manager.get_font_file_with_cache(); float imgui_size = StyleManager::get_imgui_font_size(prop, *ff.font_file, scale); if (imgui_size > StyleManager::max_imgui_font_size) @@ -1959,7 +1960,7 @@ void GLGizmoEmboss::draw_font_list() void GLGizmoEmboss::draw_model_type() { bool is_last_solid_part = m_volume->is_the_only_one_part(); - std::string title = _u8L("Text is to object"); + std::string title = _u8L("Operation"); if (is_last_solid_part) { ImVec4 color{.5f, .5f, .5f, 1.f}; m_imgui->text_colored(color, title.c_str()); @@ -1973,14 +1974,15 @@ void GLGizmoEmboss::draw_model_type() ModelVolumeType part = ModelVolumeType::MODEL_PART; ModelVolumeType type = m_volume->type(); - if (ImGui::RadioButton(_u8L("Added").c_str(), type == part)) + //TRN EmbossOperation + if (ImGui::RadioButton(_u8L("Join").c_str(), type == part)) new_type = part; else if (ImGui::IsItemHovered()) ImGui::SetTooltip("%s", _u8L("Click to change text into object part.").c_str()); ImGui::SameLine(); std::string last_solid_part_hint = _u8L("You can't change a type of the last solid part of the object."); - if (ImGui::RadioButton(_u8L("Subtracted").c_str(), type == negative)) + if (ImGui::RadioButton(_CTX_utf8(L_CONTEXT("Cut", "EmbossOperation"), "EmbossOperation").c_str(), type == negative)) new_type = negative; else if (ImGui::IsItemHovered()) { if (is_last_solid_part) @@ -2187,7 +2189,7 @@ void GLGizmoEmboss::draw_style_add_button() }else if (only_add_style) { ImGui::SetTooltip("%s", _u8L("Add style to my list.").c_str()); } else { - ImGui::SetTooltip("%s", _u8L("Add as new named style.").c_str()); + ImGui::SetTooltip("%s", _u8L("Save as new style.").c_str()); } } @@ -2300,7 +2302,7 @@ void GLGizmoEmboss::draw_style_list() { trunc_name = ImGuiWrapper::trunc(current_name, max_style_name_width); } - std::string title = _u8L("Presets"); + std::string title = _u8L("Styles"); if (m_style_manager.exist_stored_style()) ImGui::Text("%s", title.c_str()); else @@ -2309,7 +2311,7 @@ void GLGizmoEmboss::draw_style_list() { ImGui::SetNextItemWidth(m_gui_cfg->input_width); auto add_text_modify = [&is_modified](const std::string& name) { if (!is_modified) return name; - return name + " (" + _u8L("modified") + ")"; + return name + Preset::suffix_modified(); }; std::optional selected_style_index; if (ImGui::BeginCombo("##style_selector", add_text_modify(trunc_name).c_str())) { @@ -2842,17 +2844,17 @@ void GLGizmoEmboss::draw_advanced() } m_imgui->disabled_end(); // !can_use_surface // TRN EmbossGizmo: font units - std::string units = _u8L("font points"); + std::string units = _u8L("points"); std::string units_fmt = "%.0f " + units; - // input gap between letters + // input gap between characters auto def_char_gap = stored_style ? &stored_style->prop.char_gap : nullptr; int half_ascent = font_info.ascent / 2; int min_char_gap = -half_ascent, max_char_gap = half_ascent; - if (rev_slider(tr.char_gap, font_prop.char_gap, def_char_gap, _u8L("Revert gap between letters"), - min_char_gap, max_char_gap, units_fmt, _L("Distance between letters"))){ + if (rev_slider(tr.char_gap, font_prop.char_gap, def_char_gap, _u8L("Revert gap between characters"), + min_char_gap, max_char_gap, units_fmt, _L("Distance between characters"))){ // Condition prevent recalculation when insertint out of limits value by imgui input if (!priv::Limits::apply(font_prop.char_gap, priv::limits.char_gap) || !m_volume->text_configuration->style.prop.char_gap.has_value() || @@ -2914,7 +2916,7 @@ void GLGizmoEmboss::draw_advanced() m_imgui->disabled_begin(!allowe_surface_distance); const std::string undo_move_tooltip = _u8L("Undo translation"); - const wxString move_tooltip = _L("Distance of the center of text from model surface"); + const wxString move_tooltip = _L("Distance of the center of the text to the model surface."); bool is_moved = false; bool use_inch = wxGetApp().app_config->get_bool("use_inches"); if (use_inch) { From e1c253b29e1771c80b43110d45c9a1ece604b3ef Mon Sep 17 00:00:00 2001 From: David Kocik Date: Fri, 31 Mar 2023 15:42:13 +0200 Subject: [PATCH 13/13] fix of obsolete text --- src/slic3r/GUI/Downloader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/Downloader.cpp b/src/slic3r/GUI/Downloader.cpp index 3d2a00106..ebf275b8f 100644 --- a/src/slic3r/GUI/Downloader.cpp +++ b/src/slic3r/GUI/Downloader.cpp @@ -147,7 +147,7 @@ void Downloader::start_download(const std::string& full_url) std::string escaped_url = FileGet::escape_url(full_url.substr(24)); #endif if (!boost::starts_with(escaped_url, "https://") || !FileGet::is_subdomain(escaped_url, "printables.com")) { - std::string msg = format(_L("Download won't start. Download URL doesn't point to https://files.printables.com : %1%"), escaped_url); + std::string msg = format(_L("Download won't start. Download URL doesn't point to https://printables.com : %1%"), escaped_url); BOOST_LOG_TRIVIAL(error) << msg; NotificationManager* ntf_mngr = wxGetApp().notification_manager(); ntf_mngr->push_notification(NotificationType::CustomNotification, NotificationManager::NotificationLevel::RegularNotificationLevel, msg);