From 73c9f939e07916350bb0a70eeb4fee28da7dad9d Mon Sep 17 00:00:00 2001 From: Vojtech Bubnik Date: Wed, 3 Feb 2021 15:12:53 +0100 Subject: Squash merge of lh_brim_rework, brim separated to Brim.cpp,hpp Refactored accessors to PrintObjectPtrs, PrintRegionPtrs, LayerPtrs, SupportLayerPtrs for const correctness. --- src/libslic3r/Brim.cpp | 490 ++++++++++++++++++++++++++++++++ src/libslic3r/Brim.hpp | 14 + src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/Config.hpp | 3 +- src/libslic3r/ExPolygon.cpp | 7 + src/libslic3r/ExPolygon.hpp | 19 ++ src/libslic3r/ExtrusionEntity.hpp | 19 ++ src/libslic3r/GCode.cpp | 2 +- src/libslic3r/GCode.hpp | 2 +- src/libslic3r/GCode/ToolOrdering.cpp | 2 +- src/libslic3r/PerimeterGenerator.cpp | 2 +- src/libslic3r/Polygon.cpp | 8 + src/libslic3r/Polygon.hpp | 1 + src/libslic3r/Polyline.hpp | 1 + src/libslic3r/Preset.cpp | 2 +- src/libslic3r/Print.cpp | 189 ++---------- src/libslic3r/Print.hpp | 84 ++++-- src/libslic3r/PrintBase.cpp | 5 + src/libslic3r/PrintBase.hpp | 18 ++ src/libslic3r/PrintConfig.cpp | 25 ++ src/libslic3r/PrintConfig.hpp | 26 +- src/libslic3r/PrintObject.cpp | 6 +- src/libslic3r/PrintRegion.cpp | 6 +- src/libslic3r/SupportMaterial.cpp | 70 +++-- src/libslic3r/SupportMaterial.hpp | 2 +- src/slic3r/GUI/ConfigManipulation.cpp | 4 +- src/slic3r/GUI/Field.cpp | 2 + src/slic3r/GUI/GLCanvas3D.cpp | 4 +- src/slic3r/GUI/GUI.cpp | 2 + src/slic3r/GUI/GUI_ObjectList.cpp | 4 +- src/slic3r/GUI/OptionsGroup.cpp | 3 + src/slic3r/GUI/Plater.cpp | 2 +- src/slic3r/GUI/Tab.cpp | 2 + src/slic3r/GUI/UnsavedChangesDialog.cpp | 2 + 34 files changed, 795 insertions(+), 235 deletions(-) create mode 100644 src/libslic3r/Brim.cpp create mode 100644 src/libslic3r/Brim.hpp (limited to 'src') diff --git a/src/libslic3r/Brim.cpp b/src/libslic3r/Brim.cpp new file mode 100644 index 000000000..0e677e8b7 --- /dev/null +++ b/src/libslic3r/Brim.cpp @@ -0,0 +1,490 @@ +#include "clipper/clipper_z.hpp" + +#include "ClipperUtils.hpp" +#include "EdgeGrid.hpp" +#include "Layer.hpp" +#include "Print.hpp" +#include "ShortestPath.hpp" +#include "libslic3r.h" + +#include +#include +#include + +namespace Slic3r { + +static void append_and_translate(ExPolygons &dst, const ExPolygons &src, const PrintInstance &instance) { + size_t dst_idx = dst.size(); + expolygons_append(dst, src); + for (; dst_idx < dst.size(); ++dst_idx) + dst[dst_idx].translate(instance.shift.x(), instance.shift.y()); +} + +static void append_and_translate(Polygons &dst, const Polygons &src, const PrintInstance &instance) { + size_t dst_idx = dst.size(); + polygons_append(dst, src); + for (; dst_idx < dst.size(); ++dst_idx) + dst[dst_idx].translate(instance.shift.x(), instance.shift.y()); +} + +static float max_brim_width(const ConstPrintObjectPtrsAdaptor &objects) +{ + assert(!objects.empty()); + return float(std::accumulate(objects.begin() + 1, objects.end(), objects.front()->config().brim_width.value, + [](double partial_result, const PrintObject *object) { + return std::max(partial_result, object->config().brim_width.value); + })); +} + +static ConstPrintObjectPtrs get_top_level_objects_with_brim(const Print &print) +{ + Polygons islands; + ConstPrintObjectPtrs island_to_object; + for (const PrintObject *object : print.objects()) { + Polygons islands_object; + for (const ExPolygon &ex_poly : object->layers().front()->lslices) + islands_object.emplace_back(ex_poly.contour); + + islands.reserve(islands.size() + object->instances().size() * islands_object.size()); + for (const PrintInstance &instance : object->instances()) + for (Polygon &poly : islands_object) { + islands.emplace_back(poly); + islands.back().translate(instance.shift); + island_to_object.emplace_back(object); + } + } + assert(islands.size() == island_to_object.size()); + + ClipperLib_Z::Paths islands_clip; + islands_clip.reserve(islands.size()); + for (const Polygon &poly : islands) { + islands_clip.emplace_back(); + ClipperLib_Z::Path &island_clip = islands_clip.back(); + island_clip.reserve(poly.points.size()); + int island_idx = int(&poly - &islands.front()); + // The Z coordinate carries index of the island used to get the pointer to the object. + for (const Point &pt : poly.points) + island_clip.emplace_back(pt.x(), pt.y(), island_idx + 1); + } + + // Init Clipper + ClipperLib_Z::Clipper clipper; + // Assign the maximum Z from four points. This values is valid index of the island + clipper.ZFillFunction([](const ClipperLib_Z::IntPoint &e1bot, const ClipperLib_Z::IntPoint &e1top, const ClipperLib_Z::IntPoint &e2bot, + const ClipperLib_Z::IntPoint &e2top, ClipperLib_Z::IntPoint &pt) { + pt.Z = std::max(std::max(e1bot.Z, e1top.Z), std::max(e2bot.Z, e2top.Z)); + }); + // Add islands + clipper.AddPaths(islands_clip, ClipperLib_Z::ptSubject, true); + // Execute union operation to construct polytree + ClipperLib_Z::PolyTree islands_polytree; + clipper.Execute(ClipperLib_Z::ctUnion, islands_polytree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd); + + std::unordered_set processed_objects_idx; + ConstPrintObjectPtrs top_level_objects_with_brim; + for (int i = 0; i < islands_polytree.ChildCount(); ++i) { + for (const ClipperLib_Z::IntPoint &point : islands_polytree.Childs[i]->Contour) { + if (point.Z != 0 && processed_objects_idx.find(island_to_object[point.Z - 1]->id().id) == processed_objects_idx.end()) { + top_level_objects_with_brim.emplace_back(island_to_object[point.Z - 1]); + processed_objects_idx.insert(island_to_object[point.Z - 1]->id().id); + } + } + } + return top_level_objects_with_brim; +} + +static Polygons top_level_outer_brim_islands(const ConstPrintObjectPtrs &top_level_objects_with_brim) +{ + Polygons islands; + for (const PrintObject *object : top_level_objects_with_brim) { + float brim_offset = float(scale_(object->config().brim_offset.value)); + Polygons islands_object; + for (const ExPolygon &ex_poly : object->layers().front()->lslices) { + Polygons contour_offset = offset(ex_poly.contour, brim_offset); + for (Polygon &poly : contour_offset) + poly.douglas_peucker(SCALED_RESOLUTION); + + polygons_append(islands_object, std::move(contour_offset)); + } + + for (const PrintInstance &instance : object->instances()) + append_and_translate(islands, islands_object, instance); + } + return islands; +} + +static ExPolygons top_level_outer_brim_area(const Print &print, const ConstPrintObjectPtrs &top_level_objects_with_brim, const float no_brim_offset) +{ + std::unordered_set top_level_objects_idx; + top_level_objects_idx.reserve(top_level_objects_with_brim.size()); + for (const PrintObject *object : top_level_objects_with_brim) + top_level_objects_idx.insert(object->id().id); + + ExPolygons brim_area; + Polygons no_brim_area; + for (const PrintObject *object : print.objects()) { + const BrimType brim_type = object->config().brim_type.value; + const float brim_offset = scale_(object->config().brim_offset.value); + const float brim_width = scale_(object->config().brim_width.value); + const bool is_top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end(); + + ExPolygons brim_area_object; + Polygons no_brim_area_object; + for (const ExPolygon &ex_poly : object->layers().front()->lslices) { + if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner) && is_top_outer_brim) + append(brim_area_object, diff_ex(offset_ex(ex_poly.contour, brim_width + brim_offset), offset_ex(ex_poly.contour, brim_offset))); + + if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim) + append(no_brim_area_object, offset(ex_poly.holes, -no_brim_offset)); + + if (brim_type != BrimType::btNoBrim) + append(no_brim_area_object, offset(ex_poly.contour, brim_offset)); + + no_brim_area_object.emplace_back(ex_poly.contour); + } + + for (const PrintInstance &instance : object->instances()) { + append_and_translate(brim_area, brim_area_object, instance); + append_and_translate(no_brim_area, no_brim_area_object, instance); + } + } + + return diff_ex(to_polygons(std::move(brim_area)), no_brim_area); +} + +static ExPolygons inner_brim_area(const Print &print, const ConstPrintObjectPtrs &top_level_objects_with_brim, const float no_brim_offset) +{ + std::unordered_set top_level_objects_idx; + top_level_objects_idx.reserve(top_level_objects_with_brim.size()); + for (const PrintObject *object : top_level_objects_with_brim) + top_level_objects_idx.insert(object->id().id); + + ExPolygons brim_area; + ExPolygons no_brim_area; + Polygons holes; + for (const PrintObject *object : print.objects()) { + const BrimType brim_type = object->config().brim_type.value; + const float brim_offset = scale_(object->config().brim_offset.value); + const float brim_width = scale_(object->config().brim_width.value); + const bool top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end(); + + ExPolygons brim_area_object; + ExPolygons no_brim_area_object; + Polygons holes_object; + for (const ExPolygon &ex_poly : object->layers().front()->lslices) { + if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner) { + if (top_outer_brim) + no_brim_area_object.emplace_back(ex_poly); + else + append(brim_area_object, diff_ex(offset_ex(ex_poly.contour, brim_width + brim_offset), offset_ex(ex_poly.contour, brim_offset))); + } + + if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner) + append(brim_area_object, diff_ex(offset_ex(ex_poly.holes, -brim_offset), offset_ex(ex_poly.holes, -brim_width - brim_offset))); + + if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim) + append(no_brim_area_object, offset_ex(ex_poly.contour, no_brim_offset)); + + if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim) + append(no_brim_area_object, offset_ex(ex_poly.holes, -no_brim_offset)); + + append(holes_object, ex_poly.holes); + } + append(no_brim_area_object, offset_ex(object->layers().front()->lslices, brim_offset)); + + for (const PrintInstance &instance : object->instances()) { + append_and_translate(brim_area, brim_area_object, instance); + append_and_translate(no_brim_area, no_brim_area_object, instance); + append_and_translate(holes, holes_object, instance); + } + } + + return diff_ex(intersection_ex(to_polygons(std::move(brim_area)), holes), no_brim_area); +} + +static void optimize_polylines_by_reversing(Polylines *polylines) +{ + for (size_t poly_idx = 1; poly_idx < polylines->size(); ++poly_idx) { + const Polyline &prev = (*polylines)[poly_idx - 1]; + Polyline & next = (*polylines)[poly_idx]; + + if (!next.is_closed()) { + double dist_to_start = (next.first_point() - prev.last_point()).cast().norm(); + double dist_to_end = (next.last_point() - prev.last_point()).cast().norm(); + + if (dist_to_end < dist_to_start) next.reverse(); + } + } +} + +static Polylines connect_brim_lines(Polylines &&polylines, const Polygons &brim_area, float max_connection_length) +{ + EdgeGrid::Grid grid; + BoundingBox bbox(get_extents(polylines)); + bbox.offset(SCALED_EPSILON); + grid.set_bbox(bbox); + + std::vector polylines_points(polylines.size() + brim_area.size()); + for (const Polyline &poly : polylines) + polylines_points[&poly - &polylines.front()] = poly.points; + for (const Polygon &poly : brim_area) + polylines_points.emplace_back(poly.points); + grid.create(polylines_points, coord_t(scale_(10.))); + + struct Visitor + { + explicit Visitor(const EdgeGrid::Grid &grid) : grid(grid) {} + + bool operator()(coord_t iy, coord_t ix) + { + // Called with a row and colum of the grid cell, which is intersected by a line. + auto cell_data_range = grid.cell_data_range(iy, ix); + this->intersect = false; + for (auto it_contour_and_segment = cell_data_range.first; it_contour_and_segment != cell_data_range.second; ++it_contour_and_segment) { + // End points of the line segment and their vector. + auto segment = grid.segment(*it_contour_and_segment); + if (Geometry::segments_intersect(segment.first, segment.second, brim_line.a, brim_line.b)) { + this->intersect = true; + return false; + } + } + // Continue traversing the grid along the edge. + return true; + } + + const EdgeGrid::Grid &grid; + Line brim_line; + bool intersect; + + } visitor(grid); + + Polyline *prev = &polylines.front(); + for (size_t poly_idx = 1; poly_idx < polylines.size(); ++poly_idx) { + Polyline &next = polylines[poly_idx]; + + double dist = Line(prev->last_point(), next.first_point()).length(); + if (dist <= max_connection_length) { + visitor.brim_line.a = prev->last_point(); + visitor.brim_line.b = next.first_point(); + visitor.brim_line.extend(-SCALED_EPSILON); + grid.visit_cells_intersecting_line(visitor.brim_line.a, visitor.brim_line.b, visitor); + if (!visitor.intersect) + append(prev->points, std::move(next.points)); + else + prev = &next; + } + } + + Polylines polylines_out; + polylines_out.reserve(std::count_if(polylines.begin(), polylines.end(), [](const Polyline &pl) { return !pl.empty(); })); + for (Polyline &pl : polylines) + if (!pl.empty()) + polylines_out.emplace_back(std::move(pl)); + + return polylines_out; +} + +static void make_inner_brim(const Print &print, const ConstPrintObjectPtrs &top_level_objects_with_brim, ExtrusionEntityCollection &brim) +{ + Flow flow = print.brim_flow(); + ExPolygons islands_ex = inner_brim_area(print, top_level_objects_with_brim, flow.scaled_spacing()); + ExPolygons loops_ex; + islands_ex = offset_ex(islands_ex, -0.5f * float(flow.scaled_spacing()), jtSquare); + for (size_t i = 0; !islands_ex.empty(); ++i) { + for (ExPolygon &poly_ex : islands_ex) + poly_ex.douglas_peucker(SCALED_RESOLUTION); + expolygons_append(loops_ex, islands_ex); + islands_ex = offset_ex(islands_ex, -float(flow.scaled_spacing()), jtSquare); + } + + Polygons loops = union_pt_chained_outside_in(loops, false); + std::reverse(loops.begin(), loops.end()); + extrusion_entities_append_loops(brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), + float(flow.width), float(print.skirt_first_layer_height())); +} + +// Produce brim lines around those objects, that have the brim enabled. +// Collect islands_area to be merged into the final 1st layer convex hull. +ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cancel, Polygons &islands_area) +{ + Flow flow = print.brim_flow(); + ConstPrintObjectPtrs top_level_objects_with_brim = get_top_level_objects_with_brim(print); + Polygons islands = top_level_outer_brim_islands(top_level_objects_with_brim); + ExPolygons islands_area_ex = top_level_outer_brim_area(print, top_level_objects_with_brim, flow.scaled_spacing()); + islands_area = to_polygons(islands_area_ex); + + Polygons loops; + size_t num_loops = size_t(floor(max_brim_width(print.objects()) / flow.spacing())); + for (size_t i = 0; i < num_loops; ++i) { + try_cancel(); + islands = offset(islands, float(flow.scaled_spacing()), jtSquare); + for (Polygon &poly : islands) + poly.douglas_peucker(SCALED_RESOLUTION); + polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing()))); + } + loops = union_pt_chained_outside_in(loops, false); + + std::vector loops_pl_by_levels; + { + Polylines loops_pl = to_polylines(loops); + loops_pl_by_levels.assign(loops_pl.size(), Polylines()); + tbb::parallel_for(tbb::blocked_range(0, loops_pl.size()), + [&loops_pl_by_levels, &loops_pl, &islands_area](const tbb::blocked_range &range) { + for (size_t i = range.begin(); i < range.end(); ++i) { + loops_pl_by_levels[i] = chain_polylines(intersection_pl({ std::move(loops_pl[i]) }, islands_area)); + } + }); + } + + // output + ExtrusionEntityCollection brim; + + // Reduce down to the ordered list of polylines. + Polylines all_loops; + for (Polylines &polylines : loops_pl_by_levels) + append(all_loops, std::move(polylines)); + loops_pl_by_levels.clear(); + + optimize_polylines_by_reversing(&all_loops); + all_loops = connect_brim_lines(std::move(all_loops), offset(islands_area_ex,SCALED_EPSILON), flow.scaled_spacing() * 2); + + const bool could_brim_intersects_skirt = std::any_of(print.objects().begin(), print.objects().end(), [&print](PrintObject *object) { + const BrimType &bt = object->config().brim_type; + return (bt == btOuterOnly || bt == btOuterAndInner) && print.config().skirt_distance.value < object->config().brim_width; + }); + // If there is a possibility that brim intersects skirt, go through loops and split those extrusions + // The result is either the original Polygon or a list of Polylines + if (! print.skirt().empty() && could_brim_intersects_skirt) + { + // Find the bounding polygons of the skirt + const Polygons skirt_inners = offset(dynamic_cast(print.skirt().entities.back())->polygon(), + -float(scale_(print.skirt_flow().spacing()))/2.f, + ClipperLib::jtRound, + float(scale_(0.1))); + const Polygons skirt_outers = offset(dynamic_cast(print.skirt().entities.front())->polygon(), + float(scale_(print.skirt_flow().spacing()))/2.f, + ClipperLib::jtRound, + float(scale_(0.1))); + + // First calculate the trimming region. + ClipperLib_Z::Paths trimming; + { + ClipperLib_Z::Paths input_subject; + ClipperLib_Z::Paths input_clip; + for (const Polygon &poly : skirt_outers) { + input_subject.emplace_back(); + ClipperLib_Z::Path &out = input_subject.back(); + out.reserve(poly.points.size()); + for (const Point &pt : poly.points) + out.emplace_back(pt.x(), pt.y(), 0); + } + for (const Polygon &poly : skirt_inners) { + input_clip.emplace_back(); + ClipperLib_Z::Path &out = input_clip.back(); + out.reserve(poly.points.size()); + for (const Point &pt : poly.points) + out.emplace_back(pt.x(), pt.y(), 0); + } + // init Clipper + ClipperLib_Z::Clipper clipper; + // add polygons + clipper.AddPaths(input_subject, ClipperLib_Z::ptSubject, true); + clipper.AddPaths(input_clip, ClipperLib_Z::ptClip, true); + // perform operation + clipper.Execute(ClipperLib_Z::ctDifference, trimming, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd); + } + + // Second, trim the extrusion loops with the trimming regions. + ClipperLib_Z::Paths loops_trimmed; + { + // Produce ClipperLib_Z::Paths from polylines (not necessarily closed). + ClipperLib_Z::Paths input_clip; + for (const Polyline &loop_pl : all_loops) { + input_clip.emplace_back(); + ClipperLib_Z::Path& out = input_clip.back(); + out.reserve(loop_pl.points.size()); + int64_t loop_idx = &loop_pl - &all_loops.front(); + for (const Point& pt : loop_pl.points) + // The Z coordinate carries index of the source loop. + out.emplace_back(pt.x(), pt.y(), loop_idx + 1); + } + // init Clipper + ClipperLib_Z::Clipper clipper; + clipper.ZFillFunction([](const ClipperLib_Z::IntPoint& e1bot, const ClipperLib_Z::IntPoint& e1top, const ClipperLib_Z::IntPoint& e2bot, const ClipperLib_Z::IntPoint& e2top, ClipperLib_Z::IntPoint& pt) { + // Assign a valid input loop identifier. Such an identifier is strictly positive, the next line is safe even in case one side of a segment + // hat the Z coordinate not set to the contour coordinate. + pt.Z = std::max(std::max(e1bot.Z, e1top.Z), std::max(e2bot.Z, e2top.Z)); + }); + // add polygons + clipper.AddPaths(input_clip, ClipperLib_Z::ptSubject, false); + clipper.AddPaths(trimming, ClipperLib_Z::ptClip, true); + // perform operation + ClipperLib_Z::PolyTree loops_trimmed_tree; + clipper.Execute(ClipperLib_Z::ctDifference, loops_trimmed_tree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd); + ClipperLib_Z::PolyTreeToPaths(loops_trimmed_tree, loops_trimmed); + } + + // Third, produce the extrusions, sorted by the source loop indices. + { + std::vector> loops_trimmed_order; + loops_trimmed_order.reserve(loops_trimmed.size()); + for (const ClipperLib_Z::Path &path : loops_trimmed) { + size_t input_idx = 0; + for (const ClipperLib_Z::IntPoint &pt : path) + if (pt.Z > 0) { + input_idx = (size_t)pt.Z; + break; + } + assert(input_idx != 0); + loops_trimmed_order.emplace_back(&path, input_idx); + } + std::stable_sort(loops_trimmed_order.begin(), loops_trimmed_order.end(), + [](const std::pair &l, const std::pair &r) { + return l.second < r.second; + }); + + Point last_pt(0, 0); + for (size_t i = 0; i < loops_trimmed_order.size();) { + // Find all pieces that the initial loop was split into. + size_t j = i + 1; + for (; j < loops_trimmed_order.size() && loops_trimmed_order[i].second == loops_trimmed_order[j].second; ++ j) ; + const ClipperLib_Z::Path &first_path = *loops_trimmed_order[i].first; + if (i + 1 == j && first_path.size() > 3 && first_path.front().X == first_path.back().X && first_path.front().Y == first_path.back().Y) { + auto *loop = new ExtrusionLoop(); + brim.entities.emplace_back(loop); + loop->paths.emplace_back(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(print.skirt_first_layer_height())); + Points &points = loop->paths.front().polyline.points; + points.reserve(first_path.size()); + for (const ClipperLib_Z::IntPoint &pt : first_path) + points.emplace_back(coord_t(pt.X), coord_t(pt.Y)); + i = j; + } else { + //FIXME The path chaining here may not be optimal. + ExtrusionEntityCollection this_loop_trimmed; + this_loop_trimmed.entities.reserve(j - i); + for (; i < j; ++ i) { + this_loop_trimmed.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(print.skirt_first_layer_height()))); + const ClipperLib_Z::Path &path = *loops_trimmed_order[i].first; + Points &points = static_cast(this_loop_trimmed.entities.back())->polyline.points; + points.reserve(path.size()); + for (const ClipperLib_Z::IntPoint &pt : path) + points.emplace_back(coord_t(pt.X), coord_t(pt.Y)); + } + chain_and_reorder_extrusion_entities(this_loop_trimmed.entities, &last_pt); + brim.entities.reserve(brim.entities.size() + this_loop_trimmed.entities.size()); + append(brim.entities, std::move(this_loop_trimmed.entities)); + this_loop_trimmed.entities.clear(); + } + last_pt = brim.last_point(); + } + } + } else { + extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(print.skirt_first_layer_height())); + } + + make_inner_brim(print, top_level_objects_with_brim, brim); + + return brim; +} + +} // namespace Slic3r diff --git a/src/libslic3r/Brim.hpp b/src/libslic3r/Brim.hpp new file mode 100644 index 000000000..18bff2960 --- /dev/null +++ b/src/libslic3r/Brim.hpp @@ -0,0 +1,14 @@ +#ifndef slic3r_Brim_hpp_ +#define slic3r_Brim_hpp_ + +namespace Slic3r { + +class Print; + +// Produce brim lines around those objects, that have the brim enabled. +// Collect islands_area to be merged into the final 1st layer convex hull. +ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cancel, Polygons &islands_area); + +} // Slic3r + +#endif // slic3r_Brim_hpp_ diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 7b7067d9f..b9f87058c 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -21,6 +21,8 @@ add_library(libslic3r STATIC BoundingBox.hpp BridgeDetector.cpp BridgeDetector.hpp + Brim.cpp + Brim.hpp ClipperUtils.cpp ClipperUtils.hpp Config.cpp diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 12c19eaef..0b91648da 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -1946,8 +1946,9 @@ public: int& opt_int(const t_config_option_key &opt_key, unsigned int idx) { return this->option(opt_key)->get_at(idx); } int opt_int(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast(this->option(opt_key))->get_at(idx); } + // In ConfigManipulation::toggle_print_fff_options, it is called on option with type ConfigOptionEnumGeneric* and also ConfigOptionEnum*. template - ENUM opt_enum(const t_config_option_key &opt_key) const { return (ENUM)dynamic_cast(this->option(opt_key))->value; } + ENUM opt_enum(const t_config_option_key &opt_key) const { return this->option>(opt_key)->value; } bool opt_bool(const t_config_option_key &opt_key) const { return this->option(opt_key)->value != 0; } bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { return this->option(opt_key)->get_at(idx) != 0; } diff --git a/src/libslic3r/ExPolygon.cpp b/src/libslic3r/ExPolygon.cpp index f698a3558..12bfa3b35 100644 --- a/src/libslic3r/ExPolygon.cpp +++ b/src/libslic3r/ExPolygon.cpp @@ -80,6 +80,13 @@ bool ExPolygon::is_valid() const return true; } +void ExPolygon::douglas_peucker(double tolerance) +{ + this->contour.douglas_peucker(tolerance); + for (Polygon &poly : this->holes) + poly.douglas_peucker(tolerance); +} + bool ExPolygon::contains(const Line &line) const { return this->contains(Polyline(line.a, line.b)); diff --git a/src/libslic3r/ExPolygon.hpp b/src/libslic3r/ExPolygon.hpp index b4651abc2..20d0ffa7d 100644 --- a/src/libslic3r/ExPolygon.hpp +++ b/src/libslic3r/ExPolygon.hpp @@ -49,6 +49,7 @@ public: double area() const; bool empty() const { return contour.points.empty(); } bool is_valid() const; + void douglas_peucker(double tolerance); // Contains the line / polyline / polylines etc COMPLETELY. bool contains(const Line &line) const; @@ -249,6 +250,24 @@ inline Polygons to_polygons(ExPolygons &&src) return polygons; } +inline ExPolygons to_expolygons(const Polygons &polys) +{ + ExPolygons ex_polys; + ex_polys.assign(polys.size(), ExPolygon()); + for (size_t idx = 0; idx < polys.size(); ++idx) + ex_polys[idx].contour = polys[idx]; + return ex_polys; +} + +inline ExPolygons to_expolygons(Polygons &&polys) +{ + ExPolygons ex_polys; + ex_polys.assign(polys.size(), ExPolygon()); + for (size_t idx = 0; idx < polys.size(); ++idx) + ex_polys[idx].contour = std::move(polys[idx]); + return ex_polys; +} + inline void polygons_append(Polygons &dst, const ExPolygon &src) { dst.reserve(dst.size() + src.holes.size() + 1); diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index 6b0153b2e..9b972f211 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -343,6 +343,25 @@ inline void extrusion_entities_append_loops(ExtrusionEntitiesPtr &dst, Polygons loops.clear(); } +inline void extrusion_entities_append_loops_and_paths(ExtrusionEntitiesPtr &dst, Polylines &&polylines, ExtrusionRole role, double mm3_per_mm, float width, float height) +{ + dst.reserve(dst.size() + polylines.size()); + for (Polyline &polyline : polylines) { + if (polyline.is_valid()) { + if (polyline.is_closed()) { + ExtrusionPath extrusion_path(role, mm3_per_mm, width, height); + extrusion_path.polyline = std::move(polyline); + dst.emplace_back(new ExtrusionLoop(std::move(extrusion_path))); + } else { + ExtrusionPath *extrusion_path = new ExtrusionPath(role, mm3_per_mm, width, height); + extrusion_path->polyline = std::move(polyline); + dst.emplace_back(extrusion_path); + } + } + } + polylines.clear(); +} + } #endif diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 87bfe3065..261487cc1 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1790,7 +1790,7 @@ void GCode::process_layer( // Just a reminder: A spiral vase mode is allowed for a single object, single material print only. m_enable_loop_clipping = true; if (m_spiral_vase && layers.size() == 1 && support_layer == nullptr) { - bool enable = (layer.id() > 0 || print.config().brim_width.value == 0.) && (layer.id() >= (size_t)print.config().skirt_height.value && ! print.has_infinite_skirt()); + bool enable = (layer.id() > 0 || !print.has_brim()) && (layer.id() >= (size_t)print.config().skirt_height.value && ! print.has_infinite_skirt()); if (enable) { for (const LayerRegion *layer_region : layer.regions()) if (size_t(layer_region->region()->config().bottom_solid_layers.value) > layer.id() || diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 458eae80a..1dbb153dd 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -33,7 +33,7 @@ class GCode; namespace { struct Item; } struct PrintInstance; -using PrintObjectPtrs = std::vector; +class ConstPrintObjectPtrsAdaptor; class OozePrevention { public: diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 74f061e4e..fae4028de 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -600,7 +600,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int return std::max(0.f, volume_to_wipe); // Soluble filament cannot be wiped in a random infill, neither the filament after it // we will sort objects so that dedicated for wiping are at the beginning: - PrintObjectPtrs object_list = print.objects(); + ConstPrintObjectPtrs object_list = print.objects().vector(); std::sort(object_list.begin(), object_list.end(), [](const PrintObject* a, const PrintObject* b) { return a->config().wipe_into_objects; }); // We will now iterate through diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 7a4bbe27a..b6929b839 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -604,7 +604,7 @@ void PerimeterGenerator::process() // we continue inwards after having finished the brim // TODO: add test for perimeter order if (this->config->external_perimeters_first || - (this->layer_id == 0 && this->print_config->brim_width.value > 0)) + (this->layer_id == 0 && this->object_config->brim_width.value > 0)) entities.reverse(); // append perimeters for this slice as a collection if (! entities.empty()) diff --git a/src/libslic3r/Polygon.cpp b/src/libslic3r/Polygon.cpp index f74df1c9a..7c588c67c 100644 --- a/src/libslic3r/Polygon.cpp +++ b/src/libslic3r/Polygon.cpp @@ -96,6 +96,14 @@ bool Polygon::make_clockwise() return false; } +void Polygon::douglas_peucker(double tolerance) +{ + this->points.push_back(this->points.front()); + Points p = MultiPoint::_douglas_peucker(this->points, tolerance); + p.pop_back(); + this->points = std::move(p); +} + // Does an unoriented polygon contain a point? // Tested by counting intersections along a horizontal line. bool Polygon::contains(const Point &point) const diff --git a/src/libslic3r/Polygon.hpp b/src/libslic3r/Polygon.hpp index b550ae7d7..aa72b4244 100644 --- a/src/libslic3r/Polygon.hpp +++ b/src/libslic3r/Polygon.hpp @@ -55,6 +55,7 @@ public: bool make_counter_clockwise(); bool make_clockwise(); bool is_valid() const { return this->points.size() >= 3; } + void douglas_peucker(double tolerance); // Does an unoriented polygon contain a point? // Tested by counting intersections along a horizontal line. diff --git a/src/libslic3r/Polyline.hpp b/src/libslic3r/Polyline.hpp index ef1da9afb..9c70522bf 100644 --- a/src/libslic3r/Polyline.hpp +++ b/src/libslic3r/Polyline.hpp @@ -75,6 +75,7 @@ public: template void simplify_by_visibility(const T &area); void split_at(const Point &point, Polyline* p1, Polyline* p2) const; bool is_straight() const; + bool is_closed() const { return this->points.front() == this->points.back(); } }; // Don't use this class in production code, it is used exclusively by the Perl binding for unit tests! diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index 751b2a2a6..f535555d0 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -419,7 +419,7 @@ const std::vector& Preset::print_options() "top_solid_infill_speed", "support_material_speed", "support_material_xy_spacing", "support_material_interface_speed", "bridge_speed", "gap_fill_speed", "travel_speed", "first_layer_speed", "perimeter_acceleration", "infill_acceleration", "bridge_acceleration", "first_layer_acceleration", "default_acceleration", "skirts", "skirt_distance", "skirt_height", "draft_shield", - "min_skirt_length", "brim_width", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers", + "min_skirt_length", "brim_width", "brim_offset", "brim_type", "support_material", "support_material_auto", "support_material_threshold", "support_material_enforce_layers", "raft_layers", "support_material_pattern", "support_material_with_sheath", "support_material_spacing", "support_material_synchronize_layers", "support_material_angle", "support_material_interface_layers", "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index d50ff19e8..61a5ae99c 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1,8 +1,7 @@ -#include "clipper/clipper_z.hpp" - #include "Exception.hpp" #include "Print.hpp" #include "BoundingBox.hpp" +#include "Brim.hpp" #include "ClipperUtils.hpp" #include "Extruder.hpp" #include "Flow.hpp" @@ -15,8 +14,6 @@ #include "GCode/WipeTower.hpp" #include "Utils.hpp" -//#include "PrintExport.hpp" - #include #include @@ -174,7 +171,10 @@ bool Print::invalidate_state_by_config_options(const std::vector print_objects_old = std::move(m_objects); + PrintObjectPtrs print_objects_old = std::move(m_objects); m_objects.clear(); m_objects.reserve(print_objects_old.size()); for (PrintObject *print_object : print_objects_old) { @@ -945,7 +945,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_ // 4) Generate PrintObjects from ModelObjects and their instances. { - std::vector print_objects_new; + PrintObjectPtrs print_objects_new; print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size())); bool new_objects = false; // Walk over all new model objects and check, whether there are matching PrintObjects. @@ -1188,6 +1188,12 @@ bool Print::has_skirt() const return (m_config.skirt_height > 0 && m_config.skirts > 0) || this->has_infinite_skirt(); } +bool Print::has_brim() const +{ + return std::any_of(m_objects.begin(), m_objects.end(), + [](PrintObject *object) { return object->config().brim_type != btNoBrim && object->config().brim_width.value > 0.; }); +} + static inline bool sequential_print_horizontal_clearance_valid(const Print &print) { Polygons convex_hulls_other; @@ -1655,9 +1661,12 @@ void Print::process() if (this->set_started(psBrim)) { m_brim.clear(); m_first_layer_convex_hull.points.clear(); - if (m_config.brim_width > 0) { + if (this->has_brim()) { this->set_status(88, L("Generating brim")); - this->_make_brim(); + Polygons islands_area; + m_brim = make_brim(*this, this->make_try_cancel(), islands_area); + for (Polygon &poly : union_(this->first_layer_islands(), islands_area)) + append(m_first_layer_convex_hull.points, std::move(poly.points)); } // Brim depends on skirt (brim lines are trimmed by the skirt lines), therefore if // the skirt gets invalidated, brim gets invalidated as well and the following line is called. @@ -1831,164 +1840,6 @@ void Print::_make_skirt() append(m_skirt_convex_hull, std::move(poly.points)); } -void Print::_make_brim() -{ - // Brim is only printed on first layer and uses perimeter extruder. - Polygons islands = this->first_layer_islands(); - Polygons loops; - Flow flow = this->brim_flow(); - size_t num_loops = size_t(floor(m_config.brim_width.value / flow.spacing())); - for (size_t i = 0; i < num_loops; ++ i) { - this->throw_if_canceled(); - islands = offset(islands, float(flow.scaled_spacing()), jtSquare); - for (Polygon &poly : islands) { - // poly.simplify(SCALED_RESOLUTION); - poly.points.push_back(poly.points.front()); - Points p = MultiPoint::_douglas_peucker(poly.points, SCALED_RESOLUTION); - p.pop_back(); - poly.points = std::move(p); - } - if (i + 1 == num_loops) { - // Remember the outer edge of the last brim line extruded as m_first_layer_convex_hull. - for (Polygon &poly : islands) - append(m_first_layer_convex_hull.points, poly.points); - } - polygons_append(loops, offset(islands, -0.5f * float(flow.scaled_spacing()))); - } - loops = union_pt_chained_outside_in(loops, false); - - // If there is a possibility that brim intersects skirt, go through loops and split those extrusions - // The result is either the original Polygon or a list of Polylines - if (! m_skirt.empty() && m_config.skirt_distance.value < m_config.brim_width) - { - // Find the bounding polygons of the skirt - const Polygons skirt_inners = offset(dynamic_cast(m_skirt.entities.back())->polygon(), - -float(scale_(this->skirt_flow().spacing()))/2.f, - ClipperLib::jtRound, - float(scale_(0.1))); - const Polygons skirt_outers = offset(dynamic_cast(m_skirt.entities.front())->polygon(), - float(scale_(this->skirt_flow().spacing()))/2.f, - ClipperLib::jtRound, - float(scale_(0.1))); - - // First calculate the trimming region. - ClipperLib_Z::Paths trimming; - { - ClipperLib_Z::Paths input_subject; - ClipperLib_Z::Paths input_clip; - for (const Polygon &poly : skirt_outers) { - input_subject.emplace_back(); - ClipperLib_Z::Path &out = input_subject.back(); - out.reserve(poly.points.size()); - for (const Point &pt : poly.points) - out.emplace_back(pt.x(), pt.y(), 0); - } - for (const Polygon &poly : skirt_inners) { - input_clip.emplace_back(); - ClipperLib_Z::Path &out = input_clip.back(); - out.reserve(poly.points.size()); - for (const Point &pt : poly.points) - out.emplace_back(pt.x(), pt.y(), 0); - } - // init Clipper - ClipperLib_Z::Clipper clipper; - // add polygons - clipper.AddPaths(input_subject, ClipperLib_Z::ptSubject, true); - clipper.AddPaths(input_clip, ClipperLib_Z::ptClip, true); - // perform operation - clipper.Execute(ClipperLib_Z::ctDifference, trimming, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd); - } - - // Second, trim the extrusion loops with the trimming regions. - ClipperLib_Z::Paths loops_trimmed; - { - // Produce a closed polyline (repeat the first point at the end). - ClipperLib_Z::Paths input_clip; - for (const Polygon &loop : loops) { - input_clip.emplace_back(); - ClipperLib_Z::Path& out = input_clip.back(); - out.reserve(loop.points.size()); - int64_t loop_idx = &loop - &loops.front(); - for (const Point& pt : loop.points) - // The Z coordinate carries index of the source loop. - out.emplace_back(pt.x(), pt.y(), loop_idx + 1); - out.emplace_back(out.front()); - } - // init Clipper - ClipperLib_Z::Clipper clipper; - clipper.ZFillFunction([](const ClipperLib_Z::IntPoint& e1bot, const ClipperLib_Z::IntPoint& e1top, const ClipperLib_Z::IntPoint& e2bot, const ClipperLib_Z::IntPoint& e2top, ClipperLib_Z::IntPoint& pt) { - // Assign a valid input loop identifier. Such an identifier is strictly positive, the next line is safe even in case one side of a segment - // hat the Z coordinate not set to the contour coordinate. - pt.Z = std::max(std::max(e1bot.Z, e1top.Z), std::max(e2bot.Z, e2top.Z)); - }); - // add polygons - clipper.AddPaths(input_clip, ClipperLib_Z::ptSubject, false); - clipper.AddPaths(trimming, ClipperLib_Z::ptClip, true); - // perform operation - ClipperLib_Z::PolyTree loops_trimmed_tree; - clipper.Execute(ClipperLib_Z::ctDifference, loops_trimmed_tree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd); - ClipperLib_Z::PolyTreeToPaths(loops_trimmed_tree, loops_trimmed); - } - - // Third, produce the extrusions, sorted by the source loop indices. - { - std::vector> loops_trimmed_order; - loops_trimmed_order.reserve(loops_trimmed.size()); - for (const ClipperLib_Z::Path &path : loops_trimmed) { - size_t input_idx = 0; - for (const ClipperLib_Z::IntPoint &pt : path) - if (pt.Z > 0) { - input_idx = (size_t)pt.Z; - break; - } - assert(input_idx != 0); - loops_trimmed_order.emplace_back(&path, input_idx); - } - std::stable_sort(loops_trimmed_order.begin(), loops_trimmed_order.end(), - [](const std::pair &l, const std::pair &r) { - return l.second < r.second; - }); - - Point last_pt(0, 0); - for (size_t i = 0; i < loops_trimmed_order.size();) { - // Find all pieces that the initial loop was split into. - size_t j = i + 1; - for (; j < loops_trimmed_order.size() && loops_trimmed_order[i].second == loops_trimmed_order[j].second; ++ j) ; - const ClipperLib_Z::Path &first_path = *loops_trimmed_order[i].first; - if (i + 1 == j && first_path.size() > 3 && first_path.front().X == first_path.back().X && first_path.front().Y == first_path.back().Y) { - auto *loop = new ExtrusionLoop(); - m_brim.entities.emplace_back(loop); - loop->paths.emplace_back(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height())); - Points &points = loop->paths.front().polyline.points; - points.reserve(first_path.size()); - for (const ClipperLib_Z::IntPoint &pt : first_path) - points.emplace_back(coord_t(pt.X), coord_t(pt.Y)); - i = j; - } else { - //FIXME The path chaining here may not be optimal. - ExtrusionEntityCollection this_loop_trimmed; - this_loop_trimmed.entities.reserve(j - i); - for (; i < j; ++ i) { - this_loop_trimmed.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height()))); - const ClipperLib_Z::Path &path = *loops_trimmed_order[i].first; - Points &points = static_cast(this_loop_trimmed.entities.back())->polyline.points; - points.reserve(path.size()); - for (const ClipperLib_Z::IntPoint &pt : path) - points.emplace_back(coord_t(pt.X), coord_t(pt.Y)); - } - chain_and_reorder_extrusion_entities(this_loop_trimmed.entities, &last_pt); - m_brim.entities.reserve(m_brim.entities.size() + this_loop_trimmed.entities.size()); - append(m_brim.entities, std::move(this_loop_trimmed.entities)); - this_loop_trimmed.entities.clear(); - } - last_pt = m_brim.last_point(); - } - } - } else { - extrusion_entities_append_loops(m_brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height())); - } -} - Polygons Print::first_layer_islands() const { Polygons islands; @@ -2104,8 +1955,8 @@ void Print::_make_wipe_tower() if (idx_begin != size_t(-1)) { // Find the position in m_objects.first()->support_layers to insert these new support layers. double wipe_tower_new_layer_print_z_first = m_wipe_tower_data.tool_ordering.layer_tools()[idx_begin].print_z; - SupportLayerPtrs::const_iterator it_layer = m_objects.front()->support_layers().begin(); - SupportLayerPtrs::const_iterator it_end = m_objects.front()->support_layers().end(); + auto it_layer = m_objects.front()->support_layers().begin(); + auto it_end = m_objects.front()->support_layers().end(); for (; it_layer != it_end && (*it_layer)->print_z - EPSILON < wipe_tower_new_layer_print_z_first; ++ it_layer); // Find the stopper of the sequence of wipe tower layers, which do not have a counterpart in an object or a support layer. for (size_t i = idx_begin; i < idx_end; ++ i) { diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 281ce35bc..819cbab8c 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -73,7 +73,7 @@ public: // Collect 0-based extruder indices used to print this region's object. void collect_object_printing_extruders(std::vector &object_extruders) const; - static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig ®ion_config, std::vector &object_extruders); + static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig ®ion_config, const bool has_brim, std::vector &object_extruders); // Methods modifying the PrintRegion's state: public: @@ -95,9 +95,39 @@ private: ~PrintRegion() = default; }; +template +class ConstVectorOfPtrsAdaptor { +public: + // Returning a non-const pointer to const pointers to T. + T * const * begin() const { return m_data->data(); } + T * const * end() const { return m_data->data() + m_data->size(); } + const T* front() const { return m_data->front(); } + const T* back() const { return m_data->front(); } + size_t size() const { return m_data->size(); } + bool empty() const { return m_data->empty(); } + const T* operator[](size_t i) const { return (*m_data)[i]; } + const T* at(size_t i) const { return m_data->at(i); } + std::vector vector() const { return std::vector(this->begin(), this->end()); } +protected: + ConstVectorOfPtrsAdaptor(const std::vector *data) : m_data(data) {} +private: + const std::vector *m_data; +}; + +typedef std::vector LayerPtrs; +typedef std::vector ConstLayerPtrs; +class ConstLayerPtrsAdaptor : public ConstVectorOfPtrsAdaptor { + friend PrintObject; + ConstLayerPtrsAdaptor(const LayerPtrs *data) : ConstVectorOfPtrsAdaptor(data) {} +}; + +typedef std::vector SupportLayerPtrs; +typedef std::vector ConstSupportLayerPtrs; +class ConstSupportLayerPtrsAdaptor : public ConstVectorOfPtrsAdaptor { + friend PrintObject; + ConstSupportLayerPtrsAdaptor(const SupportLayerPtrs *data) : ConstVectorOfPtrsAdaptor(data) {} +}; -typedef std::vector LayerPtrs; -typedef std::vector SupportLayerPtrs; class BoundingBoxf3; // TODO: for temporary constructor parameter // Single instance of a PrintObject. @@ -125,12 +155,16 @@ public: std::vector>> region_volumes; // Size of an object: XYZ in scaled coordinates. The size might not be quite snug in XY plane. - const Vec3crd& size() const { return m_size; } - const PrintObjectConfig& config() const { return m_config; } - const LayerPtrs& layers() const { return m_layers; } - const SupportLayerPtrs& support_layers() const { return m_support_layers; } - const Transform3d& trafo() const { return m_trafo; } - const PrintInstances& instances() const { return m_instances; } + const Vec3crd& size() const { return m_size; } + const PrintObjectConfig& config() const { return m_config; } + ConstLayerPtrsAdaptor layers() const { return ConstLayerPtrsAdaptor(&m_layers); } + ConstSupportLayerPtrsAdaptor support_layers() const { return ConstSupportLayerPtrsAdaptor(&m_support_layers); } + const Transform3d& trafo() const { return m_trafo; } + const PrintInstances& instances() const { return m_instances; } + + // Whoever will get a non-const pointer to PrintObject will be able to modify its layers. + LayerPtrs& layers() { return m_layers; } + SupportLayerPtrs& support_layers() { return m_support_layers; } // Bounding box is used to align the object infill patterns, and to calculate attractor for the rear seam. // The bounding box may not be quite snug. @@ -171,7 +205,7 @@ public: void clear_support_layers(); SupportLayer* get_support_layer(int idx) { return m_support_layers[idx]; } SupportLayer* add_support_layer(int id, coordf_t height, coordf_t print_z); - SupportLayerPtrs::const_iterator insert_support_layer(SupportLayerPtrs::const_iterator pos, size_t id, coordf_t height, coordf_t print_z, coordf_t slice_z); + SupportLayerPtrs::iterator insert_support_layer(SupportLayerPtrs::iterator pos, size_t id, coordf_t height, coordf_t print_z, coordf_t slice_z); void delete_support_layer(int idx); // Initialize the layer_height_profile from the model_object's layer_height_profile, from model_object's layer height table, or from slicing parameters. @@ -344,14 +378,27 @@ struct PrintStatistics } }; -typedef std::vector PrintObjectPtrs; -typedef std::vector PrintRegionPtrs; +typedef std::vector PrintObjectPtrs; +typedef std::vector ConstPrintObjectPtrs; +class ConstPrintObjectPtrsAdaptor : public ConstVectorOfPtrsAdaptor { + friend Print; + ConstPrintObjectPtrsAdaptor(const PrintObjectPtrs *data) : ConstVectorOfPtrsAdaptor(data) {} +}; + +typedef std::vector PrintRegionPtrs; +typedef std::vector ConstPrintRegionPtrs; +class ConstPrintRegionPtrsAdaptor : public ConstVectorOfPtrsAdaptor { + friend Print; + ConstPrintRegionPtrsAdaptor(const PrintRegionPtrs *data) : ConstVectorOfPtrsAdaptor(data) {} +}; // The complete print tray with possibly multiple objects. class Print : public PrintBaseWithState { private: // Prevents erroneous use by other classes. typedef PrintBaseWithState Inherited; + // Bool indicates if supports of PrintObject are top-level contour. + typedef std::pair PrintObjectInfo; public: Print() = default; @@ -385,6 +432,7 @@ public: bool has_infinite_skirt() const; bool has_skirt() const; + bool has_brim() const; // Returns an empty string if valid, otherwise returns an error message. std::string validate() const override; @@ -403,9 +451,8 @@ public: const PrintConfig& config() const { return m_config; } const PrintObjectConfig& default_object_config() const { return m_default_object_config; } const PrintRegionConfig& default_region_config() const { return m_default_region_config; } - //FIXME returning const vector to non-const PrintObject*, caller could modify PrintObjects! - const PrintObjectPtrs& objects() const { return m_objects; } - PrintObject* get_object(size_t idx) { return m_objects[idx]; } + ConstPrintObjectPtrsAdaptor objects() const { return ConstPrintObjectPtrsAdaptor(&m_objects); } + PrintObject* get_object(size_t idx) { return const_cast(m_objects[idx]); } const PrintObject* get_object(size_t idx) const { return m_objects[idx]; } // PrintObject by its ObjectID, to be used to uniquely bind slicing warnings to their source PrintObjects // in the notification center. @@ -414,11 +461,15 @@ public: [object_id](const PrintObject *obj) { return obj->id() == object_id; }); return (it == m_objects.end()) ? nullptr : *it; } - const PrintRegionPtrs& regions() const { return m_regions; } + ConstPrintRegionPtrsAdaptor regions() const { return ConstPrintRegionPtrsAdaptor(&m_regions); } // How many of PrintObject::copies() over all print objects are there? // If zero, then the print is empty and the print shall not be executed. unsigned int num_object_instances() const; + // For Perl bindings. + PrintObjectPtrs& objects_mutable() { return m_objects; } + PrintRegionPtrs& regions_mutable() { return m_regions; } + const ExtrusionEntityCollection& skirt() const { return m_skirt; } const ExtrusionEntityCollection& brim() const { return m_brim; } // Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line. @@ -461,7 +512,6 @@ private: bool invalidate_state_by_config_options(const std::vector &opt_keys); void _make_skirt(); - void _make_brim(); void _make_wipe_tower(); void finalize_first_layer_convex_hull(); diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index fb5e102c1..721741d4a 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -13,6 +13,11 @@ namespace Slic3r { +void PrintTryCancel::operator()() +{ + m_print->throw_if_canceled(); +} + size_t PrintStateBase::g_last_timestamp = 0; // Update "scale", "input_filename", "input_filename_base" placeholders from the current m_objects. diff --git a/src/libslic3r/PrintBase.hpp b/src/libslic3r/PrintBase.hpp index bfbabd06b..8fca43319 100644 --- a/src/libslic3r/PrintBase.hpp +++ b/src/libslic3r/PrintBase.hpp @@ -324,6 +324,20 @@ protected: ModelObject *m_model_object; }; +// Wrapper around the private PrintBase.throw_if_canceled(), so that a cancellation object could be passed +// to a non-friend of PrintBase by a PrintBase derived object. +class PrintTryCancel +{ +public: + // calls print.throw_if_canceled(). + void operator()(); +private: + friend PrintBase; + PrintTryCancel() = delete; + PrintTryCancel(const PrintBase *print) : m_print(print) {} + const PrintBase *m_print; +}; + /** * @brief Printing involves slicing and export of device dependent instructions. * @@ -472,6 +486,8 @@ protected: // If the background processing stop was requested, throw CanceledException. // To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly. void throw_if_canceled() const { if (m_cancel_status) throw CanceledException(); } + // Wrapper around this->throw_if_canceled(), so that throw_if_canceled() may be passed to a function without making throw_if_canceled() public. + PrintTryCancel make_try_cancel() const { return PrintTryCancel(this); } // To be called by this->output_filename() with the format string pulled from the configuration layer. std::string output_filename(const std::string &format, const std::string &default_ext, const std::string &filename_base, const DynamicConfig *config_override = nullptr) const; @@ -495,6 +511,8 @@ private: // The mutex will be used to guard the worker thread against entering a stage // while the data influencing the stage is modified. mutable tbb::mutex m_state_mutex; + + friend PrintTryCancel; }; template diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 587c22cc6..20f3991c9 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -298,12 +298,37 @@ void PrintConfigDef::init_fff_params() def = this->add("brim_width", coFloat); def->label = L("Brim width"); + def->category = L("Skirt and brim"); def->tooltip = L("Horizontal width of the brim that will be printed around each object on the first layer."); def->sidetext = L("mm"); def->min = 0; def->mode = comSimple; def->set_default_value(new ConfigOptionFloat(0)); + def = this->add("brim_type", coEnum); + def->label = L("Brim type"); + def->category = L("Skirt and brim"); + def->tooltip = L("The places where the brim will be printed around each object on the first layer."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.emplace_back("no_brim"); + def->enum_values.emplace_back("outer_only"); + def->enum_values.emplace_back("inner_only"); + def->enum_values.emplace_back("outer_and_inner"); + def->enum_labels.emplace_back(L("No brim")); + def->enum_labels.emplace_back(L("Outer brim only")); + def->enum_labels.emplace_back(L("Inner brim only")); + def->enum_labels.emplace_back(L("Outer and inner brim")); + def->mode = comSimple; + def->set_default_value(new ConfigOptionEnum(btOuterOnly)); + + def = this->add("brim_offset", coFloat); + def->label = L("Brim offset"); + def->category = L("Skirt and brim"); + def->tooltip = L("The offset of the brim from the printed object."); + def->sidetext = L("mm"); + def->mode = comSimple; + def->set_default_value(new ConfigOptionFloat(0)); + def = this->add("clip_multipart_objects", coBool); def->label = L("Clip multi-part objects"); def->tooltip = L("When printing multi-material objects, this settings will make Slic3r " diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index e2f71a941..5189baab2 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -103,6 +103,13 @@ enum SLAPillarConnectionMode { slapcmDynamic }; +enum BrimType { + btNoBrim, + btOuterOnly, + btInnerOnly, + btOuterAndInner, +}; + template<> inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { static t_config_enum_values keys_map; if (keys_map.empty()) { @@ -264,6 +271,17 @@ template<> inline const t_config_enum_values& ConfigOptionEnum inline const t_config_enum_values& ConfigOptionEnum::get_enum_values() { + static const t_config_enum_values keys_map = { + {"no_brim", btNoBrim}, + {"outer_only", btOuterOnly}, + {"inner_only", btInnerOnly}, + {"outer_and_inner", btOuterAndInner} + }; + + return keys_map; +} + // Defines each and every confiuration option of Slic3r, including the properties of the GUI dialogs. // Does not store the actual values, but defines default values. class PrintConfigDef : public ConfigDef @@ -477,6 +495,9 @@ class PrintObjectConfig : public StaticPrintConfig { STATIC_PRINT_CONFIG_CACHE(PrintObjectConfig) public: + ConfigOptionFloat brim_offset; + ConfigOptionEnum brim_type; + ConfigOptionFloat brim_width; ConfigOptionBool clip_multipart_objects; ConfigOptionBool dont_support_bridges; ConfigOptionFloat elefant_foot_compensation; @@ -526,6 +547,9 @@ public: protected: void initialize(StaticCacheBase &cache, const char *base_ptr) { + OPT_PTR(brim_offset); + OPT_PTR(brim_type); + OPT_PTR(brim_width); OPT_PTR(clip_multipart_objects); OPT_PTR(dont_support_bridges); OPT_PTR(elefant_foot_compensation); @@ -891,7 +915,6 @@ public: ConfigOptionInts bed_temperature; ConfigOptionFloat bridge_acceleration; ConfigOptionInts bridge_fan_speed; - ConfigOptionFloat brim_width; ConfigOptionBool complete_objects; ConfigOptionFloats colorprint_heights; ConfigOptionBools cooling; @@ -966,7 +989,6 @@ protected: OPT_PTR(bed_temperature); OPT_PTR(bridge_acceleration); OPT_PTR(bridge_fan_speed); - OPT_PTR(brim_width); OPT_PTR(complete_objects); OPT_PTR(colorprint_heights); OPT_PTR(cooling); diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index 543a45846..d639d03c8 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -501,7 +501,7 @@ SupportLayer* PrintObject::add_support_layer(int id, coordf_t height, coordf_t p return m_support_layers.back(); } -SupportLayerPtrs::const_iterator PrintObject::insert_support_layer(SupportLayerPtrs::const_iterator pos, size_t id, coordf_t height, coordf_t print_z, coordf_t slice_z) +SupportLayerPtrs::iterator PrintObject::insert_support_layer(SupportLayerPtrs::iterator pos, size_t id, coordf_t height, coordf_t print_z, coordf_t slice_z) { return m_support_layers.insert(pos, new SupportLayer(id, this, height, print_z, slice_z)); } @@ -1628,6 +1628,7 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full PrintRegion::collect_object_printing_extruders( print_config, region_config_from_model_volume(default_region_config, nullptr, *model_volume, num_extruders), + object_config.brim_type != btNoBrim && object_config.brim_width > 0., object_extruders); for (const std::pair &range_and_config : model_object.layer_config_ranges) if (range_and_config.second.has("perimeter_extruder") || @@ -1636,6 +1637,7 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full PrintRegion::collect_object_printing_extruders( print_config, region_config_from_model_volume(default_region_config, &range_and_config.second.get(), *model_volume, num_extruders), + object_config.brim_type != btNoBrim && object_config.brim_width > 0., object_extruders); } sort_remove_duplicates(object_extruders); @@ -1721,7 +1723,7 @@ void PrintObject::_slice(const std::vector &layer_height_profile) } // Make sure all layers contain layer region objects for all regions. for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) - layer->add_region(this->print()->regions()[region_id]); + layer->add_region(this->print()->get_region(region_id)); prev = layer; } } diff --git a/src/libslic3r/PrintRegion.cpp b/src/libslic3r/PrintRegion.cpp index 2a75cd621..79eb647f6 100644 --- a/src/libslic3r/PrintRegion.cpp +++ b/src/libslic3r/PrintRegion.cpp @@ -66,7 +66,7 @@ coordf_t PrintRegion::bridging_height_avg(const PrintConfig &print_config) const return this->nozzle_dmr_avg(print_config) * sqrt(m_config.bridge_flow_ratio.value); } -void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig ®ion_config, std::vector &object_extruders) +void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig ®ion_config, const bool has_brim, std::vector &object_extruders) { // These checks reflect the same logic used in the GUI for enabling/disabling extruder selection fields. auto num_extruders = (int)print_config.nozzle_diameter.size(); @@ -74,7 +74,7 @@ void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_con int i = std::max(0, extruder_id - 1); object_extruders.emplace_back((i >= num_extruders) ? 0 : i); }; - if (region_config.perimeters.value > 0 || print_config.brim_width.value > 0) + if (region_config.perimeters.value > 0 || has_brim) emplace_extruder(region_config.perimeter_extruder); if (region_config.fill_density.value > 0) emplace_extruder(region_config.infill_extruder); @@ -92,7 +92,7 @@ void PrintRegion::collect_object_printing_extruders(std::vector &o assert(this->config().infill_extruder <= num_extruders); assert(this->config().solid_infill_extruder <= num_extruders); #endif - collect_object_printing_extruders(print()->config(), this->config(), object_extruders); + collect_object_printing_extruders(print()->config(), this->config(), print()->has_brim(), object_extruders); } } diff --git a/src/libslic3r/SupportMaterial.cpp b/src/libslic3r/SupportMaterial.cpp index 95c429754..1695075c8 100644 --- a/src/libslic3r/SupportMaterial.cpp +++ b/src/libslic3r/SupportMaterial.cpp @@ -388,7 +388,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) BOOST_LOG_TRIVIAL(info) << "Support generator - Generating tool paths"; // Generate the actual toolpaths and save them into each layer. - this->generate_toolpaths(object, raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers); + this->generate_toolpaths(object.support_layers(), raft_layers, bottom_contacts, top_contacts, intermediate_layers, interface_layers); #ifdef SLIC3R_DEBUG { @@ -1662,62 +1662,74 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta // If no vec item with Z value >= of an internal threshold of fn_higher_equal is found, return vec.size() // If the initial idx is size_t(-1), then use binary search. // Otherwise search linearly upwards. -template -size_t idx_higher_or_equal(const std::vector &vec, size_t idx, FN_HIGHER_EQUAL fn_higher_equal) +template +size_t idx_higher_or_equal(IT begin, IT end, size_t idx, FN_HIGHER_EQUAL fn_higher_equal) { - if (vec.empty()) { + auto size = int(end - begin); + if (size == 0) { idx = 0; } else if (idx == size_t(-1)) { // First of the batch of layers per thread pool invocation. Use binary search. int idx_low = 0; - int idx_high = std::max(0, int(vec.size()) - 1); + int idx_high = std::max(0, size - 1); while (idx_low + 1 < idx_high) { int idx_mid = (idx_low + idx_high) / 2; - if (fn_higher_equal(vec[idx_mid])) + if (fn_higher_equal(begin[idx_mid])) idx_high = idx_mid; else idx_low = idx_mid; } - idx = fn_higher_equal(vec[idx_low]) ? idx_low : - (fn_higher_equal(vec[idx_high]) ? idx_high : vec.size()); + idx = fn_higher_equal(begin[idx_low]) ? idx_low : + (fn_higher_equal(begin[idx_high]) ? idx_high : size); } else { // For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search. - while (idx < vec.size() && ! fn_higher_equal(vec[idx])) + while (idx < size && ! fn_higher_equal(begin[idx])) ++ idx; } return idx; } +template +size_t idx_higher_or_equal(const std::vector& vec, size_t idx, FN_HIGHER_EQUAL fn_higher_equal) +{ + return idx_higher_or_equal(vec.begin(), vec.end(), idx, fn_higher_equal); +} // FN_LOWER_EQUAL: the provided object pointer has a Z value <= of an internal threshold. // Find the first item with Z value <= of an internal threshold of fn_lower_equal. // If no vec item with Z value <= of an internal threshold of fn_lower_equal is found, return -1. // If the initial idx is < -1, then use binary search. // Otherwise search linearly downwards. -template -int idx_lower_or_equal(const std::vector &vec, int idx, FN_LOWER_EQUAL fn_lower_equal) +template +int idx_lower_or_equal(IT begin, IT end, int idx, FN_LOWER_EQUAL fn_lower_equal) { - if (vec.empty()) { + auto size = int(end - begin); + if (size == 0) { idx = -1; } else if (idx < -1) { // First of the batch of layers per thread pool invocation. Use binary search. int idx_low = 0; - int idx_high = std::max(0, int(vec.size()) - 1); + int idx_high = std::max(0, size - 1); while (idx_low + 1 < idx_high) { int idx_mid = (idx_low + idx_high) / 2; - if (fn_lower_equal(vec[idx_mid])) + if (fn_lower_equal(begin[idx_mid])) idx_low = idx_mid; else idx_high = idx_mid; } - idx = fn_lower_equal(vec[idx_high]) ? idx_high : - (fn_lower_equal(vec[idx_low ]) ? idx_low : -1); + idx = fn_lower_equal(begin[idx_high]) ? idx_high : + (fn_lower_equal(begin[idx_low ]) ? idx_low : -1); } else { // For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search. - while (idx >= 0 && ! fn_lower_equal(vec[idx])) + while (idx >= 0 && ! fn_lower_equal(begin[idx])) -- idx; } return idx; } +template +int idx_lower_or_equal(const std::vector &vec, int idx, FN_LOWER_EQUAL fn_lower_equal) +{ + return idx_lower_or_equal(vec.begin(), vec.end(), idx, fn_lower_equal); +} // Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them. void PrintObjectSupportMaterial::trim_top_contacts_by_bottom_contacts( @@ -1972,7 +1984,7 @@ void PrintObjectSupportMaterial::generate_base_layers( Polygons polygons_new; // Use the precomputed layer_support_areas. - idx_object_layer_above = std::max(0, idx_lower_or_equal(object.layers(), idx_object_layer_above, + idx_object_layer_above = std::max(0, idx_lower_or_equal(object.layers().begin(), object.layers().end(), idx_object_layer_above, [&layer_intermediate](const Layer *layer){ return layer->print_z <= layer_intermediate.print_z + EPSILON; })); polygons_new = layer_support_areas[idx_object_layer_above]; @@ -2108,7 +2120,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( // Find the overlapping object layers including the extra above / below gap. coordf_t z_threshold = support_layer.print_z - support_layer.height - gap_extra_below + EPSILON; idx_object_layer_overlapping = idx_higher_or_equal( - object.layers(), idx_object_layer_overlapping, + object.layers().begin(), object.layers().end(), idx_object_layer_overlapping, [z_threshold](const Layer *layer){ return layer->print_z >= z_threshold; }); // Collect all the object layers intersecting with this layer. Polygons polygons_trimming; @@ -2931,7 +2943,7 @@ void modulate_extrusion_by_overlapping_layers( } void PrintObjectSupportMaterial::generate_toolpaths( - const PrintObject &object, + SupportLayerPtrs &support_layers, const MyLayersPtr &raft_layers, const MyLayersPtr &bottom_contacts, const MyLayersPtr &top_contacts, @@ -3000,13 +3012,13 @@ void PrintObjectSupportMaterial::generate_toolpaths( // Insert the raft base layers. size_t n_raft_layers = size_t(std::max(0, int(m_slicing_params.raft_layers()) - 1)); tbb::parallel_for(tbb::blocked_range(0, n_raft_layers), - [this, &object, &raft_layers, + [this, &support_layers, &raft_layers, infill_pattern, &bbox_object, support_density, interface_density, raft_angle_1st_layer, raft_angle_base, raft_angle_interface, link_max_length_factor, with_sheath] (const tbb::blocked_range& range) { for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) { assert(support_layer_id < raft_layers.size()); - SupportLayer &support_layer = *object.support_layers()[support_layer_id]; + SupportLayer &support_layer = *support_layers[support_layer_id]; assert(support_layer.support_fills.entities.empty()); MyLayer &raft_layer = *raft_layers[support_layer_id]; @@ -3102,10 +3114,10 @@ void PrintObjectSupportMaterial::generate_toolpaths( MyLayerExtruded interface_layer; std::vector overlaps; }; - std::vector layer_caches(object.support_layers().size(), LayerCache()); + std::vector layer_caches(support_layers.size(), LayerCache()); - tbb::parallel_for(tbb::blocked_range(n_raft_layers, object.support_layers().size()), - [this, &object, &bottom_contacts, &top_contacts, &intermediate_layers, &interface_layers, &layer_caches, &loop_interface_processor, + tbb::parallel_for(tbb::blocked_range(n_raft_layers, support_layers.size()), + [this, &support_layers, &bottom_contacts, &top_contacts, &intermediate_layers, &interface_layers, &layer_caches, &loop_interface_processor, infill_pattern, &bbox_object, support_density, interface_density, interface_angle, &angles, link_max_length_factor, with_sheath] (const tbb::blocked_range& range) { // Indices of the 1st layer in their respective container at the support layer height. @@ -3119,7 +3131,7 @@ void PrintObjectSupportMaterial::generate_toolpaths( filler_support->set_bounding_box(bbox_object); for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) { - SupportLayer &support_layer = *object.support_layers()[support_layer_id]; + SupportLayer &support_layer = *support_layers[support_layer_id]; LayerCache &layer_cache = layer_caches[support_layer_id]; // Find polygons with the same print_z. @@ -3317,11 +3329,11 @@ void PrintObjectSupportMaterial::generate_toolpaths( }); // Now modulate the support layer height in parallel. - tbb::parallel_for(tbb::blocked_range(n_raft_layers, object.support_layers().size()), - [this, &object, &layer_caches] + tbb::parallel_for(tbb::blocked_range(n_raft_layers, support_layers.size()), + [this, &support_layers, &layer_caches] (const tbb::blocked_range& range) { for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) { - SupportLayer &support_layer = *object.support_layers()[support_layer_id]; + SupportLayer &support_layer = *support_layers[support_layer_id]; LayerCache &layer_cache = layer_caches[support_layer_id]; for (LayerCacheItem &layer_cache_item : layer_cache.overlaps) { modulate_extrusion_by_overlapping_layers(layer_cache_item.layer_extruded->extrusions, *layer_cache_item.layer_extruded->layer, layer_cache_item.overlapping); diff --git a/src/libslic3r/SupportMaterial.hpp b/src/libslic3r/SupportMaterial.hpp index 2e1a05946..72d487949 100644 --- a/src/libslic3r/SupportMaterial.hpp +++ b/src/libslic3r/SupportMaterial.hpp @@ -224,7 +224,7 @@ private: // Produce the actual G-code. void generate_toolpaths( - const PrintObject &object, + SupportLayerPtrs &support_layers, const MyLayersPtr &raft_layers, const MyLayersPtr &bottom_contacts, const MyLayersPtr &top_contacts, diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index aba26b203..1f67f8792 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -267,7 +267,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) for (auto el : { "skirt_distance", "draft_shield", "min_skirt_length" }) toggle_field(el, have_skirt); - bool have_brim = config->opt_float("brim_width") > 0; + bool have_brim = config->opt_enum("brim_type") != btNoBrim; + for (auto el : { "brim_width", "brim_offset" }) + toggle_field(el, have_brim); // perimeter_extruder uses the same logic as in Print::extruders() toggle_field("perimeter_extruder", have_perimeters || have_brim); diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index fc8b25efa..be7d7c1c7 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -1220,6 +1220,8 @@ boost::any& Choice::get_value() m_value = static_cast(ret_enum); else if (m_opt_id == "printhost_authorization_type") m_value = static_cast(ret_enum); + else if (m_opt_id == "brim_type") + m_value = static_cast(ret_enum); } else if (m_opt.gui_type == "f_enum_open") { const int ret_enum = field->GetSelection(); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 6332a7837..fbe6681f4 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -5680,7 +5680,7 @@ void GLCanvas3D::_load_print_toolpaths() if (!print->is_step_done(psSkirt) || !print->is_step_done(psBrim)) return; - if (!print->has_skirt() && (print->config().brim_width.value == 0)) + if (!print->has_skirt() && !print->has_brim()) return; const float color[] = { 0.5f, 1.0f, 0.5f, 1.0f }; // greenish @@ -5692,7 +5692,7 @@ void GLCanvas3D::_load_print_toolpaths() total_layer_count = std::max(total_layer_count, print_object->total_layer_count()); } size_t skirt_height = print->has_infinite_skirt() ? total_layer_count : std::min(print->config().skirt_height.value, total_layer_count); - if ((skirt_height == 0) && (print->config().brim_width.value > 0)) + if ((skirt_height == 0) && print->has_brim()) skirt_height = 1; // Get first skirt_height layers. diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index b0322a821..58149181d 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -202,6 +202,8 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); else if(opt_key == "printhost_authorization_type") config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); + else if(opt_key == "brim_type") + config.set_key_value(opt_key, new ConfigOptionEnum(boost::any_cast(value))); } break; case coPoints:{ diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index a1249c696..cf2178280 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -97,7 +97,7 @@ ObjectList::ObjectList(wxWindow* parent) : CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel"); CATEGORY_ICON[L("Extrusion Width")] = create_scaled_bitmap("funnel"); CATEGORY_ICON[L("Wipe options")] = create_scaled_bitmap("funnel"); -// CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap("skirt+brim"); + CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap("skirt+brim"); // CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap("time"); CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap("wrench"); // ptSLA @@ -686,7 +686,7 @@ void ObjectList::msw_rescale_icons() CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel"); CATEGORY_ICON[L("Extrusion Width")] = create_scaled_bitmap("funnel"); CATEGORY_ICON[L("Wipe options")] = create_scaled_bitmap("funnel"); -// CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap("skirt+brim"); + CATEGORY_ICON[L("Skirt and brim")] = create_scaled_bitmap("skirt+brim"); // CATEGORY_ICON[L("Speed > Acceleration")] = create_scaled_bitmap("time"); CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap("wrench"); // ptSLA diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 8f5e20241..dafce5efc 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -899,6 +899,9 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config else if (opt_key == "printhost_authorization_type") { ret = static_cast(config.option>(opt_key)->value); } + else if (opt_key == "brim_type") { + ret = static_cast(config.option>(opt_key)->value); + } } break; case coPoints: diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 8b4274579..398502b53 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1924,7 +1924,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) , main_frame(main_frame) , config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({ "bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance", - "brim_width", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material", + "brim_width", "brim_offset", "brim_type", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material", "wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology", // These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor. diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index eb9659c8a..843ec5652 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1479,7 +1479,9 @@ void TabPrint::build() optgroup->append_single_option_line("min_skirt_length", category_path + "skirt"); optgroup = page->new_optgroup(L("Brim")); + optgroup->append_single_option_line("brim_type", category_path + "brim"); optgroup->append_single_option_line("brim_width", category_path + "brim"); + optgroup->append_single_option_line("brim_offset", category_path + "brim"); page = add_options_page(L("Support material"), "support"); category_path = "support-material_1698#"; diff --git a/src/slic3r/GUI/UnsavedChangesDialog.cpp b/src/slic3r/GUI/UnsavedChangesDialog.cpp index cd5183cb3..68487921d 100644 --- a/src/slic3r/GUI/UnsavedChangesDialog.cpp +++ b/src/slic3r/GUI/UnsavedChangesDialog.cpp @@ -966,6 +966,8 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig& return get_string_from_enum(opt_key, config); if (opt_key == "support_pillar_connection_mode") return get_string_from_enum(opt_key, config); + if (opt_key == "brim_type") + return get_string_from_enum(opt_key, config); break; } case coPoints: { -- cgit v1.2.3