Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVojtech Bubnik <bubnikv@gmail.com>2021-02-03 17:12:53 +0300
committerVojtech Bubnik <bubnikv@gmail.com>2021-02-03 17:12:53 +0300
commit73c9f939e07916350bb0a70eeb4fee28da7dad9d (patch)
tree6af9ba45a76bfd795e466f9e419194a2eee87668 /src
parente52efe48b08c6f1fd8dd39829f967ca5a6a57d3d (diff)
Squash merge of lh_brim_rework,
brim separated to Brim.cpp,hpp Refactored accessors to PrintObjectPtrs, PrintRegionPtrs, LayerPtrs, SupportLayerPtrs for const correctness.
Diffstat (limited to 'src')
-rw-r--r--src/libslic3r/Brim.cpp490
-rw-r--r--src/libslic3r/Brim.hpp14
-rw-r--r--src/libslic3r/CMakeLists.txt2
-rw-r--r--src/libslic3r/Config.hpp3
-rw-r--r--src/libslic3r/ExPolygon.cpp7
-rw-r--r--src/libslic3r/ExPolygon.hpp19
-rw-r--r--src/libslic3r/ExtrusionEntity.hpp19
-rw-r--r--src/libslic3r/GCode.cpp2
-rw-r--r--src/libslic3r/GCode.hpp2
-rw-r--r--src/libslic3r/GCode/ToolOrdering.cpp2
-rw-r--r--src/libslic3r/PerimeterGenerator.cpp2
-rw-r--r--src/libslic3r/Polygon.cpp8
-rw-r--r--src/libslic3r/Polygon.hpp1
-rw-r--r--src/libslic3r/Polyline.hpp1
-rw-r--r--src/libslic3r/Preset.cpp2
-rw-r--r--src/libslic3r/Print.cpp189
-rw-r--r--src/libslic3r/Print.hpp84
-rw-r--r--src/libslic3r/PrintBase.cpp5
-rw-r--r--src/libslic3r/PrintBase.hpp18
-rw-r--r--src/libslic3r/PrintConfig.cpp25
-rw-r--r--src/libslic3r/PrintConfig.hpp26
-rw-r--r--src/libslic3r/PrintObject.cpp6
-rw-r--r--src/libslic3r/PrintRegion.cpp6
-rw-r--r--src/libslic3r/SupportMaterial.cpp70
-rw-r--r--src/libslic3r/SupportMaterial.hpp2
-rw-r--r--src/slic3r/GUI/ConfigManipulation.cpp4
-rw-r--r--src/slic3r/GUI/Field.cpp2
-rw-r--r--src/slic3r/GUI/GLCanvas3D.cpp4
-rw-r--r--src/slic3r/GUI/GUI.cpp2
-rw-r--r--src/slic3r/GUI/GUI_ObjectList.cpp4
-rw-r--r--src/slic3r/GUI/OptionsGroup.cpp3
-rw-r--r--src/slic3r/GUI/Plater.cpp2
-rw-r--r--src/slic3r/GUI/Tab.cpp2
-rw-r--r--src/slic3r/GUI/UnsavedChangesDialog.cpp2
34 files changed, 795 insertions, 235 deletions
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 <algorithm>
+#include <numeric>
+#include <tbb/parallel_for.h>
+
+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<size_t> 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<size_t> 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<size_t> 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<double>().norm();
+ double dist_to_end = (next.last_point() - prev.last_point()).cast<double>().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<Points> 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<Polylines> 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<size_t>(0, loops_pl.size()),
+ [&loops_pl_by_levels, &loops_pl, &islands_area](const tbb::blocked_range<size_t> &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<ExtrusionLoop*>(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<ExtrusionLoop*>(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<std::pair<const ClipperLib_Z::Path*, size_t>> 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<const ClipperLib_Z::Path*, size_t> &l, const std::pair<const ClipperLib_Z::Path*, size_t> &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<ExtrusionPath*>(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<ConfigOptionInts>(opt_key)->get_at(idx); }
int opt_int(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast<const ConfigOptionInts*>(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<typename ENUM>
- ENUM opt_enum(const t_config_option_key &opt_key) const { return (ENUM)dynamic_cast<const ConfigOptionEnumGeneric*>(this->option(opt_key))->value; }
+ ENUM opt_enum(const t_config_option_key &opt_key) const { return this->option<ConfigOptionEnum<ENUM>>(opt_key)->value; }
bool opt_bool(const t_config_option_key &opt_key) const { return this->option<ConfigOptionBool>(opt_key)->value != 0; }
bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { return this->option<ConfigOptionBools>(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<PrintObject*>;
+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 <class T> 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<std::string>& 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 <float.h>
#include <algorithm>
@@ -174,7 +171,10 @@ bool Print::invalidate_state_by_config_options(const std::vector<t_config_option
|| opt_key == "wipe_tower_y"
|| opt_key == "wipe_tower_rotation_angle") {
steps.emplace_back(psSkirt);
- } else if (opt_key == "brim_width") {
+ } else if (
+ opt_key == "brim_width"
+ || opt_key == "brim_offset"
+ || opt_key == "brim_type") {
steps.emplace_back(psBrim);
steps.emplace_back(psSkirt);
} else if (
@@ -797,7 +797,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
}
if (deleted_any) {
// Delete PrintObjects of the deleted ModelObjects.
- std::vector<PrintObject*> 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<PrintObject*> 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<ExtrusionLoop*>(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<ExtrusionLoop*>(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<std::pair<const ClipperLib_Z::Path*, size_t>> 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<const ClipperLib_Z::Path*, size_t> &l, const std::pair<const ClipperLib_Z::Path*, size_t> &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<ExtrusionPath*>(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<unsigned int> &object_extruders) const;
- static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig &region_config, std::vector<unsigned int> &object_extruders);
+ static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig &region_config, const bool has_brim, std::vector<unsigned int> &object_extruders);
// Methods modifying the PrintRegion's state:
public:
@@ -95,9 +95,39 @@ private:
~PrintRegion() = default;
};
+template<typename T>
+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<const T*> vector() const { return std::vector<const T*>(this->begin(), this->end()); }
+protected:
+ ConstVectorOfPtrsAdaptor(const std::vector<T*> *data) : m_data(data) {}
+private:
+ const std::vector<T*> *m_data;
+};
+
+typedef std::vector<Layer*> LayerPtrs;
+typedef std::vector<const Layer*> ConstLayerPtrs;
+class ConstLayerPtrsAdaptor : public ConstVectorOfPtrsAdaptor<Layer> {
+ friend PrintObject;
+ ConstLayerPtrsAdaptor(const LayerPtrs *data) : ConstVectorOfPtrsAdaptor<Layer>(data) {}
+};
+
+typedef std::vector<SupportLayer*> SupportLayerPtrs;
+typedef std::vector<const SupportLayer*> ConstSupportLayerPtrs;
+class ConstSupportLayerPtrsAdaptor : public ConstVectorOfPtrsAdaptor<SupportLayer> {
+ friend PrintObject;
+ ConstSupportLayerPtrsAdaptor(const SupportLayerPtrs *data) : ConstVectorOfPtrsAdaptor<SupportLayer>(data) {}
+};
-typedef std::vector<Layer*> LayerPtrs;
-typedef std::vector<SupportLayer*> SupportLayerPtrs;
class BoundingBoxf3; // TODO: for temporary constructor parameter
// Single instance of a PrintObject.
@@ -125,12 +155,16 @@ public:
std::vector<std::vector<std::pair<t_layer_height_range, int>>> 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<PrintObject*> PrintObjectPtrs;
-typedef std::vector<PrintRegion*> PrintRegionPtrs;
+typedef std::vector<PrintObject*> PrintObjectPtrs;
+typedef std::vector<const PrintObject*> ConstPrintObjectPtrs;
+class ConstPrintObjectPtrsAdaptor : public ConstVectorOfPtrsAdaptor<PrintObject> {
+ friend Print;
+ ConstPrintObjectPtrsAdaptor(const PrintObjectPtrs *data) : ConstVectorOfPtrsAdaptor<PrintObject>(data) {}
+};
+
+typedef std::vector<PrintRegion*> PrintRegionPtrs;
+typedef std::vector<const PrintRegion*> ConstPrintRegionPtrs;
+class ConstPrintRegionPtrsAdaptor : public ConstVectorOfPtrsAdaptor<PrintRegion> {
+ friend Print;
+ ConstPrintRegionPtrsAdaptor(const PrintRegionPtrs *data) : ConstVectorOfPtrsAdaptor<PrintRegion>(data) {}
+};
// The complete print tray with possibly multiple objects.
class Print : public PrintBaseWithState<PrintStep, psCount>
{
private: // Prevents erroneous use by other classes.
typedef PrintBaseWithState<PrintStep, psCount> Inherited;
+ // Bool indicates if supports of PrintObject are top-level contour.
+ typedef std::pair<PrintObject *, bool> 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<PrintObject*>(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<t_config_option_key> &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<typename PrintStepEnum, const size_t COUNT>
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<BrimType>::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<BrimType>(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<PrinterTechnology>::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<SLAPillarConnecti
return keys_map;
}
+template<> inline const t_config_enum_values& ConfigOptionEnum<BrimType>::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<BrimType> 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<const t_layer_height_range, ModelConfig> &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<coordf_t> &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 &region_config, std::vector<unsigned int> &object_extruders)
+void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig &region_config, const bool has_brim, std::vector<unsigned int> &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<unsigned int> &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<typename T, typename FN_HIGHER_EQUAL>
-size_t idx_higher_or_equal(const std::vector<T*> &vec, size_t idx, FN_HIGHER_EQUAL fn_higher_equal)
+template<typename IT, typename FN_HIGHER_EQUAL>
+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<typename T, typename FN_HIGHER_EQUAL>
+size_t idx_higher_or_equal(const std::vector<T>& 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<typename T, typename FN_LOWER_EQUAL>
-int idx_lower_or_equal(const std::vector<T*> &vec, int idx, FN_LOWER_EQUAL fn_lower_equal)
+template<typename IT, typename FN_LOWER_EQUAL>
+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<typename T, typename FN_LOWER_EQUAL>
+int idx_lower_or_equal(const std::vector<T*> &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<size_t>(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<size_t>& 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<LayerCacheItem> overlaps;
};
- std::vector<LayerCache> layer_caches(object.support_layers().size(), LayerCache());
+ std::vector<LayerCache> layer_caches(support_layers.size(), LayerCache());
- tbb::parallel_for(tbb::blocked_range<size_t>(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<size_t>(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<size_t>& 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<size_t>(n_raft_layers, object.support_layers().size()),
- [this, &object, &layer_caches]
+ tbb::parallel_for(tbb::blocked_range<size_t>(n_raft_layers, support_layers.size()),
+ [this, &support_layers, &layer_caches]
(const tbb::blocked_range<size_t>& 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<BrimType>("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<SLAPillarConnectionMode>(ret_enum);
else if (m_opt_id == "printhost_authorization_type")
m_value = static_cast<AuthorizationType>(ret_enum);
+ else if (m_opt_id == "brim_type")
+ m_value = static_cast<BrimType>(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<size_t>(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<SLAPillarConnectionMode>(boost::any_cast<SLAPillarConnectionMode>(value)));
else if(opt_key == "printhost_authorization_type")
config.set_key_value(opt_key, new ConfigOptionEnum<AuthorizationType>(boost::any_cast<AuthorizationType>(value)));
+ else if(opt_key == "brim_type")
+ config.set_key_value(opt_key, new ConfigOptionEnum<BrimType>(boost::any_cast<BrimType>(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<int>(config.option<ConfigOptionEnum<AuthorizationType>>(opt_key)->value);
}
+ else if (opt_key == "brim_type") {
+ ret = static_cast<int>(config.option<ConfigOptionEnum<BrimType>>(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<SLADisplayOrientation>(opt_key, config);
if (opt_key == "support_pillar_connection_mode")
return get_string_from_enum<SLAPillarConnectionMode>(opt_key, config);
+ if (opt_key == "brim_type")
+ return get_string_from_enum<BrimType>(opt_key, config);
break;
}
case coPoints: {