From 82182ac8b81b54dd6ac6f900aefed2f446206b12 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 16 Oct 2023 11:44:08 +0200 Subject: [PATCH] Propagate warnings about unhealed shape to UI. --- src/libslic3r/Emboss.cpp | 40 +++--- src/libslic3r/Emboss.hpp | 15 ++- src/libslic3r/EmbossShape.hpp | 9 +- src/libslic3r/Format/3mf.cpp | 10 +- src/libslic3r/NSVGUtils.cpp | 18 +-- src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp | 125 +++++++++++++++--- .../GUI/Jobs/CreateFontNameImageJob.cpp | 2 +- src/slic3r/GUI/Jobs/EmbossJob.cpp | 10 +- 8 files changed, 177 insertions(+), 52 deletions(-) diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index bd752549e7..602f408f50 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -30,6 +30,7 @@ const double ASCENT_CENTER = 1/3.; // 0.5 is above small letter // every glyph's shape point is divided by SHAPE_SCALE - increase precission of fixed point value // stored in fonts (to be able represents curve by sequence of lines) static constexpr double SHAPE_SCALE = 0.001; // SCALING_FACTOR promile is fine enough +static unsigned MAX_HEAL_ITERATION_OF_TEXT = 10; using namespace Slic3r; using namespace Emboss; @@ -432,7 +433,7 @@ bool Emboss::divide_segments_for_close_point(ExPolygons &expolygons, double dist return true; } -std::pair Emboss::heal_polygons(const Polygons &shape, bool is_non_zero, unsigned int max_iteration) +HealedExPolygons Emboss::heal_polygons(const Polygons &shape, bool is_non_zero, unsigned int max_iteration) { const double clean_distance = 1.415; // little grater than sqrt(2) ClipperLib::PolyFillType fill_type = is_non_zero ? @@ -621,7 +622,7 @@ bool heal_dupl_inter(ExPolygons &shape, unsigned max_iteration) if (fill_trouble_holes(holes, duplicate_points, intersection_points, shape)) { holes.clear(); continue; - } + } holes.clear(); holes.reserve(intersections.size() + duplicate_points.size()); @@ -785,7 +786,7 @@ std::optional get_glyph(const stbtt_fontinfo &font_info, int unicode_lett // https://docs.microsoft.com/en-us/typography/opentype/spec/ttch01 // https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html bool is_non_zero = true; - glyph.shape = Emboss::heal_polygons(glyph_polygons, is_non_zero, max_iteration).first; + glyph.shape = Emboss::heal_polygons(glyph_polygons, is_non_zero, max_iteration); } return glyph; } @@ -1279,7 +1280,7 @@ const int CANCEL_CHECK = 10; } // namespace /// Union shape defined by glyphs -ExPolygons Slic3r::union_ex(const ExPolygonsWithIds &shapes) +HealedExPolygons Slic3r::union_ex(const ExPolygonsWithIds &shapes, unsigned max_heal_iteration) { // unify to one expolygon ExPolygons result; @@ -1289,11 +1290,12 @@ ExPolygons Slic3r::union_ex(const ExPolygonsWithIds &shapes) expolygons_append(result, shape.expoly); } result = union_ex(result); - bool is_healed = heal_expolygons(result); - return result; + + bool is_healed = heal_expolygons(result, max_heal_iteration); + return {result, is_healed}; } -ExPolygons Slic3r::union_with_delta(const ExPolygonsWithIds &shapes, float delta) +HealedExPolygons Slic3r::union_with_delta(const ExPolygonsWithIds &shapes, float delta, unsigned max_heal_iteration) { // unify to one expolygons ExPolygons expolygons; @@ -1303,22 +1305,22 @@ ExPolygons Slic3r::union_with_delta(const ExPolygonsWithIds &shapes, float delta expolygons_append(expolygons, offset_ex(shape.expoly, delta)); } ExPolygons result = union_ex(expolygons); - result = offset_ex(result, -delta); - bool is_healed = heal_expolygons(result); - return result; + result = offset_ex(result, -delta); + bool is_healed = heal_expolygons(result, max_heal_iteration); + return {result, is_healed}; } -void Slic3r::translate(ExPolygonsWithIds &e, const Point &p) +void Slic3r::translate(ExPolygonsWithIds &expolygons_with_ids, const Point &p) { - for (auto &[id, expoly] : e) - translate(expoly, p); + for (ExPolygonsWithId &expolygons_with_id : expolygons_with_ids) + translate(expolygons_with_id.expoly, p); } -BoundingBox Slic3r::get_extents(const ExPolygonsWithIds &e) +BoundingBox Slic3r::get_extents(const ExPolygonsWithIds &expolygons_with_ids) { BoundingBox bb; - for (auto &[id, expoly] : e) - bb.merge(get_extents(expoly)); + for (const ExPolygonsWithId &expolygons_with_id : expolygons_with_ids) + bb.merge(get_extents(expolygons_with_id.expoly)); return bb; } @@ -1328,11 +1330,13 @@ void Slic3r::center(ExPolygonsWithIds &e) translate(e, -bb.center()); } -ExPolygons Emboss::text2shapes(FontFileWithCache &font_with_cache, const char *text, const FontProp &font_prop, const std::function& was_canceled) +HealedExPolygons Emboss::text2shapes(FontFileWithCache &font_with_cache, const char *text, const FontProp &font_prop, const std::function& was_canceled) { std::wstring text_w = boost::nowide::widen(text); ExPolygonsWithIds vshapes = text2vshapes(font_with_cache, text_w, font_prop, was_canceled); - return union_ex(vshapes); + + float delta = static_cast(1. / SHAPE_SCALE); + return union_with_delta(vshapes, delta, MAX_HEAL_ITERATION_OF_TEXT); } namespace { diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp index 91317449b5..a498b60afb 100644 --- a/src/libslic3r/Emboss.hpp +++ b/src/libslic3r/Emboss.hpp @@ -18,6 +18,13 @@ namespace Slic3r { +// Extend expolygons with information whether it was successfull healed +struct HealedExPolygons{ + ExPolygons expolygons; + bool is_healed; + operator ExPolygons&() { return expolygons; } +}; + /// /// class with only static function add ability to engraved OR raised /// text OR polygons onto model surface @@ -153,7 +160,7 @@ namespace Emboss /// User defined property of the font /// Way to interupt processing /// Inner polygon cw(outer ccw) - ExPolygons text2shapes (FontFileWithCache &font, const char *text, const FontProp &font_prop, const std::function &was_canceled = []() {return false;}); + HealedExPolygons text2shapes (FontFileWithCache &font, const char *text, const FontProp &font_prop, const std::function &was_canceled = []() {return false;}); ExPolygonsWithIds text2vshapes(FontFileWithCache &font, const std::wstring& text, const FontProp &font_prop, const std::function& was_canceled = []() {return false;}); const unsigned ENTER_UNICODE = static_cast('\n'); @@ -169,7 +176,7 @@ namespace Emboss /// Fill type ClipperLib::pftNonZero for overlapping otherwise /// Look at heal_expolygon()::max_iteration /// Healed shapes with flag is fully healed - std::pair heal_polygons(const Polygons &shape, bool is_non_zero = true, unsigned max_iteration = 10); + HealedExPolygons heal_polygons(const Polygons &shape, bool is_non_zero = true, unsigned max_iteration = 10); /// /// NOTE: call Slic3r::union_ex before this call @@ -467,9 +474,9 @@ namespace Emboss void translate(ExPolygonsWithIds &e, const Point &p); BoundingBox get_extents(const ExPolygonsWithIds &e); void center(ExPolygonsWithIds &e); -ExPolygons union_ex(const ExPolygonsWithIds &shapes); +HealedExPolygons union_ex(const ExPolygonsWithIds &shapes, unsigned max_heal_iteration); // delta .. safe offset before union (use as boolean close) // NOTE: remove unprintable spaces between neighbor curves (made by linearization of curve) -ExPolygons union_with_delta(const ExPolygonsWithIds &shapes, float delta); +HealedExPolygons union_with_delta(const ExPolygonsWithIds &shapes, float delta, unsigned max_heal_iteration); } // namespace Slic3r #endif // slic3r_Emboss_hpp_ diff --git a/src/libslic3r/EmbossShape.hpp b/src/libslic3r/EmbossShape.hpp index 72693c6675..1c7fdfe795 100644 --- a/src/libslic3r/EmbossShape.hpp +++ b/src/libslic3r/EmbossShape.hpp @@ -61,6 +61,9 @@ struct ExPolygonsWithId // shape defined by integer point contain only lines // Curves are converted to sequence of lines ExPolygons expoly; + + // flag whether expolygons are fully healed(without duplication) + bool is_healed = true; }; using ExPolygonsWithIds = std::vector; @@ -103,7 +106,11 @@ struct EmbossShape std::shared_ptr file_data = nullptr; }; SvgFile svg_file; - + + // flag whether during cration of union expolygon final shape was fully correct + // correct mean without selfintersection and duplicate(double) points + bool is_healed = true; + // undo / redo stack recovery template void save(Archive &ar) const { diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index a6377f67cf..998218a2f9 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -181,6 +181,7 @@ static constexpr const char *FONT_WEIGHT_ATTR = "weight"; // Store / load of EmbossShape static constexpr const char *SHAPE_TAG = "slic3rpe:shape"; static constexpr const char *SHAPE_SCALE_ATTR = "scale"; +static constexpr const char *UNHEALED_ATTR = "unhealed"; static constexpr const char *SVG_FILE_PATH_ATTR = "filepath"; static constexpr const char *SVG_FILE_PATH_IN_3MF_ATTR = "filepath3mf"; @@ -3855,12 +3856,15 @@ void to_xml(std::stringstream &stream, const EmbossShape &es, const ModelVolume stream << SHAPE_SCALE_ATTR << "=\"" << es.scale << "\" "; + if (!es.is_healed) + stream << UNHEALED_ATTR << "=\"" << 1 << "\" "; + // projection const EmbossProjection &p = es.projection; stream << DEPTH_ATTR << "=\"" << p.depth << "\" "; if (p.use_surface) stream << USE_SURFACE_ATTR << "=\"" << 1 << "\" "; - + // FIX of baked transformation Transform3d fix = create_fix(es.fix_3mf_tr, volume); stream << TRANSFORM_ATTR << "=\""; @@ -3872,6 +3876,8 @@ void to_xml(std::stringstream &stream, const EmbossShape &es, const ModelVolume std::optional read_emboss_shape(const char **attributes, unsigned int num_attributes) { double scale = get_attribute_value_float(attributes, num_attributes, SHAPE_SCALE_ATTR); + int unhealed = get_attribute_value_int(attributes, num_attributes, UNHEALED_ATTR); + bool is_healed = unhealed != 1; EmbossProjection projection; projection.depth = get_attribute_value_float(attributes, num_attributes, DEPTH_ATTR); @@ -3893,7 +3899,7 @@ std::optional read_emboss_shape(const char **attributes, unsigned i ExPolygonsWithIds shapes; // TODO: need to implement EmbossShape::SvgFile svg{file_path, file_path_3mf}; - return EmbossShape{shapes, scale, std::move(projection), std::move(fix_tr_mat), std::move(svg)}; + return EmbossShape{shapes, scale, std::move(projection), std::move(fix_tr_mat), std::move(svg), is_healed}; } diff --git a/src/libslic3r/NSVGUtils.cpp b/src/libslic3r/NSVGUtils.cpp index 69922d2d36..6e486d9a15 100644 --- a/src/libslic3r/NSVGUtils.cpp +++ b/src/libslic3r/NSVGUtils.cpp @@ -18,8 +18,8 @@ struct LinesPath{ Polygons polygons; Polylines polylines; }; LinesPath linearize_path(NSVGpath *first_path, const NSVGLineParams ¶m); -ExPolygons fill_to_expolygons(const LinesPath &lines_path, const NSVGshape &shape, const NSVGLineParams ¶m); -ExPolygons stroke_to_expolygons(const LinesPath &lines_path, const NSVGshape &shape, const NSVGLineParams ¶m); +HealedExPolygons fill_to_expolygons(const LinesPath &lines_path, const NSVGshape &shape, const NSVGLineParams ¶m); +HealedExPolygons stroke_to_expolygons(const LinesPath &lines_path, const NSVGshape &shape, const NSVGLineParams ¶m); } // namespace namespace Slic3r { @@ -45,11 +45,13 @@ ExPolygonsWithIds create_shape_with_ids(const NSVGimage &image, const NSVGLinePa if (is_fill_used) { unsigned unique_id = static_cast(2 * shape_id); - result.push_back({unique_id, fill_to_expolygons(lines_path, shape, param)}); + HealedExPolygons expoly = fill_to_expolygons(lines_path, shape, param); + result.push_back({unique_id, expoly.expolygons, expoly.is_healed}); } if (is_stroke_used) { unsigned unique_id = static_cast(2 * shape_id + 1); - result.push_back({unique_id, stroke_to_expolygons(lines_path, shape, param)}); + HealedExPolygons expoly = stroke_to_expolygons(lines_path, shape, param); + result.push_back({unique_id, expoly.expolygons, expoly.is_healed}); } } @@ -352,7 +354,7 @@ LinesPath linearize_path(NSVGpath *first_path, const NSVGLineParams ¶m) return result; } -ExPolygons fill_to_expolygons(const LinesPath &lines_path, const NSVGshape &shape, const NSVGLineParams ¶m) +HealedExPolygons fill_to_expolygons(const LinesPath &lines_path, const NSVGshape &shape, const NSVGLineParams ¶m) { Polygons fill = lines_path.polygons; // copy @@ -366,7 +368,7 @@ ExPolygons fill_to_expolygons(const LinesPath &lines_path, const NSVGshape &shap if (shape.fillRule == NSVGfillRule::NSVG_FILLRULE_EVENODD) is_non_zero = false; - return Emboss::heal_polygons(fill, is_non_zero, param.max_heal_iteration).first; + return Emboss::heal_polygons(fill, is_non_zero, param.max_heal_iteration); } struct DashesParam{ @@ -475,7 +477,7 @@ Polylines to_dashes(const Polyline &polyline, const DashesParam& param) return dashes; } -ExPolygons stroke_to_expolygons(const LinesPath &lines_path, const NSVGshape &shape, const NSVGLineParams ¶m) +HealedExPolygons stroke_to_expolygons(const LinesPath &lines_path, const NSVGshape &shape, const NSVGLineParams ¶m) { // convert stroke to polygon ClipperLib::JoinType join_type = ClipperLib::JoinType::jtSquare; @@ -515,7 +517,7 @@ ExPolygons stroke_to_expolygons(const LinesPath &lines_path, const NSVGshape &sh } bool is_non_zero = true; - return Emboss::heal_polygons(result, is_non_zero, param.max_heal_iteration).first; + return Emboss::heal_polygons(result, is_non_zero, param.max_heal_iteration); } } // namespace \ No newline at end of file diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp index 59fb13cbac..2157a8a53d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp @@ -777,8 +777,57 @@ void wu_draw_line(Linef line, } } +template // N .. count of channels per pixel +void draw_side_outline(const ExPolygons &shape, const std::array &color, std::vector &data, size_t data_width, double scale) +{ + int count_lines = data.size() / (N * data_width); + size_t data_line = N * data_width; + auto get_offset = [count_lines, data_line](int x, int y) { + // NOTE: y has opposit direction in texture + return (count_lines - y - 1) * data_line + x * N; + }; + + // overlap color + auto draw = [&data, data_width, count_lines, get_offset, &color](int x, int y, float brightess) { + if (x < 0 || y < 0 || x >= data_width || y >= count_lines) + return; // out of image + size_t offset = get_offset(x, y); + bool change_color = false; + for (size_t i = 0; i < N - 1; ++i) { + if(data[offset + i] != color[i]){ + data[offset + i] = color[i]; + change_color = true; + } + } + + unsigned char &alpha = data[offset + N - 1]; + if (alpha == 0 || change_color){ + alpha = static_cast(std::round(brightess * 255)); + } else if (alpha != 255){ + alpha = static_cast(std::min(255, int(alpha) + static_cast(std::round(brightess * 255)))); + } + }; + + BoundingBox bb_unscaled = get_extents(shape); + Linesf lines = to_linesf(shape); + BoundingBoxf bb(bb_unscaled.min.cast(), bb_unscaled.max.cast()); + + // scale lines to pixels + if (!is_approx(scale, 1.)) { + for (Linef &line : lines) { + line.a *= scale; + line.b *= scale; + } + bb.min *= scale; + bb.max *= scale; + } + + for (const Linef &line : lines) + wu_draw_line_side(line, draw); +} + /// -/// Draw filled ExPolygon into data +/// Draw filled ExPolygon into data /// line by line inspired by: http://alienryderflex.com/polygon_fill/ /// /// Count channels for one pixel(RGBA = 4) @@ -809,7 +858,6 @@ void draw_filled(const ExPolygons &shape, const std::array& co bb.min *= scale; bb.max *= scale; } - auto tree = Slic3r::AABBTreeLines::build_aabb_tree_over_indexed_lines(lines); int count_lines = data.size() / (N * data_width); size_t data_line = N * data_width; @@ -839,8 +887,11 @@ void draw_filled(const ExPolygons &shape, const std::array& co alpha = static_cast(std::min(255, int(alpha) + static_cast(std::round(brightess * 255)))); } }; - for (const Linef& line: lines) wu_draw_line_side(line, draw); + + for (const Linef& line: lines) + wu_draw_line_side(line, draw); + auto tree = Slic3r::AABBTreeLines::build_aabb_tree_over_indexed_lines(lines); // range for intersection line double x1 = bb.min.x() - 1.f; @@ -883,7 +934,7 @@ void draw_filled(const ExPolygons &shape, const std::array& co } // init texture by draw expolygons into texture -bool init_texture(Texture &texture, const ExPolygonsWithIds& shapes_with_ids, unsigned max_size_px){ +bool init_texture(Texture &texture, const ExPolygonsWithIds& shapes_with_ids, unsigned max_size_px, const std::vector& shape_warnings){ BoundingBox bb = get_extents(shapes_with_ids); Point bb_size = bb.size(); double bb_width = bb_size.x(); // [in mm] @@ -914,12 +965,35 @@ bool init_texture(Texture &texture, const ExPolygonsWithIds& shapes_with_ids, un shape = union_ex(shape); // align to texture - for (ExPolygon& expolygon: shape) - expolygon.translate(-bb.min); - + translate(shape, -bb.min); + size_t texture_width = static_cast(texture.width); unsigned char alpha = 255; // without transparency - std::array color{201, 201, 201, alpha}; - draw_filled<4>(shape, color, data, (size_t)texture.width, scale); + std::array color_shape{201, 201, 201, alpha}; // from degin by @JosefZachar + std::array color_error{237, 28, 36, alpha}; // from icon: resources/icons/flag_red.svg + std::array color_warning{237, 107, 33, alpha}; // icons orange + // draw unhealedable shape + for (const ExPolygonsWithId &shapes_with_id : shapes_with_ids) + if (!shapes_with_id.is_healed) { + ExPolygons bad_shape = shapes_with_id.expoly; // copy + translate(bad_shape, -bb.min); // align to texture + draw_side_outline<4>(bad_shape, color_error, data, texture_width, scale); + } + // Draw shape with warning + if (!shape_warnings.empty()) { + for (const ExPolygonsWithId &shapes_with_id : shapes_with_ids){ + assert(shapes_with_id.id < shape_warnings.size()); + if (shapes_with_id.id >= shape_warnings.size()) + continue; + if (shape_warnings[shapes_with_id.id].empty()) + continue; // no warnings for shape + ExPolygons warn_shape = shapes_with_id.expoly; // copy + translate(warn_shape, -bb.min); // align to texture + draw_side_outline<4>(warn_shape, color_warning, data, texture_width, scale); + } + } + + // Draw rest of shape + draw_filled<4>(shape, color_shape, data, texture_width, scale); // sends data to gpu glsafe(::glPixelStorei(GL_UNPACK_ALIGNMENT, 1)); @@ -1027,14 +1101,33 @@ std::string create_stroke_warning(const NSVGshape &shape) { /// Input svg loaded to shapes /// Vector of warnings with same size as EmbossShape::shapes_with_ids /// or Empty when no warnings -> for fast checking that every thing is all right(more common case) -std::vector create_shape_warnings(const NSVGimage &image, float scale){ +std::vector create_shape_warnings(const EmbossShape &shape, float scale){ + assert(shape.svg_file.image != nullptr); + if (shape.svg_file.image == nullptr) + return {std::string{"Uninitialized SVG image"}}; + + const NSVGimage &image = *shape.svg_file.image; std::vector result; auto add_warning = [&result, &image](size_t index, const std::string &message) { if (result.empty()) result = std::vector(get_shapes_count(image) * 2); - result[index] = message; + std::string &res = result[index]; + if (res.empty()) + res = message; + else + res += '\n' + message; }; + if (!shape.is_healed) { + for (const ExPolygonsWithId &i : shape.shapes_with_ids) + if (!i.is_healed) + add_warning(i.id, _u8L("Path can't be healed from selfintersection and multiple points.")); + + // This waning is not connected to NSVGshape. It is about union of paths, but Zero index is shown first + size_t index = 0; + add_warning(index, _u8L("Final shape constains selfintersection or multiple points with same coordinate")); + } + size_t shape_index = 0; for (NSVGshape *shape = image.shapes; shape != NULL; shape = shape->next, ++shape_index) { if (!(shape->flags & NSVG_FLAGS_VISIBLE)){ @@ -1114,8 +1207,8 @@ void GLGizmoSVG::set_volume_by_selection() m_volume = volume; m_volume_id = volume->id(); - m_volume_shape = *volume->emboss_shape; // copy - m_shape_warnings = create_shape_warnings(image, get_scale_for_tolerance()); + m_volume_shape = es; // copy + m_shape_warnings = create_shape_warnings(es, get_scale_for_tolerance()); // Calculate current angle of up vector m_angle = calculate_angle(selection); @@ -1303,7 +1396,7 @@ void GLGizmoSVG::draw_preview(){ // drag&drop is out of rendering scope so texture must be created on this place if (m_texture.id == 0) { const ExPolygonsWithIds &shapes = m_volume->emboss_shape->shapes_with_ids; - init_texture(m_texture, shapes, m_gui_cfg->texture_max_size_px); + init_texture(m_texture, shapes, m_gui_cfg->texture_max_size_px, m_shape_warnings); } //::draw(m_volume_shape.shapes_with_ids, m_gui_cfg->texture_max_size_px); @@ -1535,8 +1628,8 @@ void GLGizmoSVG::draw_filename(){ EmbossShape es_ = select_shape(m_volume_shape.svg_file.path, tes_tol); m_volume_shape.svg_file = std::move(es_.svg_file); m_volume_shape.shapes_with_ids = std::move(es_.shapes_with_ids); - m_shape_warnings = create_shape_warnings(*m_volume_shape.svg_file.image, scale); - init_texture(m_texture, m_volume_shape.shapes_with_ids, m_gui_cfg->texture_max_size_px); + m_shape_warnings = create_shape_warnings(m_volume_shape, scale); + init_texture(m_texture, m_volume_shape.shapes_with_ids, m_gui_cfg->texture_max_size_px, m_shape_warnings); process(); } } diff --git a/src/slic3r/GUI/Jobs/CreateFontNameImageJob.cpp b/src/slic3r/GUI/Jobs/CreateFontNameImageJob.cpp index cd0207cde8..3c080148f1 100644 --- a/src/slic3r/GUI/Jobs/CreateFontNameImageJob.cpp +++ b/src/slic3r/GUI/Jobs/CreateFontNameImageJob.cpp @@ -78,7 +78,7 @@ void CreateFontImageJob::process(Ctl &ctl) // normalize height of font BoundingBox bounding_box; - for (ExPolygon &shape : shapes) + for (const ExPolygon &shape : shapes) bounding_box.merge(BoundingBox(shape.contour.points)); if (bounding_box.size().x() < 1 || bounding_box.size().y() < 1) { m_input.cancel->store(true); diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index 1d28e518ce..1fcbb09ff7 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -791,9 +791,15 @@ bool check(const UpdateSurfaceVolumeData &input, bool is_main_thread) template ExPolygons create_shape(DataBase &input, Fnc was_canceled) { - const EmbossShape &es = input.create_shape(); + EmbossShape &es = input.create_shape(); float delta = 50.f; - return union_with_delta(es.shapes_with_ids, delta); + unsigned max_heal_iteration = 10; + HealedExPolygons result = union_with_delta(es.shapes_with_ids, delta, max_heal_iteration); + es.is_healed = result.is_healed; + for (const ExPolygonsWithId &e : es.shapes_with_ids) + if (!e.is_healed) + es.is_healed = false; + return result.expolygons; } //#define STORE_SAMPLING