Squash merge of lh_brim_rework,

brim separated to Brim.cpp,hpp
Refactored accessors to PrintObjectPtrs, PrintRegionPtrs, LayerPtrs,
SupportLayerPtrs for const correctness.
This commit is contained in:
Vojtech Bubnik 2021-02-03 15:12:53 +01:00
parent e52efe48b0
commit 73c9f939e0
37 changed files with 803 additions and 243 deletions

490
src/libslic3r/Brim.cpp Normal file
View file

@ -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

14
src/libslic3r/Brim.hpp Normal file
View file

@ -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_

View file

@ -21,6 +21,8 @@ add_library(libslic3r STATIC
BoundingBox.hpp BoundingBox.hpp
BridgeDetector.cpp BridgeDetector.cpp
BridgeDetector.hpp BridgeDetector.hpp
Brim.cpp
Brim.hpp
ClipperUtils.cpp ClipperUtils.cpp
ClipperUtils.hpp ClipperUtils.hpp
Config.cpp Config.cpp

View file

@ -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) { 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); } 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> 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) 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; } bool opt_bool(const t_config_option_key &opt_key, unsigned int idx) const { return this->option<ConfigOptionBools>(opt_key)->get_at(idx) != 0; }

View file

@ -80,6 +80,13 @@ bool ExPolygon::is_valid() const
return true; 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 bool ExPolygon::contains(const Line &line) const
{ {
return this->contains(Polyline(line.a, line.b)); return this->contains(Polyline(line.a, line.b));

View file

@ -49,6 +49,7 @@ public:
double area() const; double area() const;
bool empty() const { return contour.points.empty(); } bool empty() const { return contour.points.empty(); }
bool is_valid() const; bool is_valid() const;
void douglas_peucker(double tolerance);
// Contains the line / polyline / polylines etc COMPLETELY. // Contains the line / polyline / polylines etc COMPLETELY.
bool contains(const Line &line) const; bool contains(const Line &line) const;
@ -249,6 +250,24 @@ inline Polygons to_polygons(ExPolygons &&src)
return polygons; 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) inline void polygons_append(Polygons &dst, const ExPolygon &src)
{ {
dst.reserve(dst.size() + src.holes.size() + 1); dst.reserve(dst.size() + src.holes.size() + 1);

View file

@ -343,6 +343,25 @@ inline void extrusion_entities_append_loops(ExtrusionEntitiesPtr &dst, Polygons
loops.clear(); 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 #endif

View file

@ -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. // Just a reminder: A spiral vase mode is allowed for a single object, single material print only.
m_enable_loop_clipping = true; m_enable_loop_clipping = true;
if (m_spiral_vase && layers.size() == 1 && support_layer == nullptr) { 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) { if (enable) {
for (const LayerRegion *layer_region : layer.regions()) for (const LayerRegion *layer_region : layer.regions())
if (size_t(layer_region->region()->config().bottom_solid_layers.value) > layer.id() || if (size_t(layer_region->region()->config().bottom_solid_layers.value) > layer.id() ||

View file

@ -33,7 +33,7 @@ class GCode;
namespace { struct Item; } namespace { struct Item; }
struct PrintInstance; struct PrintInstance;
using PrintObjectPtrs = std::vector<PrintObject*>; class ConstPrintObjectPtrsAdaptor;
class OozePrevention { class OozePrevention {
public: public:

View file

@ -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 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: // 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; }); 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 // We will now iterate through

View file

@ -604,7 +604,7 @@ void PerimeterGenerator::process()
// we continue inwards after having finished the brim // we continue inwards after having finished the brim
// TODO: add test for perimeter order // TODO: add test for perimeter order
if (this->config->external_perimeters_first || 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(); entities.reverse();
// append perimeters for this slice as a collection // append perimeters for this slice as a collection
if (! entities.empty()) if (! entities.empty())

View file

@ -96,6 +96,14 @@ bool Polygon::make_clockwise()
return false; 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? // Does an unoriented polygon contain a point?
// Tested by counting intersections along a horizontal line. // Tested by counting intersections along a horizontal line.
bool Polygon::contains(const Point &point) const bool Polygon::contains(const Point &point) const

View file

@ -55,6 +55,7 @@ public:
bool make_counter_clockwise(); bool make_counter_clockwise();
bool make_clockwise(); bool make_clockwise();
bool is_valid() const { return this->points.size() >= 3; } bool is_valid() const { return this->points.size() >= 3; }
void douglas_peucker(double tolerance);
// Does an unoriented polygon contain a point? // Does an unoriented polygon contain a point?
// Tested by counting intersections along a horizontal line. // Tested by counting intersections along a horizontal line.

View file

@ -75,6 +75,7 @@ public:
template <class T> void simplify_by_visibility(const T &area); template <class T> void simplify_by_visibility(const T &area);
void split_at(const Point &point, Polyline* p1, Polyline* p2) const; void split_at(const Point &point, Polyline* p1, Polyline* p2) const;
bool is_straight() 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! // Don't use this class in production code, it is used exclusively by the Perl binding for unit tests!

View file

@ -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", "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_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", "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", "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_synchronize_layers", "support_material_angle", "support_material_interface_layers",
"support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance", "support_material_interface_spacing", "support_material_interface_contact_loops", "support_material_contact_distance",

View file

@ -1,8 +1,7 @@
#include "clipper/clipper_z.hpp"
#include "Exception.hpp" #include "Exception.hpp"
#include "Print.hpp" #include "Print.hpp"
#include "BoundingBox.hpp" #include "BoundingBox.hpp"
#include "Brim.hpp"
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "Extruder.hpp" #include "Extruder.hpp"
#include "Flow.hpp" #include "Flow.hpp"
@ -15,8 +14,6 @@
#include "GCode/WipeTower.hpp" #include "GCode/WipeTower.hpp"
#include "Utils.hpp" #include "Utils.hpp"
//#include "PrintExport.hpp"
#include <float.h> #include <float.h>
#include <algorithm> #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_y"
|| opt_key == "wipe_tower_rotation_angle") { || opt_key == "wipe_tower_rotation_angle") {
steps.emplace_back(psSkirt); 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(psBrim);
steps.emplace_back(psSkirt); steps.emplace_back(psSkirt);
} else if ( } else if (
@ -797,7 +797,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
} }
if (deleted_any) { if (deleted_any) {
// Delete PrintObjects of the deleted ModelObjects. // 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.clear();
m_objects.reserve(print_objects_old.size()); m_objects.reserve(print_objects_old.size());
for (PrintObject *print_object : print_objects_old) { 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. // 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())); print_objects_new.reserve(std::max(m_objects.size(), m_model.objects.size()));
bool new_objects = false; bool new_objects = false;
// Walk over all new model objects and check, whether there are matching PrintObjects. // 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(); 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) static inline bool sequential_print_horizontal_clearance_valid(const Print &print)
{ {
Polygons convex_hulls_other; Polygons convex_hulls_other;
@ -1655,9 +1661,12 @@ void Print::process()
if (this->set_started(psBrim)) { if (this->set_started(psBrim)) {
m_brim.clear(); m_brim.clear();
m_first_layer_convex_hull.points.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->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 // 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. // 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)); 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 Print::first_layer_islands() const
{ {
Polygons islands; Polygons islands;
@ -2104,8 +1955,8 @@ void Print::_make_wipe_tower()
if (idx_begin != size_t(-1)) { if (idx_begin != size_t(-1)) {
// Find the position in m_objects.first()->support_layers to insert these new support layers. // 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; 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(); auto it_layer = m_objects.front()->support_layers().begin();
SupportLayerPtrs::const_iterator it_end = m_objects.front()->support_layers().end(); 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); 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. // 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) { for (size_t i = idx_begin; i < idx_end; ++ i) {

View file

@ -73,7 +73,7 @@ public:
// Collect 0-based extruder indices used to print this region's object. // Collect 0-based extruder indices used to print this region's object.
void collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const; 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: // Methods modifying the PrintRegion's state:
public: public:
@ -95,9 +95,39 @@ private:
~PrintRegion() = default; ~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<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<SupportLayer*> SupportLayerPtrs;
typedef std::vector<const SupportLayer*> ConstSupportLayerPtrs;
class ConstSupportLayerPtrsAdaptor : public ConstVectorOfPtrsAdaptor<SupportLayer> {
friend PrintObject;
ConstSupportLayerPtrsAdaptor(const SupportLayerPtrs *data) : ConstVectorOfPtrsAdaptor<SupportLayer>(data) {}
};
class BoundingBoxf3; // TODO: for temporary constructor parameter class BoundingBoxf3; // TODO: for temporary constructor parameter
// Single instance of a PrintObject. // Single instance of a PrintObject.
@ -127,11 +157,15 @@ public:
// Size of an object: XYZ in scaled coordinates. The size might not be quite snug in XY plane. // 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 Vec3crd& size() const { return m_size; }
const PrintObjectConfig& config() const { return m_config; } const PrintObjectConfig& config() const { return m_config; }
const LayerPtrs& layers() const { return m_layers; } ConstLayerPtrsAdaptor layers() const { return ConstLayerPtrsAdaptor(&m_layers); }
const SupportLayerPtrs& support_layers() const { return m_support_layers; } ConstSupportLayerPtrsAdaptor support_layers() const { return ConstSupportLayerPtrsAdaptor(&m_support_layers); }
const Transform3d& trafo() const { return m_trafo; } const Transform3d& trafo() const { return m_trafo; }
const PrintInstances& instances() const { return m_instances; } 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. // 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. // The bounding box may not be quite snug.
BoundingBox bounding_box() const { return BoundingBox(Point(- m_size.x() / 2, - m_size.y() / 2), Point(m_size.x() / 2, m_size.y() / 2)); } BoundingBox bounding_box() const { return BoundingBox(Point(- m_size.x() / 2, - m_size.y() / 2), Point(m_size.x() / 2, m_size.y() / 2)); }
@ -171,7 +205,7 @@ public:
void clear_support_layers(); void clear_support_layers();
SupportLayer* get_support_layer(int idx) { return m_support_layers[idx]; } SupportLayer* get_support_layer(int idx) { return m_support_layers[idx]; }
SupportLayer* add_support_layer(int id, coordf_t height, coordf_t print_z); 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); 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. // Initialize the layer_height_profile from the model_object's layer_height_profile, from model_object's layer height table, or from slicing parameters.
@ -345,13 +379,26 @@ struct PrintStatistics
}; };
typedef std::vector<PrintObject*> PrintObjectPtrs; 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<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. // The complete print tray with possibly multiple objects.
class Print : public PrintBaseWithState<PrintStep, psCount> class Print : public PrintBaseWithState<PrintStep, psCount>
{ {
private: // Prevents erroneous use by other classes. private: // Prevents erroneous use by other classes.
typedef PrintBaseWithState<PrintStep, psCount> Inherited; typedef PrintBaseWithState<PrintStep, psCount> Inherited;
// Bool indicates if supports of PrintObject are top-level contour.
typedef std::pair<PrintObject *, bool> PrintObjectInfo;
public: public:
Print() = default; Print() = default;
@ -385,6 +432,7 @@ public:
bool has_infinite_skirt() const; bool has_infinite_skirt() const;
bool has_skirt() const; bool has_skirt() const;
bool has_brim() const;
// Returns an empty string if valid, otherwise returns an error message. // Returns an empty string if valid, otherwise returns an error message.
std::string validate() const override; std::string validate() const override;
@ -403,9 +451,8 @@ public:
const PrintConfig& config() const { return m_config; } const PrintConfig& config() const { return m_config; }
const PrintObjectConfig& default_object_config() const { return m_default_object_config; } const PrintObjectConfig& default_object_config() const { return m_default_object_config; }
const PrintRegionConfig& default_region_config() const { return m_default_region_config; } const PrintRegionConfig& default_region_config() const { return m_default_region_config; }
//FIXME returning const vector to non-const PrintObject*, caller could modify PrintObjects! ConstPrintObjectPtrsAdaptor objects() const { return ConstPrintObjectPtrsAdaptor(&m_objects); }
const PrintObjectPtrs& objects() const { return m_objects; } PrintObject* get_object(size_t idx) { return const_cast<PrintObject*>(m_objects[idx]); }
PrintObject* get_object(size_t idx) { return m_objects[idx]; }
const PrintObject* get_object(size_t idx) const { return 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 // PrintObject by its ObjectID, to be used to uniquely bind slicing warnings to their source PrintObjects
// in the notification center. // in the notification center.
@ -414,11 +461,15 @@ public:
[object_id](const PrintObject *obj) { return obj->id() == object_id; }); [object_id](const PrintObject *obj) { return obj->id() == object_id; });
return (it == m_objects.end()) ? nullptr : *it; 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? // 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. // If zero, then the print is empty and the print shall not be executed.
unsigned int num_object_instances() const; 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& skirt() const { return m_skirt; }
const ExtrusionEntityCollection& brim() const { return m_brim; } const ExtrusionEntityCollection& brim() const { return m_brim; }
// Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line. // 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); bool invalidate_state_by_config_options(const std::vector<t_config_option_key> &opt_keys);
void _make_skirt(); void _make_skirt();
void _make_brim();
void _make_wipe_tower(); void _make_wipe_tower();
void finalize_first_layer_convex_hull(); void finalize_first_layer_convex_hull();

View file

@ -13,6 +13,11 @@
namespace Slic3r namespace Slic3r
{ {
void PrintTryCancel::operator()()
{
m_print->throw_if_canceled();
}
size_t PrintStateBase::g_last_timestamp = 0; size_t PrintStateBase::g_last_timestamp = 0;
// Update "scale", "input_filename", "input_filename_base" placeholders from the current m_objects. // Update "scale", "input_filename", "input_filename_base" placeholders from the current m_objects.

View file

@ -324,6 +324,20 @@ protected:
ModelObject *m_model_object; 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. * @brief Printing involves slicing and export of device dependent instructions.
* *
@ -472,6 +486,8 @@ protected:
// If the background processing stop was requested, throw CanceledException. // 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. // 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(); } 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. // 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; 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 // The mutex will be used to guard the worker thread against entering a stage
// while the data influencing the stage is modified. // while the data influencing the stage is modified.
mutable tbb::mutex m_state_mutex; mutable tbb::mutex m_state_mutex;
friend PrintTryCancel;
}; };
template<typename PrintStepEnum, const size_t COUNT> template<typename PrintStepEnum, const size_t COUNT>

View file

@ -298,12 +298,37 @@ void PrintConfigDef::init_fff_params()
def = this->add("brim_width", coFloat); def = this->add("brim_width", coFloat);
def->label = L("Brim width"); 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->tooltip = L("Horizontal width of the brim that will be printed around each object on the first layer.");
def->sidetext = L("mm"); def->sidetext = L("mm");
def->min = 0; def->min = 0;
def->mode = comSimple; def->mode = comSimple;
def->set_default_value(new ConfigOptionFloat(0)); 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 = this->add("clip_multipart_objects", coBool);
def->label = L("Clip multi-part objects"); def->label = L("Clip multi-part objects");
def->tooltip = L("When printing multi-material objects, this settings will make Slic3r " def->tooltip = L("When printing multi-material objects, this settings will make Slic3r "

View file

@ -103,6 +103,13 @@ enum SLAPillarConnectionMode {
slapcmDynamic slapcmDynamic
}; };
enum BrimType {
btNoBrim,
btOuterOnly,
btInnerOnly,
btOuterAndInner,
};
template<> inline const t_config_enum_values& ConfigOptionEnum<PrinterTechnology>::get_enum_values() { template<> inline const t_config_enum_values& ConfigOptionEnum<PrinterTechnology>::get_enum_values() {
static t_config_enum_values keys_map; static t_config_enum_values keys_map;
if (keys_map.empty()) { if (keys_map.empty()) {
@ -264,6 +271,17 @@ template<> inline const t_config_enum_values& ConfigOptionEnum<SLAPillarConnecti
return keys_map; 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. // 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. // Does not store the actual values, but defines default values.
class PrintConfigDef : public ConfigDef class PrintConfigDef : public ConfigDef
@ -477,6 +495,9 @@ class PrintObjectConfig : public StaticPrintConfig
{ {
STATIC_PRINT_CONFIG_CACHE(PrintObjectConfig) STATIC_PRINT_CONFIG_CACHE(PrintObjectConfig)
public: public:
ConfigOptionFloat brim_offset;
ConfigOptionEnum<BrimType> brim_type;
ConfigOptionFloat brim_width;
ConfigOptionBool clip_multipart_objects; ConfigOptionBool clip_multipart_objects;
ConfigOptionBool dont_support_bridges; ConfigOptionBool dont_support_bridges;
ConfigOptionFloat elefant_foot_compensation; ConfigOptionFloat elefant_foot_compensation;
@ -526,6 +547,9 @@ public:
protected: protected:
void initialize(StaticCacheBase &cache, const char *base_ptr) 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(clip_multipart_objects);
OPT_PTR(dont_support_bridges); OPT_PTR(dont_support_bridges);
OPT_PTR(elefant_foot_compensation); OPT_PTR(elefant_foot_compensation);
@ -891,7 +915,6 @@ public:
ConfigOptionInts bed_temperature; ConfigOptionInts bed_temperature;
ConfigOptionFloat bridge_acceleration; ConfigOptionFloat bridge_acceleration;
ConfigOptionInts bridge_fan_speed; ConfigOptionInts bridge_fan_speed;
ConfigOptionFloat brim_width;
ConfigOptionBool complete_objects; ConfigOptionBool complete_objects;
ConfigOptionFloats colorprint_heights; ConfigOptionFloats colorprint_heights;
ConfigOptionBools cooling; ConfigOptionBools cooling;
@ -966,7 +989,6 @@ protected:
OPT_PTR(bed_temperature); OPT_PTR(bed_temperature);
OPT_PTR(bridge_acceleration); OPT_PTR(bridge_acceleration);
OPT_PTR(bridge_fan_speed); OPT_PTR(bridge_fan_speed);
OPT_PTR(brim_width);
OPT_PTR(complete_objects); OPT_PTR(complete_objects);
OPT_PTR(colorprint_heights); OPT_PTR(colorprint_heights);
OPT_PTR(cooling); OPT_PTR(cooling);

View file

@ -501,7 +501,7 @@ SupportLayer* PrintObject::add_support_layer(int id, coordf_t height, coordf_t p
return m_support_layers.back(); 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)); 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( PrintRegion::collect_object_printing_extruders(
print_config, print_config,
region_config_from_model_volume(default_region_config, nullptr, *model_volume, num_extruders), 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); object_extruders);
for (const std::pair<const t_layer_height_range, ModelConfig> &range_and_config : model_object.layer_config_ranges) 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") || if (range_and_config.second.has("perimeter_extruder") ||
@ -1636,6 +1637,7 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig& full
PrintRegion::collect_object_printing_extruders( PrintRegion::collect_object_printing_extruders(
print_config, print_config,
region_config_from_model_volume(default_region_config, &range_and_config.second.get(), *model_volume, num_extruders), 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); object_extruders);
} }
sort_remove_duplicates(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. // 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) 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; prev = layer;
} }
} }

View file

@ -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); 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. // 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(); 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); int i = std::max(0, extruder_id - 1);
object_extruders.emplace_back((i >= num_extruders) ? 0 : i); 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); emplace_extruder(region_config.perimeter_extruder);
if (region_config.fill_density.value > 0) if (region_config.fill_density.value > 0)
emplace_extruder(region_config.infill_extruder); 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().infill_extruder <= num_extruders);
assert(this->config().solid_infill_extruder <= num_extruders); assert(this->config().solid_infill_extruder <= num_extruders);
#endif #endif
collect_object_printing_extruders(print()->config(), this->config(), object_extruders); collect_object_printing_extruders(print()->config(), this->config(), print()->has_brim(), object_extruders);
} }
} }

View file

@ -388,7 +388,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
BOOST_LOG_TRIVIAL(info) << "Support generator - Generating tool paths"; BOOST_LOG_TRIVIAL(info) << "Support generator - Generating tool paths";
// Generate the actual toolpaths and save them into each layer. // 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 #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 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. // If the initial idx is size_t(-1), then use binary search.
// Otherwise search linearly upwards. // Otherwise search linearly upwards.
template<typename T, typename FN_HIGHER_EQUAL> template<typename IT, typename FN_HIGHER_EQUAL>
size_t idx_higher_or_equal(const std::vector<T*> &vec, size_t idx, FN_HIGHER_EQUAL 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; idx = 0;
} else if (idx == size_t(-1)) { } else if (idx == size_t(-1)) {
// First of the batch of layers per thread pool invocation. Use binary search. // First of the batch of layers per thread pool invocation. Use binary search.
int idx_low = 0; 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) { while (idx_low + 1 < idx_high) {
int idx_mid = (idx_low + idx_high) / 2; 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; idx_high = idx_mid;
else else
idx_low = idx_mid; idx_low = idx_mid;
} }
idx = fn_higher_equal(vec[idx_low]) ? idx_low : idx = fn_higher_equal(begin[idx_low]) ? idx_low :
(fn_higher_equal(vec[idx_high]) ? idx_high : vec.size()); (fn_higher_equal(begin[idx_high]) ? idx_high : size);
} else { } else {
// For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search. // 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; ++ idx;
} }
return 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. // 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. // 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 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. // If the initial idx is < -1, then use binary search.
// Otherwise search linearly downwards. // Otherwise search linearly downwards.
template<typename T, typename FN_LOWER_EQUAL> template<typename IT, typename FN_LOWER_EQUAL>
int idx_lower_or_equal(const std::vector<T*> &vec, int idx, FN_LOWER_EQUAL 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; idx = -1;
} else if (idx < -1) { } else if (idx < -1) {
// First of the batch of layers per thread pool invocation. Use binary search. // First of the batch of layers per thread pool invocation. Use binary search.
int idx_low = 0; 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) { while (idx_low + 1 < idx_high) {
int idx_mid = (idx_low + idx_high) / 2; 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; idx_low = idx_mid;
else else
idx_high = idx_mid; idx_high = idx_mid;
} }
idx = fn_lower_equal(vec[idx_high]) ? idx_high : idx = fn_lower_equal(begin[idx_high]) ? idx_high :
(fn_lower_equal(vec[idx_low ]) ? idx_low : -1); (fn_lower_equal(begin[idx_low ]) ? idx_low : -1);
} else { } else {
// For the other layers of this batch of layers, search incrementally, which is cheaper than the binary search. // 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; -- idx;
} }
return 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. // 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( void PrintObjectSupportMaterial::trim_top_contacts_by_bottom_contacts(
@ -1972,7 +1984,7 @@ void PrintObjectSupportMaterial::generate_base_layers(
Polygons polygons_new; Polygons polygons_new;
// Use the precomputed layer_support_areas. // 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; })); [&layer_intermediate](const Layer *layer){ return layer->print_z <= layer_intermediate.print_z + EPSILON; }));
polygons_new = layer_support_areas[idx_object_layer_above]; 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. // 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; coordf_t z_threshold = support_layer.print_z - support_layer.height - gap_extra_below + EPSILON;
idx_object_layer_overlapping = idx_higher_or_equal( 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; }); [z_threshold](const Layer *layer){ return layer->print_z >= z_threshold; });
// Collect all the object layers intersecting with this layer. // Collect all the object layers intersecting with this layer.
Polygons polygons_trimming; Polygons polygons_trimming;
@ -2931,7 +2943,7 @@ void modulate_extrusion_by_overlapping_layers(
} }
void PrintObjectSupportMaterial::generate_toolpaths( void PrintObjectSupportMaterial::generate_toolpaths(
const PrintObject &object, SupportLayerPtrs &support_layers,
const MyLayersPtr &raft_layers, const MyLayersPtr &raft_layers,
const MyLayersPtr &bottom_contacts, const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts, const MyLayersPtr &top_contacts,
@ -3000,13 +3012,13 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// Insert the raft base layers. // Insert the raft base layers.
size_t n_raft_layers = size_t(std::max(0, int(m_slicing_params.raft_layers()) - 1)); 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), 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] 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) { (const tbb::blocked_range<size_t>& range) {
for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id)
{ {
assert(support_layer_id < raft_layers.size()); 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()); assert(support_layer.support_fills.entities.empty());
MyLayer &raft_layer = *raft_layers[support_layer_id]; MyLayer &raft_layer = *raft_layers[support_layer_id];
@ -3102,10 +3114,10 @@ void PrintObjectSupportMaterial::generate_toolpaths(
MyLayerExtruded interface_layer; MyLayerExtruded interface_layer;
std::vector<LayerCacheItem> overlaps; 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()), tbb::parallel_for(tbb::blocked_range<size_t>(n_raft_layers, support_layers.size()),
[this, &object, &bottom_contacts, &top_contacts, &intermediate_layers, &interface_layers, &layer_caches, &loop_interface_processor, [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] infill_pattern, &bbox_object, support_density, interface_density, interface_angle, &angles, link_max_length_factor, with_sheath]
(const tbb::blocked_range<size_t>& range) { (const tbb::blocked_range<size_t>& range) {
// Indices of the 1st layer in their respective container at the support layer height. // 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); filler_support->set_bounding_box(bbox_object);
for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) 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]; LayerCache &layer_cache = layer_caches[support_layer_id];
// Find polygons with the same print_z. // Find polygons with the same print_z.
@ -3317,11 +3329,11 @@ void PrintObjectSupportMaterial::generate_toolpaths(
}); });
// Now modulate the support layer height in parallel. // Now modulate the support layer height in parallel.
tbb::parallel_for(tbb::blocked_range<size_t>(n_raft_layers, object.support_layers().size()), tbb::parallel_for(tbb::blocked_range<size_t>(n_raft_layers, support_layers.size()),
[this, &object, &layer_caches] [this, &support_layers, &layer_caches]
(const tbb::blocked_range<size_t>& range) { (const tbb::blocked_range<size_t>& range) {
for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) { 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]; LayerCache &layer_cache = layer_caches[support_layer_id];
for (LayerCacheItem &layer_cache_item : layer_cache.overlaps) { 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); modulate_extrusion_by_overlapping_layers(layer_cache_item.layer_extruded->extrusions, *layer_cache_item.layer_extruded->layer, layer_cache_item.overlapping);

View file

@ -224,7 +224,7 @@ private:
// Produce the actual G-code. // Produce the actual G-code.
void generate_toolpaths( void generate_toolpaths(
const PrintObject &object, SupportLayerPtrs &support_layers,
const MyLayersPtr &raft_layers, const MyLayersPtr &raft_layers,
const MyLayersPtr &bottom_contacts, const MyLayersPtr &bottom_contacts,
const MyLayersPtr &top_contacts, const MyLayersPtr &top_contacts,

View file

@ -267,7 +267,9 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
for (auto el : { "skirt_distance", "draft_shield", "min_skirt_length" }) for (auto el : { "skirt_distance", "draft_shield", "min_skirt_length" })
toggle_field(el, have_skirt); 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() // perimeter_extruder uses the same logic as in Print::extruders()
toggle_field("perimeter_extruder", have_perimeters || have_brim); toggle_field("perimeter_extruder", have_perimeters || have_brim);

View file

@ -1220,6 +1220,8 @@ boost::any& Choice::get_value()
m_value = static_cast<SLAPillarConnectionMode>(ret_enum); m_value = static_cast<SLAPillarConnectionMode>(ret_enum);
else if (m_opt_id == "printhost_authorization_type") else if (m_opt_id == "printhost_authorization_type")
m_value = static_cast<AuthorizationType>(ret_enum); 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") { else if (m_opt.gui_type == "f_enum_open") {
const int ret_enum = field->GetSelection(); const int ret_enum = field->GetSelection();

View file

@ -5680,7 +5680,7 @@ void GLCanvas3D::_load_print_toolpaths()
if (!print->is_step_done(psSkirt) || !print->is_step_done(psBrim)) if (!print->is_step_done(psSkirt) || !print->is_step_done(psBrim))
return; return;
if (!print->has_skirt() && (print->config().brim_width.value == 0)) if (!print->has_skirt() && !print->has_brim())
return; return;
const float color[] = { 0.5f, 1.0f, 0.5f, 1.0f }; // greenish 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()); 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); 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; skirt_height = 1;
// Get first skirt_height layers. // Get first skirt_height layers.

View file

@ -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))); config.set_key_value(opt_key, new ConfigOptionEnum<SLAPillarConnectionMode>(boost::any_cast<SLAPillarConnectionMode>(value)));
else if(opt_key == "printhost_authorization_type") else if(opt_key == "printhost_authorization_type")
config.set_key_value(opt_key, new ConfigOptionEnum<AuthorizationType>(boost::any_cast<AuthorizationType>(value))); 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; break;
case coPoints:{ case coPoints:{

View file

@ -97,7 +97,7 @@ ObjectList::ObjectList(wxWindow* parent) :
CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel"); CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel");
CATEGORY_ICON[L("Extrusion Width")] = 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("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("Speed > Acceleration")] = create_scaled_bitmap("time");
CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap("wrench"); CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap("wrench");
// ptSLA // ptSLA
@ -686,7 +686,7 @@ void ObjectList::msw_rescale_icons()
CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel"); CATEGORY_ICON[L("Extruders")] = create_scaled_bitmap("funnel");
CATEGORY_ICON[L("Extrusion Width")] = 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("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("Speed > Acceleration")] = create_scaled_bitmap("time");
CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap("wrench"); CATEGORY_ICON[L("Advanced")] = create_scaled_bitmap("wrench");
// ptSLA // ptSLA

View file

@ -899,6 +899,9 @@ boost::any ConfigOptionsGroup::get_config_value(const DynamicPrintConfig& config
else if (opt_key == "printhost_authorization_type") { else if (opt_key == "printhost_authorization_type") {
ret = static_cast<int>(config.option<ConfigOptionEnum<AuthorizationType>>(opt_key)->value); 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; break;
case coPoints: case coPoints:

View file

@ -1924,7 +1924,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
, main_frame(main_frame) , main_frame(main_frame)
, config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({ , 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", "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", "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", "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. // These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor.

View file

@ -1479,7 +1479,9 @@ void TabPrint::build()
optgroup->append_single_option_line("min_skirt_length", category_path + "skirt"); optgroup->append_single_option_line("min_skirt_length", category_path + "skirt");
optgroup = page->new_optgroup(L("Brim")); 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_width", category_path + "brim");
optgroup->append_single_option_line("brim_offset", category_path + "brim");
page = add_options_page(L("Support material"), "support"); page = add_options_page(L("Support material"), "support");
category_path = "support-material_1698#"; category_path = "support-material_1698#";

View file

@ -966,6 +966,8 @@ static wxString get_string_value(std::string opt_key, const DynamicPrintConfig&
return get_string_from_enum<SLADisplayOrientation>(opt_key, config); return get_string_from_enum<SLADisplayOrientation>(opt_key, config);
if (opt_key == "support_pillar_connection_mode") if (opt_key == "support_pillar_connection_mode")
return get_string_from_enum<SLAPillarConnectionMode>(opt_key, config); return get_string_from_enum<SLAPillarConnectionMode>(opt_key, config);
if (opt_key == "brim_type")
return get_string_from_enum<BrimType>(opt_key, config);
break; break;
} }
case coPoints: { case coPoints: {

View file

@ -18,7 +18,7 @@ SCENARIO("PrintObject: object layer heights", "[PrintObject]") {
{ "layer_height", 2 }, { "layer_height", 2 },
{ "nozzle_diameter", 3 } { "nozzle_diameter", 3 }
}); });
const std::vector<Slic3r::Layer*> &layers = print.objects().front()->layers(); ConstLayerPtrsAdaptor layers = print.objects().front()->layers();
THEN("The output vector has 10 entries") { THEN("The output vector has 10 entries") {
REQUIRE(layers.size() == 10); REQUIRE(layers.size() == 10);
} }
@ -37,7 +37,7 @@ SCENARIO("PrintObject: object layer heights", "[PrintObject]") {
{ "layer_height", 10 }, { "layer_height", 10 },
{ "nozzle_diameter", 11 } { "nozzle_diameter", 11 }
}); });
const std::vector<Slic3r::Layer*> &layers = print.objects().front()->layers(); ConstLayerPtrsAdaptor layers = print.objects().front()->layers();
THEN("The output vector has 3 entries") { THEN("The output vector has 3 entries") {
REQUIRE(layers.size() == 3); REQUIRE(layers.size() == 3);
} }
@ -55,7 +55,7 @@ SCENARIO("PrintObject: object layer heights", "[PrintObject]") {
{ "layer_height", 15 }, { "layer_height", 15 },
{ "nozzle_diameter", 16 } { "nozzle_diameter", 16 }
}); });
const std::vector<Slic3r::Layer*> &layers = print.objects().front()->layers(); ConstLayerPtrsAdaptor layers = print.objects().front()->layers();
THEN("The output vector has 2 entries") { THEN("The output vector has 2 entries") {
REQUIRE(layers.size() == 2); REQUIRE(layers.size() == 2);
} }

View file

@ -27,7 +27,7 @@ SCENARIO("SupportMaterial: support_layers_z and contact_distance", "[SupportMate
auto check = [](Slic3r::Print &print, bool &first_support_layer_height_ok, bool &layer_height_minimum_ok, bool &layer_height_maximum_ok, bool &top_spacing_ok) auto check = [](Slic3r::Print &print, bool &first_support_layer_height_ok, bool &layer_height_minimum_ok, bool &layer_height_maximum_ok, bool &top_spacing_ok)
{ {
const std::vector<Slic3r::SupportLayer*> &support_layers = print.objects().front()->support_layers(); ConstSupportLayerPtrsAdaptor support_layers = print.objects().front()->support_layers();
first_support_layer_height_ok = support_layers.front()->print_z == print.default_object_config().first_layer_height.value; first_support_layer_height_ok = support_layers.front()->print_z == print.default_object_config().first_layer_height.value;

View file

@ -95,16 +95,16 @@ _constant()
int wipe_tower_number_of_toolchanges() int wipe_tower_number_of_toolchanges()
%code%{ RETVAL = THIS->wipe_tower_data().number_of_toolchanges; %}; %code%{ RETVAL = THIS->wipe_tower_data().number_of_toolchanges; %};
PrintObjectPtrs* objects() PrintObjectPtrs* objects()
%code%{ RETVAL = const_cast<PrintObjectPtrs*>(&THIS->objects()); %}; %code%{ RETVAL = const_cast<PrintObjectPtrs*>(&THIS->objects_mutable()); %};
Ref<PrintObject> get_object(int idx) Ref<PrintObject> get_object(int idx)
%code%{ RETVAL = THIS->objects()[idx]; %}; %code%{ RETVAL = THIS->objects_mutable()[idx]; %};
size_t object_count() size_t object_count()
%code%{ RETVAL = THIS->objects().size(); %}; %code%{ RETVAL = THIS->objects().size(); %};
PrintRegionPtrs* regions() PrintRegionPtrs* regions()
%code%{ RETVAL = const_cast<PrintRegionPtrs*>(&THIS->regions()); %}; %code%{ RETVAL = const_cast<PrintRegionPtrs*>(&THIS->regions_mutable()); %};
Ref<PrintRegion> get_region(int idx) Ref<PrintRegion> get_region(int idx)
%code%{ RETVAL = THIS->regions()[idx]; %}; %code%{ RETVAL = THIS->regions_mutable()[idx]; %};
size_t region_count() size_t region_count()
%code%{ RETVAL = THIS->regions().size(); %}; %code%{ RETVAL = THIS->regions().size(); %};