mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-14 10:17:55 -06:00

1. first formal version of macos 2. add the bambu networking plugin install logic 3. auto compute the wipe volume when filament change 4. add the logic of wiping into support 5. refine the GUI layout and icons, improve the gui apperance in lots of small places 6. serveral improve to support 7. support AMS auto-mapping 8. disable lots of unstable features: such as params table, media file download, HMS 9. fix serveral kinds of bugs 10. update the document of building 11. ...
1886 lines
98 KiB
C++
1886 lines
98 KiB
C++
#include "clipper/clipper_z.hpp"
|
|
|
|
#include "ClipperUtils.hpp"
|
|
#include "EdgeGrid.hpp"
|
|
#include "Layer.hpp"
|
|
#include "Print.hpp"
|
|
#include "ShortestPath.hpp"
|
|
#include "libslic3r.h"
|
|
#include "PrintConfig.hpp"
|
|
#include "Model.hpp"
|
|
#include <algorithm>
|
|
#include <numeric>
|
|
#include <unordered_set>
|
|
#include <tbb/parallel_for.h>
|
|
|
|
#ifndef NDEBUG
|
|
// #define BRIM_DEBUG_TO_SVG
|
|
#endif
|
|
|
|
#if defined(BRIM_DEBUG_TO_SVG)
|
|
#include "SVG.hpp"
|
|
#endif
|
|
|
|
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());
|
|
}
|
|
// BBS: generate brim area by objs
|
|
static void append_and_translate(ExPolygons& dst, const ExPolygons& src,
|
|
const PrintInstance& instance, const Print& print, std::map<ObjectID, ExPolygons>& brimAreaMap) {
|
|
ExPolygons srcShifted = src;
|
|
for (size_t src_idx = 0; src_idx < src.size(); ++src_idx)
|
|
srcShifted[src_idx].translate(instance.shift.x(), instance.shift.y());
|
|
srcShifted = diff_ex(std::move(srcShifted), dst);
|
|
//expolygons_append(dst, temp2);
|
|
expolygons_append(brimAreaMap[instance.print_object->id()], srcShifted);
|
|
}
|
|
|
|
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(), objects.end(), 0.,
|
|
[](double partial_result, const PrintObject *object) {
|
|
return std::max(partial_result, object->config().brim_type == btNoBrim ? 0. : object->config().brim_width.value);
|
|
}));
|
|
}
|
|
|
|
// Returns ExPolygons of the bottom layer of the print object after elephant foot compensation.
|
|
static ExPolygons get_print_object_bottom_layer_expolygons(const PrintObject &print_object)
|
|
{
|
|
ExPolygons ex_polygons;
|
|
for (LayerRegion *region : print_object.layers().front()->regions())
|
|
Slic3r::append(ex_polygons, closing_ex(region->slices.surfaces, float(SCALED_EPSILON)));
|
|
return ex_polygons;
|
|
}
|
|
|
|
// Returns ExPolygons of bottom layer for every print object in Print after elephant foot compensation.
|
|
static std::vector<ExPolygons> get_print_bottom_layers_expolygons(const Print &print)
|
|
{
|
|
std::vector<ExPolygons> bottom_layers_expolygons;
|
|
bottom_layers_expolygons.reserve(print.objects().size());
|
|
for (const PrintObject *object : print.objects())
|
|
bottom_layers_expolygons.emplace_back(get_print_object_bottom_layer_expolygons(*object));
|
|
|
|
return bottom_layers_expolygons;
|
|
}
|
|
|
|
static ConstPrintObjectPtrs get_top_level_objects_with_brim(const Print &print, const std::vector<ExPolygons> &bottom_layers_expolygons)
|
|
{
|
|
assert(print.objects().size() == bottom_layers_expolygons.size());
|
|
Polygons islands;
|
|
ConstPrintObjectPtrs island_to_object;
|
|
for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) {
|
|
const PrintObject *object = print.objects()[print_object_idx];
|
|
Polygons islands_object;
|
|
islands_object.reserve(bottom_layers_expolygons[print_object_idx].size());
|
|
for (const ExPolygon &ex_poly : bottom_layers_expolygons[print_object_idx])
|
|
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;
|
|
//FIXME likely pftNonZero or ptfPositive would be better. Why are we using ptfEvenOdd for Unions?
|
|
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, const double scaled_resolution)
|
|
{
|
|
Polygons islands;
|
|
for (const PrintObject *object : top_level_objects_with_brim) {
|
|
if (!object->has_brim())
|
|
continue;
|
|
|
|
//FIXME how about the brim type?
|
|
auto brim_object_gap = float(scale_(object->config().brim_object_gap.value));
|
|
Polygons islands_object;
|
|
for (const ExPolygon &ex_poly : get_print_object_bottom_layer_expolygons(*object)) {
|
|
Polygons contour_offset = offset(ex_poly.contour, brim_object_gap, ClipperLib::jtSquare);
|
|
for (Polygon &poly : contour_offset)
|
|
poly.douglas_peucker(scaled_resolution);
|
|
|
|
polygons_append(islands_object, std::move(contour_offset));
|
|
}
|
|
|
|
if (!object->support_layers().empty()) {
|
|
for (const Polygon& support_contour : object->support_layers().front()->support_fills.polygons_covered_by_spacing()) {
|
|
Polygons contour_offset = offset(support_contour, brim_object_gap, ClipperLib::jtSquare);
|
|
for (Polygon& poly : contour_offset)
|
|
poly.douglas_peucker(scaled_resolution);
|
|
|
|
polygons_append(islands_object, std::move(contour_offset));
|
|
}
|
|
}
|
|
|
|
// BBS
|
|
if (!object->tree_support_layers().empty()) {
|
|
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
|
|
Polygons contour_offset = offset(ex_poly.contour, brim_object_gap, ClipperLib::jtSquare);
|
|
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 std::vector<ExPolygons> &bottom_layers_expolygons,
|
|
const float no_brim_offset,
|
|
// BBS
|
|
double& brim_width_max,
|
|
std::map<ObjectID,
|
|
double>& brim_width_map)
|
|
{
|
|
const auto scaled_resolution = scaled<double>(print.config().resolution.value);
|
|
|
|
assert(print.objects().size() == bottom_layers_expolygons.size());
|
|
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;
|
|
brim_width_max = 0;
|
|
for(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) {
|
|
const PrintObject *object = print.objects()[print_object_idx];
|
|
const BrimType brim_type = object->config().brim_type.value;
|
|
const float brim_object_gap = scale_(object->config().brim_object_gap.value);
|
|
// recording the autoAssigned brimWidth and corresponding objs
|
|
double brimWidthAuto = object->config().brim_width.value;
|
|
double flowWidth = print.brim_flow().scaled_spacing() * SCALING_FACTOR;
|
|
brimWidthAuto = floor(brimWidthAuto / flowWidth / 2) * flowWidth * 2;
|
|
brim_width_map.insert(std::make_pair(object->id(), brimWidthAuto));
|
|
brim_width_max = std::max(brim_width_max, brimWidthAuto);
|
|
const float brim_width = scale_(brimWidthAuto);
|
|
const bool is_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;
|
|
for (const ExPolygon &ex_poly : bottom_layers_expolygons[print_object_idx]) {
|
|
if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) && is_top_outer_brim)
|
|
append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_object_gap, ClipperLib::jtRound, scaled_resolution), offset(ex_poly.contour, brim_object_gap, ClipperLib::jtSquare)));
|
|
|
|
// After 7ff76d07684858fd937ef2f5d863f105a10f798e offset and shrink don't work with CW polygons (holes), so let's make it CCW.
|
|
Polygons ex_poly_holes_reversed = ex_poly.holes;
|
|
polygons_reverse(ex_poly_holes_reversed);
|
|
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
|
|
append(no_brim_area_object, shrink_ex(ex_poly_holes_reversed, no_brim_offset, ClipperLib::jtSquare));
|
|
|
|
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim)
|
|
append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset, ClipperLib::jtSquare), ex_poly_holes_reversed));
|
|
|
|
if (brim_type != BrimType::btNoBrim)
|
|
append(no_brim_area_object, offset_ex(ExPolygon(ex_poly.contour), brim_object_gap, ClipperLib::jtSquare));
|
|
|
|
no_brim_area_object.emplace_back(ex_poly.contour);
|
|
}
|
|
|
|
if (!object->support_layers().empty()) {
|
|
for (const Polygon& support_contour : object->support_layers().front()->support_fills.polygons_covered_by_spacing()) {
|
|
if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) && is_top_outer_brim)
|
|
append(brim_area_object, diff_ex(offset(support_contour, brim_width + brim_object_gap, ClipperLib::jtRound, scaled_resolution), offset(support_contour, brim_object_gap)));
|
|
|
|
if (brim_type != BrimType::btNoBrim)
|
|
append(no_brim_area_object, offset_ex(ExPolygon(support_contour), brim_object_gap));
|
|
|
|
no_brim_area_object.emplace_back(support_contour);
|
|
}
|
|
}
|
|
|
|
// BBS
|
|
if (!object->tree_support_layers().empty()) {
|
|
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
|
|
if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) && is_top_outer_brim)
|
|
append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_object_gap, jtRound, scaled_resolution), offset(ex_poly.contour, brim_object_gap)));
|
|
|
|
if (brim_type != BrimType::btNoBrim)
|
|
append(no_brim_area_object, offset_ex(ExPolygon(ex_poly.contour), brim_object_gap));
|
|
|
|
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(brim_area, no_brim_area);
|
|
}
|
|
|
|
// BBS: the brims of different objs will not overlapped with each other, and are stored by objs and by extruders
|
|
static ExPolygons top_level_outer_brim_area(const Print& print, const ConstPrintObjectPtrs& top_level_objects_with_brim,
|
|
const float no_brim_offset, double& brim_width_max, std::map<ObjectID, double>& brim_width_map,
|
|
std::map<ObjectID, ExPolygons>& brimAreaMap,
|
|
std::map<ObjectID, ExPolygons>& supportBrimAreaMap, std::vector<std::pair<ObjectID, unsigned int>>& objPrintVec)
|
|
{
|
|
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);
|
|
|
|
unsigned int support_material_extruder = 1;
|
|
if (print.has_support_material()) {
|
|
assert(top_level_objects_with_brim.front()->config().support_filament >= 0);
|
|
if (top_level_objects_with_brim.front()->config().support_filament > 0)
|
|
support_material_extruder = top_level_objects_with_brim.front()->config().support_filament;
|
|
}
|
|
|
|
ExPolygons brim_area;
|
|
ExPolygons no_brim_area;
|
|
brim_width_max = 0;
|
|
struct brimWritten {
|
|
bool obj;
|
|
bool sup;
|
|
};
|
|
std::map<ObjectID, brimWritten> brimToWrite;
|
|
for (const auto& objectWithExtruder : objPrintVec)
|
|
brimToWrite.insert({ objectWithExtruder.first, {true,true} });
|
|
|
|
for (unsigned int extruderNo : print.extruders()) {
|
|
++extruderNo;
|
|
for (const auto &objectWithExtruder : objPrintVec) {
|
|
const PrintObject* object = print.get_object(objectWithExtruder.first);
|
|
const BrimType brim_type = object->config().brim_type.value;
|
|
const float brim_offset = scale_(object->config().brim_object_gap.value);
|
|
// recording the autoAssigned brimWidth and corresponding objs
|
|
double brimWidthAuto = object->config().brim_width.value;
|
|
double flowWidth = print.brim_flow().scaled_spacing() * SCALING_FACTOR;
|
|
brimWidthAuto = floor(brimWidthAuto / flowWidth / 2) * flowWidth * 2;
|
|
brim_width_map.insert(std::make_pair(object->id(), brimWidthAuto));
|
|
brim_width_max = std::max(brim_width_max, brimWidthAuto);
|
|
const float brim_width = scale_(brimWidthAuto);
|
|
const bool is_top_outer_brim = top_level_objects_idx.find(object->id().id) != top_level_objects_idx.end();
|
|
|
|
ExPolygons nullBrim;
|
|
brimAreaMap.insert(std::make_pair(object->id(), nullBrim));
|
|
ExPolygons brim_area_object;
|
|
ExPolygons brim_area_support;
|
|
ExPolygons no_brim_area_object;
|
|
ExPolygons no_brim_area_support;
|
|
if (objectWithExtruder.second == extruderNo && brimToWrite.at(object->id()).obj) {
|
|
for (const ExPolygon& ex_poly : object->layers().front()->lslices) {
|
|
if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) && is_top_outer_brim) {
|
|
append(brim_area_object, diff_ex(offset_ex(ex_poly.contour, brim_width + brim_offset, jtRound, SCALED_RESOLUTION),
|
|
offset_ex(ex_poly.contour, brim_offset)));
|
|
}
|
|
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
|
|
append(no_brim_area_object, offset_ex(ex_poly.holes, -no_brim_offset));
|
|
|
|
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim)
|
|
append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly.holes));
|
|
|
|
if (brim_type != BrimType::btNoBrim)
|
|
append(no_brim_area_object, offset_ex(ExPolygon(ex_poly.contour), brim_offset));
|
|
|
|
no_brim_area_object.emplace_back(ex_poly.contour);
|
|
}
|
|
brimToWrite.at(object->id()).obj == false;
|
|
for (const PrintInstance& instance : object->instances()) {
|
|
if (!brim_area_object.empty())
|
|
append_and_translate(brim_area, brim_area_object, instance, print, brimAreaMap);
|
|
append_and_translate(no_brim_area, no_brim_area_object, instance);
|
|
}
|
|
if (brimAreaMap.find(object->id()) != brimAreaMap.end())
|
|
expolygons_append(brim_area, brimAreaMap[object->id()]);
|
|
}
|
|
if (support_material_extruder == extruderNo && brimToWrite.at(object->id()).sup) {
|
|
if (!object->support_layers().empty()) {
|
|
for (const Polygon& support_contour : object->support_layers().front()->support_fills.polygons_covered_by_spacing()) {
|
|
//BBS: no brim offset for supports
|
|
if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) && is_top_outer_brim)
|
|
append(brim_area_support, diff_ex(offset(support_contour, brim_width, jtRound, SCALED_RESOLUTION), offset(support_contour, 0)));
|
|
|
|
if (brim_type != BrimType::btNoBrim)
|
|
append(no_brim_area_support, offset_ex(support_contour, 0));
|
|
|
|
no_brim_area_support.emplace_back(support_contour);
|
|
}
|
|
}
|
|
|
|
// BBS
|
|
if (!object->tree_support_layers().empty()) {
|
|
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
|
|
if ((brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) && is_top_outer_brim)
|
|
append(brim_area_support, diff_ex(offset(ex_poly.contour, brim_width, jtRound, SCALED_RESOLUTION), offset(ex_poly.contour, 0)));
|
|
|
|
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
|
|
append(no_brim_area_support, offset_ex(ex_poly.holes, -no_brim_offset));
|
|
|
|
if (brim_type != BrimType::btNoBrim)
|
|
append(no_brim_area_support, offset_ex(ex_poly.contour, 0));
|
|
|
|
no_brim_area_support.emplace_back(ex_poly.contour);
|
|
}
|
|
}
|
|
brimToWrite.at(object->id()).sup == false;
|
|
for (const PrintInstance& instance : object->instances()) {
|
|
if (!brim_area_support.empty())
|
|
append_and_translate(brim_area, brim_area_support, instance, print, supportBrimAreaMap);
|
|
append_and_translate(no_brim_area, no_brim_area_support, instance);
|
|
}
|
|
if (supportBrimAreaMap.find(object->id()) != supportBrimAreaMap.end())
|
|
expolygons_append(brim_area, supportBrimAreaMap[object->id()]);
|
|
}
|
|
}
|
|
}
|
|
for (const PrintObject* object : print.objects()) {
|
|
if (brimAreaMap.find(object->id()) != brimAreaMap.end())
|
|
brimAreaMap[object->id()] = diff_ex(brimAreaMap[object->id()], no_brim_area);
|
|
if (supportBrimAreaMap.find(object->id()) != supportBrimAreaMap.end())
|
|
supportBrimAreaMap[object->id()] = diff_ex(supportBrimAreaMap[object->id()], no_brim_area);
|
|
}
|
|
return diff_ex(std::move(brim_area), no_brim_area);
|
|
}
|
|
static ExPolygons inner_brim_area(const Print &print,
|
|
const ConstPrintObjectPtrs &top_level_objects_with_brim,
|
|
const std::vector<ExPolygons> &bottom_layers_expolygons,
|
|
const float no_brim_offset)
|
|
{
|
|
assert(print.objects().size() == bottom_layers_expolygons.size());
|
|
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(size_t print_object_idx = 0; print_object_idx < print.objects().size(); ++print_object_idx) {
|
|
const PrintObject *object = print.objects()[print_object_idx];
|
|
const BrimType brim_type = object->config().brim_type.value;
|
|
const float brim_object_gap = scale_(object->config().brim_object_gap.value);
|
|
double flowWidth = print.brim_flow().scaled_spacing() * SCALING_FACTOR;
|
|
const float brim_width = scale_(floor(object->config().brim_width.value / flowWidth / 2) * flowWidth * 2);
|
|
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 : bottom_layers_expolygons[print_object_idx]) {
|
|
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) {
|
|
if (top_outer_brim)
|
|
no_brim_area_object.emplace_back(ex_poly);
|
|
else
|
|
append(brim_area_object, diff_ex(offset(ex_poly.contour, brim_width + brim_object_gap, ClipperLib::jtSquare), offset(ex_poly.contour, brim_object_gap, ClipperLib::jtSquare)));
|
|
}
|
|
|
|
// After 7ff76d07684858fd937ef2f5d863f105a10f798e offset and shrink don't work with CW polygons (holes), so let's make it CCW.
|
|
Polygons ex_poly_holes_reversed = ex_poly.holes;
|
|
polygons_reverse(ex_poly_holes_reversed);
|
|
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim)
|
|
append(brim_area_object, diff_ex(shrink_ex(ex_poly_holes_reversed, brim_object_gap, ClipperLib::jtSquare), shrink_ex(ex_poly_holes_reversed, brim_width + brim_object_gap, ClipperLib::jtSquare)));
|
|
|
|
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim)
|
|
append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset, ClipperLib::jtSquare), ex_poly_holes_reversed));
|
|
|
|
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
|
|
append(no_brim_area_object, diff_ex(ExPolygon(ex_poly.contour), shrink_ex(ex_poly_holes_reversed, no_brim_offset, ClipperLib::jtSquare)));
|
|
|
|
append(holes_object, ex_poly_holes_reversed);
|
|
}
|
|
append(no_brim_area_object, offset_ex(bottom_layers_expolygons[print_object_idx], brim_object_gap, ClipperLib::jtSquare));
|
|
|
|
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);
|
|
}
|
|
|
|
// BBS: the brims of different objs will not overlapped with each other, and are stored by objs and by extruders
|
|
static ExPolygons inner_brim_area(const Print& print, const ConstPrintObjectPtrs& top_level_objects_with_brim,
|
|
const float no_brim_offset, std::map<ObjectID, ExPolygons>& brimAreaMap,
|
|
std::map<ObjectID, ExPolygons>& supportBrimAreaMap,
|
|
std::vector<std::pair<ObjectID, unsigned int>>& objPrintVec)
|
|
{
|
|
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);
|
|
|
|
unsigned int support_material_extruder = 1;
|
|
if (print.has_support_material()) {
|
|
assert(top_level_objects_with_brim.front()->config().support_filament >= 0);
|
|
if (top_level_objects_with_brim.front()->config().support_filament > 0)
|
|
support_material_extruder = top_level_objects_with_brim.front()->config().support_filament;
|
|
}
|
|
|
|
ExPolygons brim_area;
|
|
ExPolygons no_brim_area;
|
|
Polygons holes;
|
|
Polygon bedShape(get_bed_shape(print.config()));
|
|
holes.emplace_back(get_bed_shape(print.config()));
|
|
std::map<ObjectID, ExPolygons> innerBrimAreaMap;
|
|
std::map<ObjectID, ExPolygons> innerSupportBrimAreaMap;
|
|
|
|
struct brimWritten {
|
|
bool obj;
|
|
bool sup;
|
|
};
|
|
std::map<ObjectID, brimWritten> brimToWrite;
|
|
for (const auto& objectWithExtruder : objPrintVec)
|
|
brimToWrite.insert({ objectWithExtruder.first, {true,true} });
|
|
|
|
|
|
for (unsigned int extruderNo : print.extruders()) {
|
|
++extruderNo;
|
|
for (const auto& objectWithExtruder : objPrintVec) {
|
|
const PrintObject* object = print.get_object(objectWithExtruder.first);
|
|
const BrimType brim_type = object->config().brim_type.value;
|
|
const float brim_offset = scale_(object->config().brim_object_gap.value);
|
|
double flowWidth = print.brim_flow().scaled_spacing() * SCALING_FACTOR;
|
|
const float brim_width = scale_(floor(object->config().brim_width.value / flowWidth / 2) * flowWidth * 2);
|
|
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;
|
|
ExPolygons brim_area_support;
|
|
ExPolygons no_brim_area_support;
|
|
Polygons holes_object;
|
|
Polygons holes_support;
|
|
if (objectWithExtruder.second == extruderNo && brimToWrite.at(object->id()).obj) {
|
|
for (const ExPolygon& ex_poly : object->layers().front()->lslices) {
|
|
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) {
|
|
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, jtRound, SCALED_RESOLUTION), 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, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly.holes));
|
|
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));
|
|
brimToWrite.at(object->id()).obj == false;
|
|
for (const PrintInstance& instance : object->instances()) {
|
|
if (!brim_area_object.empty())
|
|
append_and_translate(brim_area, brim_area_object, instance, print, innerBrimAreaMap);
|
|
append_and_translate(no_brim_area, no_brim_area_object, instance);
|
|
append_and_translate(holes, holes_object, instance);
|
|
}
|
|
if (innerBrimAreaMap.find(object->id()) != innerBrimAreaMap.end())
|
|
expolygons_append(brim_area, innerBrimAreaMap[object->id()]);
|
|
}
|
|
if (support_material_extruder == extruderNo && brimToWrite.at(object->id()).sup) {
|
|
if (!object->support_layers().empty()) {
|
|
for (const Polygon& support_contour : object->support_layers().front()->support_fills.polygons_covered_by_spacing()) {
|
|
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) {
|
|
if (!top_outer_brim)
|
|
append(brim_area_support, diff_ex(offset_ex(support_contour, brim_width + brim_offset, jtRound, SCALED_RESOLUTION), offset_ex(support_contour, brim_offset)));
|
|
}
|
|
if (brim_type != BrimType::btNoBrim)
|
|
append(no_brim_area_support, offset_ex(support_contour, 0));
|
|
no_brim_area_support.emplace_back(support_contour);
|
|
}
|
|
}
|
|
|
|
// BBS
|
|
if (!object->tree_support_layers().empty()) {
|
|
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
|
|
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) {
|
|
if (!top_outer_brim)
|
|
append(brim_area_support, diff_ex(offset_ex(ex_poly.contour, brim_width + brim_offset, jtRound, SCALED_RESOLUTION), offset_ex(ex_poly.contour, brim_offset)));
|
|
}
|
|
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner)
|
|
append(brim_area_support, 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_support, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly.holes));
|
|
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btNoBrim)
|
|
append(no_brim_area_support, offset_ex(ex_poly.holes, -no_brim_offset));
|
|
append(holes_support, ex_poly.holes);
|
|
if (brim_type != BrimType::btNoBrim)
|
|
append(no_brim_area_support, offset_ex(ex_poly.contour, 0));
|
|
no_brim_area_support.emplace_back(ex_poly.contour);
|
|
}
|
|
}
|
|
}
|
|
brimToWrite.at(object->id()).sup == false;
|
|
for (const PrintInstance& instance : object->instances()) {
|
|
if (!brim_area_support.empty())
|
|
append_and_translate(brim_area, brim_area_support, instance, print, innerSupportBrimAreaMap);
|
|
append_and_translate(no_brim_area, no_brim_area_support, instance);
|
|
append_and_translate(holes, holes_support, instance);
|
|
}
|
|
if (innerSupportBrimAreaMap.find(object->id()) != innerSupportBrimAreaMap.end())
|
|
expolygons_append(brim_area, innerSupportBrimAreaMap[object->id()]);
|
|
}
|
|
}
|
|
for (const PrintObject* object : print.objects()) {
|
|
if (innerBrimAreaMap.find(object->id()) != innerBrimAreaMap.end()) {
|
|
innerBrimAreaMap[object->id()] = intersection_ex(to_polygons(innerBrimAreaMap[object->id()]), holes);
|
|
append(brimAreaMap[object->id()], innerBrimAreaMap[object->id()]);
|
|
}
|
|
if (innerSupportBrimAreaMap.find(object->id()) != innerSupportBrimAreaMap.end()) {
|
|
innerSupportBrimAreaMap[object->id()] = intersection_ex(to_polygons(innerSupportBrimAreaMap[object->id()]), holes);
|
|
append(supportBrimAreaMap[object->id()], innerSupportBrimAreaMap[object->id()]);
|
|
}
|
|
}
|
|
for (const PrintObject* object : print.objects()) {
|
|
if (brimAreaMap.find(object->id()) != brimAreaMap.end())
|
|
brimAreaMap[object->id()] = diff_ex(brimAreaMap[object->id()], no_brim_area);
|
|
if (supportBrimAreaMap.find(object->id()) != supportBrimAreaMap.end())
|
|
supportBrimAreaMap[object->id()] = diff_ex(supportBrimAreaMap[object->id()], no_brim_area);
|
|
}
|
|
brim_area = intersection_ex(to_polygons(brim_area), holes);
|
|
append(no_brim_area, brim_area);
|
|
return no_brim_area;
|
|
}
|
|
|
|
//BBS maximum temperature difference from print object class
|
|
double getTemperatureFromExtruder(const PrintObject* printObject) {
|
|
auto print = printObject->print();
|
|
std::vector<size_t> extrudersFirstLayer;
|
|
auto firstLayerRegions = printObject->layers().front()->regions();
|
|
if (!firstLayerRegions.empty()) {
|
|
for (const LayerRegion* regionPtr : firstLayerRegions) {
|
|
if (regionPtr->has_extrusions())
|
|
extrudersFirstLayer.push_back(regionPtr->region().extruder(frExternalPerimeter));
|
|
}
|
|
}
|
|
|
|
const PrintConfig& config = print->config();
|
|
int curr_bed_type = config.option("curr_bed_type")->getInt();
|
|
const ConfigOptionInts* bed_temp_1st_layer_opt = config.option<ConfigOptionInts>(get_bed_temp_1st_layer_key((BedType)curr_bed_type));
|
|
|
|
double maxDeltaTemp = 0;
|
|
for (auto extruderID : extrudersFirstLayer) {
|
|
int bedTemp = bed_temp_1st_layer_opt->get_at(extruderID - 1);
|
|
if (bedTemp > maxDeltaTemp)
|
|
maxDeltaTemp = bedTemp;
|
|
}
|
|
|
|
return maxDeltaTemp;
|
|
}
|
|
//BBS adhesion coefficients from print object class
|
|
double getadhesionCoeff(const PrintObject* printObject)
|
|
{
|
|
auto& insts = printObject->instances();
|
|
auto objectVolumes = insts[0].model_instance->get_object()->volumes;
|
|
|
|
auto print = printObject->print();
|
|
std::vector<size_t> extrudersFirstLayer;
|
|
auto firstLayerRegions = printObject->layers().front()->regions();
|
|
if (!firstLayerRegions.empty()) {
|
|
for (const LayerRegion* regionPtr : firstLayerRegions) {
|
|
if (regionPtr->has_extrusions())
|
|
extrudersFirstLayer.push_back(regionPtr->region().extruder(frExternalPerimeter));
|
|
}
|
|
}
|
|
double adhesionCoeff = 1;
|
|
for (const ModelVolume* modelVolume : objectVolumes) {
|
|
for (auto iter = extrudersFirstLayer.begin(); iter != extrudersFirstLayer.end(); iter++)
|
|
if (modelVolume->extruder_id() == *iter) {
|
|
if (Model::extruderParamsMap.find(modelVolume->extruder_id()) != Model::extruderParamsMap.end())
|
|
if (Model::extruderParamsMap.at(modelVolume->extruder_id()).materialName == "PET") {
|
|
adhesionCoeff = 2;
|
|
}
|
|
else if (Model::extruderParamsMap.at(modelVolume->extruder_id()).materialName == "TPU") {
|
|
adhesionCoeff = 0.5;
|
|
}
|
|
}
|
|
}
|
|
|
|
return adhesionCoeff;
|
|
/*
|
|
def->enum_values.push_back("PLA");
|
|
def->enum_values.push_back("PET");
|
|
def->enum_values.push_back("ABS");
|
|
def->enum_values.push_back("ASA");
|
|
def->enum_values.push_back("TPU");//BBS
|
|
def->enum_values.push_back("FLEX");
|
|
def->enum_values.push_back("HIPS");
|
|
def->enum_values.push_back("EDGE");
|
|
def->enum_values.push_back("NGEN");
|
|
def->enum_values.push_back("NYLON");
|
|
def->enum_values.push_back("PVA");
|
|
def->enum_values.push_back("PC");
|
|
def->enum_values.push_back("PP");
|
|
def->enum_values.push_back("PEI");
|
|
def->enum_values.push_back("PEEK");
|
|
def->enum_values.push_back("PEKK");
|
|
def->enum_values.push_back("POM");
|
|
def->enum_values.push_back("PSU");
|
|
def->enum_values.push_back("PVDF");
|
|
def->enum_values.push_back("SCAFF");
|
|
*/
|
|
}
|
|
|
|
// BBS: second moment of area of a polygon
|
|
bool compSecondMoment(Polygon poly, Vec2d& sm)
|
|
{
|
|
if (poly.is_clockwise())
|
|
poly.make_counter_clockwise();
|
|
|
|
sm = Vec2d(0., 0.);
|
|
if (poly.points.size() >= 3) {
|
|
Vec2d p1 = poly.points.back().cast<double>();
|
|
for (const Point& p : poly.points) {
|
|
Vec2d p2 = p.cast<double>();
|
|
double a = cross2(p1, p2);
|
|
|
|
sm += Vec2d((p1.y() * p1.y() + p1.y() * p2.y() + p2.y() * p2.y()), (p1.x() * p1.x() + p1.x() * p2.x() + p2.x() * p2.x())) * a / 12;
|
|
p1 = p2;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
// BBS: properties of an expolygon
|
|
struct ExPolyProp
|
|
{
|
|
double aera = 0;
|
|
Vec2d centroid;
|
|
Vec2d secondMomentOfAreaRespectToCentroid;
|
|
|
|
};
|
|
// BBS: second moment of area of an expolyon
|
|
bool compSecondMoment(const ExPolygon& expoly, ExPolyProp& expolyProp)
|
|
{
|
|
double aera = expoly.contour.area();
|
|
Vec2d cent = expoly.contour.centroid().cast<double>() * aera;
|
|
Vec2d sm;
|
|
if (!compSecondMoment(expoly.contour, sm))
|
|
return false;
|
|
|
|
for (auto& hole : expoly.holes) {
|
|
double a = hole.area();
|
|
aera += hole.area();
|
|
cent += hole.centroid().cast<double>() * a;
|
|
Vec2d smh;
|
|
if (compSecondMoment(hole, smh))
|
|
sm += -smh;
|
|
}
|
|
|
|
cent = cent / aera;
|
|
sm = sm - Vec2d(cent.y() * cent.y(), cent.x() * cent.x()) * aera;
|
|
expolyProp.aera = aera;
|
|
expolyProp.centroid = cent;
|
|
expolyProp.secondMomentOfAreaRespectToCentroid = sm;
|
|
return true;
|
|
}
|
|
|
|
// BBS: second moment of area of expolygons
|
|
bool compSecondMoment(const ExPolygons& expolys, double& smExpolysX, double& smExpolysY)
|
|
{
|
|
if (expolys.empty()) return false;
|
|
std::vector<ExPolyProp> props;
|
|
for (const ExPolygon& expoly : expolys) {
|
|
ExPolyProp prop;
|
|
if (compSecondMoment(expoly, prop))
|
|
props.push_back(prop);
|
|
}
|
|
if (props.empty())
|
|
return false;
|
|
double totalArea = 0.;
|
|
Vec2d staticMoment(0., 0.);
|
|
for (const ExPolyProp& prop : props) {
|
|
totalArea += prop.aera;
|
|
staticMoment += prop.centroid * prop.aera;
|
|
}
|
|
double totalCentroidX = staticMoment.x() / totalArea;
|
|
double totalCentroidY = staticMoment.y() / totalArea;
|
|
|
|
smExpolysX = 0;
|
|
smExpolysY = 0;
|
|
for (const ExPolyProp& prop : props) {
|
|
double deltaX = prop.centroid.x() - totalCentroidX;
|
|
double deltaY = prop.centroid.y() - totalCentroidY;
|
|
smExpolysX += prop.secondMomentOfAreaRespectToCentroid.x() + prop.aera * deltaY * deltaY;
|
|
smExpolysY += prop.secondMomentOfAreaRespectToCentroid.y() + prop.aera * deltaX * deltaX;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
//BBS: config brimwidth by volumes
|
|
double configBrimWidthByVolumes(double deltaT, double adhension, double maxSpeed, const ModelVolume* modelVolumePtr, const ExPolygons& expolys)
|
|
{
|
|
// height of a volume
|
|
double height = 0;
|
|
if (modelVolumePtr->is_model_part()) {
|
|
auto rawBoundingbox = modelVolumePtr->mesh().transformed_bounding_box(modelVolumePtr->get_matrix());
|
|
auto bbox = modelVolumePtr->get_object()->instances.front()->transform_bounding_box(rawBoundingbox);
|
|
auto bbox_size = bbox.size();
|
|
height = bbox_size(2);
|
|
}
|
|
|
|
// sencond moment of the expolygons of the first layer of the volume
|
|
double Ixx = -1.e30, Iyy = -1.e30;
|
|
if (!expolys.empty()) {
|
|
if (!compSecondMoment(expolys, Ixx, Iyy))
|
|
Ixx = Iyy = -1.e30;
|
|
}
|
|
Ixx = Ixx * SCALING_FACTOR * SCALING_FACTOR * SCALING_FACTOR * SCALING_FACTOR;
|
|
Iyy = Iyy * SCALING_FACTOR * SCALING_FACTOR * SCALING_FACTOR * SCALING_FACTOR;
|
|
|
|
// bounding box of the expolygons of the first layer of the volume
|
|
BoundingBox bbox2;
|
|
for (const auto& expoly : expolys)
|
|
bbox2.merge(get_extents(expoly.contour));
|
|
const double& bboxX = bbox2.size()(0);
|
|
const double& bboxY = bbox2.size()(1);
|
|
double thermalLength = sqrt(bboxX * bboxX + bboxY * bboxY) * SCALING_FACTOR;
|
|
double thermalLengthRef = Model::getThermalLength(modelVolumePtr);
|
|
|
|
double height_to_area = std::max(height / Ixx * (bbox2.size()(1) * SCALING_FACTOR), height / Iyy * (bbox2.size()(0) * SCALING_FACTOR));
|
|
double brim_width = adhension * std::min(std::min(std::max(height_to_area * maxSpeed / 24, thermalLength * 8. / thermalLengthRef * std::min(height, 30.) / 30.), 18.), 1.5 * thermalLength);
|
|
// small brims are omitted
|
|
if (brim_width < 5 && brim_width < 1.5 * thermalLength)
|
|
brim_width = 0;
|
|
// large brims are omitted
|
|
if (brim_width > 18) brim_width = 18.;
|
|
|
|
return brim_width;
|
|
}
|
|
|
|
//BBS: config brimwidth by group of volumes
|
|
double configBrimWidthByVolumeGroups(double adhension, double maxSpeed, const std::vector<ModelVolume*> modelVolumePtrs, const ExPolygons& expolys)
|
|
{
|
|
// height of a group of volumes
|
|
double height = 0;
|
|
BoundingBoxf3 mergedBbx;
|
|
for (const auto& modelVolumePtr : modelVolumePtrs) {
|
|
if (modelVolumePtr->is_model_part()) {
|
|
Slic3r::Transform3d t;
|
|
if (modelVolumePtr->get_object()->instances.size() > 0)
|
|
t = modelVolumePtr->get_object()->instances.front()->get_matrix() * modelVolumePtr->get_matrix();
|
|
else
|
|
t = modelVolumePtr->get_matrix();
|
|
auto bbox = modelVolumePtr->mesh().transformed_bounding_box(t);
|
|
mergedBbx.merge(bbox);
|
|
}
|
|
}
|
|
auto bbox_size = mergedBbx.size();
|
|
height = bbox_size(2);
|
|
|
|
// second moment of the expolygons of the first layer of the volume group
|
|
double Ixx = -1.e30, Iyy = -1.e30;
|
|
if (!expolys.empty()) {
|
|
if (!compSecondMoment(expolys, Ixx, Iyy))
|
|
Ixx = Iyy = -1.e30;
|
|
}
|
|
Ixx = Ixx * SCALING_FACTOR * SCALING_FACTOR * SCALING_FACTOR * SCALING_FACTOR;
|
|
Iyy = Iyy * SCALING_FACTOR * SCALING_FACTOR * SCALING_FACTOR * SCALING_FACTOR;
|
|
|
|
// bounding box of the expolygons of the first layer of the volume
|
|
BoundingBox bbox2;
|
|
for (const auto& expoly : expolys)
|
|
bbox2.merge(get_extents(expoly.contour));
|
|
const double& bboxX = bbox2.size()(0);
|
|
const double& bboxY = bbox2.size()(1);
|
|
double thermalLength = sqrt(bboxX * bboxX + bboxY * bboxY) * SCALING_FACTOR;
|
|
double thermalLengthRef = Model::getThermalLength(modelVolumePtrs);
|
|
|
|
double height_to_area = std::max(height / Ixx * (bbox2.size()(1) * SCALING_FACTOR), height / Iyy * (bbox2.size()(0) * SCALING_FACTOR));
|
|
double brim_width = adhension * std::min(std::min(std::max(height_to_area * maxSpeed / 24, thermalLength * 8. / thermalLengthRef * std::min(height, 30.) / 30.), 18.), 1.5 * thermalLength);
|
|
// small brims are omitted
|
|
if (brim_width < 5 && brim_width < 1.5 * thermalLength)
|
|
brim_width = 0;
|
|
// large brims are omitted
|
|
if (brim_width > 18) brim_width = 18.;
|
|
|
|
return brim_width;
|
|
}
|
|
|
|
//BBS: create all brims
|
|
static ExPolygons outer_inner_brim_area(const Print& print,
|
|
const float no_brim_offset, std::map<ObjectID, ExPolygons>& brimAreaMap,
|
|
std::map<ObjectID, ExPolygons>& supportBrimAreaMap,
|
|
std::vector<std::pair<ObjectID, unsigned int>>& objPrintVec,
|
|
std::vector<unsigned int>& printExtruders)
|
|
{
|
|
unsigned int support_material_extruder = printExtruders.front() + 1;
|
|
|
|
ExPolygons brim_area;
|
|
ExPolygons no_brim_area;
|
|
Polygons holes;
|
|
|
|
struct brimWritten {
|
|
bool obj;
|
|
bool sup;
|
|
};
|
|
std::map<ObjectID, brimWritten> brimToWrite;
|
|
for (const auto& objectWithExtruder : objPrintVec)
|
|
brimToWrite.insert({ objectWithExtruder.first, {true,true} });
|
|
|
|
std::map<ObjectID, ExPolygons> objectIslandMap;
|
|
|
|
for (unsigned int extruderNo : printExtruders) {
|
|
++extruderNo;
|
|
for (const auto& objectWithExtruder : objPrintVec) {
|
|
const PrintObject* object = print.get_object(objectWithExtruder.first);
|
|
const BrimType brim_type = object->config().brim_type.value;
|
|
float brim_offset = scale_(object->config().brim_object_gap.value);
|
|
double flowWidth = print.brim_flow().scaled_spacing() * SCALING_FACTOR;
|
|
float brim_width = scale_(floor(object->config().brim_width.value / flowWidth / 2) * flowWidth * 2);
|
|
const float scaled_flow_width = print.brim_flow().scaled_spacing();
|
|
const float scaled_additional_brim_width = scale_(floor(5 / flowWidth / 2) * flowWidth * 2);
|
|
const float scaled_half_min_adh_length = scale_(1.1);
|
|
bool has_brim_auto = object->config().brim_type == btAutoBrim;
|
|
|
|
ExPolygons brim_area_object;
|
|
ExPolygons no_brim_area_object;
|
|
ExPolygons brim_area_support;
|
|
ExPolygons no_brim_area_support;
|
|
Polygons holes_object;
|
|
Polygons holes_support;
|
|
if (objectWithExtruder.second == extruderNo && brimToWrite.at(object->id()).obj) {
|
|
double deltaT = getTemperatureFromExtruder(object);
|
|
double adhension = getadhesionCoeff(object);
|
|
double maxSpeed = Model::findMaxSpeed(object->model_object());
|
|
// BBS: brims are generated by volume groups
|
|
for (const auto& volumeGroup : object->firstLayerObjGroups()) {
|
|
// find volumePtrs included in this group
|
|
std::vector<ModelVolume*> groupVolumePtrs;
|
|
for (auto& volumeID : volumeGroup.volume_ids) {
|
|
ModelVolume* currentModelVolumePtr = nullptr;
|
|
for (auto volumePtr : object->model_object()->volumes) {
|
|
if (volumePtr->id() == volumeID) {
|
|
currentModelVolumePtr = volumePtr;
|
|
break;
|
|
}
|
|
}
|
|
if (currentModelVolumePtr != nullptr) groupVolumePtrs.push_back(currentModelVolumePtr);
|
|
}
|
|
if (groupVolumePtrs.empty()) continue;
|
|
|
|
// config brim width in auto-brim mode
|
|
if (has_brim_auto) {
|
|
double brimWidthRaw = configBrimWidthByVolumeGroups(adhension, maxSpeed, groupVolumePtrs, volumeGroup.slices);
|
|
brim_width = scale_(floor(brimWidthRaw / flowWidth / 2) * flowWidth * 2);
|
|
}
|
|
|
|
for (const ExPolygon& ex_poly : volumeGroup.slices) {
|
|
// BBS: additional brim width will be added if part's adhension area is too small and brim is not generated
|
|
float brim_width_mod;
|
|
if (brim_width < scale_(5.) && has_brim_auto) {
|
|
brim_width_mod = ex_poly.area() / ex_poly.contour.length() < scaled_half_min_adh_length
|
|
&& brim_width < scaled_flow_width ? brim_width + scaled_additional_brim_width : brim_width;
|
|
}
|
|
else {
|
|
brim_width_mod = brim_width;
|
|
}
|
|
//BBS: brim width should be limited to the 1.5*boundingboxSize of a single polygon.
|
|
if (has_brim_auto) {
|
|
BoundingBox bbox2 = ex_poly.contour.bounding_box();
|
|
brim_width_mod = std::min(brim_width_mod, float(std::max(bbox2.size()(0), bbox2.size()(1))));
|
|
}
|
|
brim_width_mod = floor(brim_width_mod / scaled_flow_width / 2) * scaled_flow_width * 2;
|
|
|
|
Polygons ex_poly_holes_reversed = ex_poly.holes;
|
|
polygons_reverse(ex_poly_holes_reversed);
|
|
|
|
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) {
|
|
// BBS: inner and outer boundary are offset from the same polygon incase of round off error.
|
|
auto innerExpoly = offset_ex(ex_poly.contour, brim_offset, jtRound, SCALED_RESOLUTION);
|
|
append(brim_area_object, diff_ex(offset_ex(innerExpoly, brim_width_mod, jtRound, SCALED_RESOLUTION), innerExpoly));
|
|
// BBS: brim should be apart from holes
|
|
append(no_brim_area_object, diff_ex(ex_poly_holes_reversed, offset_ex(ex_poly_holes_reversed, -scale_(5.))));
|
|
}
|
|
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner) {
|
|
append(brim_area_object, diff_ex(offset_ex(ex_poly_holes_reversed, -brim_offset), offset_ex(ex_poly_holes_reversed, -brim_width - brim_offset)));
|
|
}
|
|
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btNoBrim)
|
|
append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly_holes_reversed));
|
|
if (brim_type == BrimType::btNoBrim)
|
|
append(no_brim_area_object, offset_ex(ex_poly_holes_reversed, -no_brim_offset));
|
|
append(holes_object, ex_poly_holes_reversed);
|
|
}
|
|
}
|
|
auto objectIsland = offset_ex(object->layers().front()->lslices, brim_offset, jtRound, SCALED_RESOLUTION);
|
|
append(no_brim_area_object, objectIsland);
|
|
|
|
brimToWrite.at(object->id()).obj = false;
|
|
for (const PrintInstance& instance : object->instances()) {
|
|
if (!brim_area_object.empty())
|
|
append_and_translate(brim_area, brim_area_object, instance, print, brimAreaMap);
|
|
append_and_translate(no_brim_area, no_brim_area_object, instance);
|
|
append_and_translate(holes, holes_object, instance);
|
|
append_and_translate(objectIslandMap[instance.print_object->id()], objectIsland, instance);
|
|
|
|
}
|
|
if (brimAreaMap.find(object->id()) != brimAreaMap.end())
|
|
expolygons_append(brim_area, brimAreaMap[object->id()]);
|
|
}
|
|
support_material_extruder = object->config().support_filament;
|
|
if (support_material_extruder == 0 && object->has_support_material()) {
|
|
if (print.config().print_sequence == PrintSequence::ByObject)
|
|
support_material_extruder = objectWithExtruder.second;
|
|
else
|
|
support_material_extruder = printExtruders.front() + 1;
|
|
}
|
|
if (support_material_extruder == extruderNo && brimToWrite.at(object->id()).sup) {
|
|
if (!object->support_layers().empty()) {
|
|
for (const Polygon& support_contour : object->support_layers().front()->support_fills.polygons_covered_by_spacing()) {
|
|
// Brim will not be generated for supports
|
|
/*
|
|
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) {
|
|
append(brim_area_support, diff_ex(offset_ex(support_contour, brim_width + brim_offset, jtRound, SCALED_RESOLUTION), offset_ex(support_contour, brim_offset)));
|
|
}
|
|
if (brim_type != BrimType::btNoBrim)
|
|
append(no_brim_area_support, offset_ex(support_contour, 0));
|
|
*/
|
|
no_brim_area_support.emplace_back(support_contour);
|
|
}
|
|
}
|
|
// BBS
|
|
if (!object->tree_support_layers().empty()) {
|
|
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
|
|
// BBS: additional brim width will be added if adhension area is too small without brim
|
|
float brim_width_mod = ex_poly.area() / ex_poly.contour.length() < scaled_half_min_adh_length
|
|
&& brim_width < scaled_flow_width ? brim_width + scaled_additional_brim_width : brim_width;
|
|
brim_width_mod = floor(brim_width_mod / scaled_flow_width / 2) * scaled_flow_width * 2;
|
|
// Brim will not be generated for supports
|
|
/*
|
|
if (brim_type == BrimType::btOuterOnly || brim_type == BrimType::btOuterAndInner || brim_type == BrimType::btAutoBrim) {
|
|
append(brim_area_support, diff_ex(offset_ex(ex_poly.contour, brim_width_mod + brim_offset, jtRound, SCALED_RESOLUTION), offset_ex(ex_poly.contour, brim_offset)));
|
|
}
|
|
if (brim_type == BrimType::btInnerOnly || brim_type == BrimType::btOuterAndInner)
|
|
append(brim_area_support, 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_support, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly.holes));
|
|
if (brim_type == BrimType::btNoBrim)
|
|
append(no_brim_area_support, offset_ex(ex_poly.holes, -no_brim_offset));
|
|
append(holes_support, ex_poly.holes);
|
|
if (brim_type != BrimType::btNoBrim)
|
|
append(no_brim_area_support, offset_ex(ex_poly.contour, 0));
|
|
no_brim_area_support.emplace_back(ex_poly.contour);
|
|
}
|
|
}
|
|
brimToWrite.at(object->id()).sup = false;
|
|
for (const PrintInstance& instance : object->instances()) {
|
|
if (!brim_area_support.empty())
|
|
append_and_translate(brim_area, brim_area_support, instance, print, supportBrimAreaMap);
|
|
append_and_translate(no_brim_area, no_brim_area_support, instance);
|
|
append_and_translate(holes, holes_support, instance);
|
|
}
|
|
if (supportBrimAreaMap.find(object->id()) != supportBrimAreaMap.end())
|
|
expolygons_append(brim_area, supportBrimAreaMap[object->id()]);
|
|
}
|
|
}
|
|
}
|
|
for (const PrintObject* object : print.objects()) {
|
|
if (brimAreaMap.find(object->id()) != brimAreaMap.end()) {
|
|
brimAreaMap[object->id()] = diff_ex(brimAreaMap[object->id()], no_brim_area);
|
|
|
|
// BBS: brim should be contacted to at least one object island
|
|
if (objectIslandMap.find(object->id()) != objectIslandMap.end() && !objectIslandMap[object->id()].empty()) {
|
|
auto tempArea = brimAreaMap[object->id()];
|
|
brimAreaMap[object->id()].clear();
|
|
// the error bound is set to 2x flow width
|
|
for (auto& ta : tempArea) {
|
|
auto offsetedTa = offset_ex(ta, print.brim_flow().scaled_spacing() * 2, jtRound, SCALED_RESOLUTION);
|
|
if (!intersection_ex(offsetedTa, objectIslandMap[object->id()]).empty())
|
|
brimAreaMap[object->id()].push_back(ta);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (supportBrimAreaMap.find(object->id()) != supportBrimAreaMap.end())
|
|
supportBrimAreaMap[object->id()] = diff_ex(supportBrimAreaMap[object->id()], no_brim_area);
|
|
}
|
|
//brim_area = diff_ex(brim_area, no_brim_area);
|
|
return brim_area;
|
|
}
|
|
// Flip orientation of open polylines to minimize travel distance.
|
|
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)
|
|
{
|
|
if (polylines.empty())
|
|
return {};
|
|
|
|
BoundingBox bbox = get_extents(polylines);
|
|
bbox.merge(get_extents(brim_area));
|
|
|
|
EdgeGrid::Grid grid(bbox.inflated(SCALED_EPSILON));
|
|
grid.create(brim_area, polylines, 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 = false;
|
|
|
|
} visitor(grid);
|
|
|
|
// Connect successive polylines if they are open, their ends are closer than max_connection_length.
|
|
// Remove empty polylines.
|
|
{
|
|
// Skip initial empty lines.
|
|
size_t poly_idx = 0;
|
|
for (; poly_idx < polylines.size() && polylines[poly_idx].empty(); ++ poly_idx) ;
|
|
size_t end = ++ poly_idx;
|
|
double max_connection_length2 = Slic3r::sqr(max_connection_length);
|
|
for (; poly_idx < polylines.size(); ++poly_idx) {
|
|
Polyline &next = polylines[poly_idx];
|
|
if (! next.empty()) {
|
|
Polyline &prev = polylines[end - 1];
|
|
bool connect = false;
|
|
if (! prev.is_closed() && ! next.is_closed()) {
|
|
double dist2 = (prev.last_point() - next.first_point()).cast<double>().squaredNorm();
|
|
if (dist2 <= max_connection_length2) {
|
|
visitor.brim_line.a = prev.last_point();
|
|
visitor.brim_line.b = next.first_point();
|
|
// Shrink the connection line to avoid collisions with the brim centerlines.
|
|
visitor.brim_line.extend(-SCALED_EPSILON);
|
|
grid.visit_cells_intersecting_line(visitor.brim_line.a, visitor.brim_line.b, visitor);
|
|
connect = ! visitor.intersect;
|
|
}
|
|
}
|
|
if (connect) {
|
|
append(prev.points, std::move(next.points));
|
|
} else {
|
|
if (end < poly_idx)
|
|
polylines[end] = std::move(next);
|
|
++ end;
|
|
}
|
|
}
|
|
}
|
|
if (end < polylines.size())
|
|
polylines.erase(polylines.begin() + int(end), polylines.end());
|
|
}
|
|
|
|
return std::move(polylines);
|
|
}
|
|
|
|
// BBS: this function is used to generate brim for inner island inside holes
|
|
// Collect island + brim area to be minused when generating inner brim for holes
|
|
static void make_inner_island_brim(const Print& print, const ConstPrintObjectPtrs& top_level_objects_with_brim,
|
|
ExtrusionEntityCollection &brim, ExPolygons &islands_area_ex)
|
|
{
|
|
const auto scaled_resolution = scaled<double>(print.config().resolution.value);
|
|
|
|
auto save_polygon_if_is_inner_island = [scaled_resolution](const Polygons& holes_area, Polygon& contour, std::map<size_t, Polygons>& hole_island_pair) {
|
|
for (size_t i = 0; i < holes_area.size(); i++) {
|
|
Polygons contour_polys;
|
|
contour_polys.push_back(contour);
|
|
if (diff_ex(contour_polys, { holes_area[i] }).empty()) {
|
|
// BBS: this is an inner island inside holes_area[i], save
|
|
contour.douglas_peucker(scaled_resolution);
|
|
hole_island_pair[i].push_back(contour);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
Flow flow = print.brim_flow();
|
|
for (const PrintObject* object : top_level_objects_with_brim) {
|
|
const BrimType brim_type = object->config().brim_type.value;
|
|
// BBS: don't need to handle this object if hasn't enabled outer_brim
|
|
if (brim_type == BrimType::btNoBrim)
|
|
continue;
|
|
|
|
//BBS: 1 collect holes area which is used to limit the brim of inner island
|
|
Polygons holes_area;
|
|
for (const ExPolygon& ex_poly : object->layers().front()->lslices)
|
|
polygons_append(holes_area, ex_poly.holes);
|
|
|
|
|
|
//BBS: 2 get the island polygons inside holes, saved as map
|
|
std::map<size_t, Polygons> hole_island_pair;
|
|
for (const ExPolygon& ex_poly : object->layers().front()->lslices) {
|
|
Polygon counter = ex_poly.contour;
|
|
save_polygon_if_is_inner_island(holes_area, counter, hole_island_pair);
|
|
}
|
|
|
|
if (!object->support_layers().empty()) {
|
|
for (const Polygon& support_contour : object->support_layers().front()->support_fills.polygons_covered_by_spacing()) {
|
|
Polygon counter = support_contour;
|
|
save_polygon_if_is_inner_island(holes_area, counter, hole_island_pair);
|
|
}
|
|
}
|
|
|
|
if (!object->tree_support_layers().empty()) {
|
|
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
|
|
Polygon counter = ex_poly.contour;
|
|
save_polygon_if_is_inner_island(holes_area, counter, hole_island_pair);
|
|
}
|
|
}
|
|
|
|
//BBS: 3 generate loops, only save part of loop which inside hole
|
|
const float brim_offset = scale_(object->config().brim_object_gap.value);
|
|
const float brim_width = scale_(object->config().brim_width.value);
|
|
if (brim_type == BrimType::btInnerOnly) {
|
|
// If brim_type is btInnerOnly, we actually doesn't generate loops for inner island.
|
|
// Only update islands_area_ex and return
|
|
for (auto it = hole_island_pair.begin(); it != hole_island_pair.end(); it++) {
|
|
ExPolygons islands_area_ex_object = intersection_ex(offset(it->second, brim_offset), offset(holes_area[it->first], -brim_offset));
|
|
for (const PrintInstance& instance : object->instances())
|
|
append_and_translate(islands_area_ex, islands_area_ex_object, instance);
|
|
}
|
|
}
|
|
else {
|
|
size_t num_loops = size_t(floor(brim_width / float(flow.scaled_spacing())));
|
|
for (auto it = hole_island_pair.begin(); it != hole_island_pair.end(); it++) {
|
|
Polygons loops;
|
|
Polygons inner_islands = offset(it->second, brim_offset);
|
|
Polygons brimable_area = offset(holes_area[it->first], -brim_offset); //offset to keep away from hole
|
|
Polygons contour = inner_islands;
|
|
for (size_t i = 0; i < num_loops; ++i) {
|
|
contour = offset(contour, float(flow.scaled_spacing()), jtSquare);
|
|
for (Polygon& poly : contour)
|
|
poly.douglas_peucker(scaled_resolution);
|
|
polygons_append(loops, offset(contour, -0.5f * float(flow.scaled_spacing())));
|
|
}
|
|
// BBS: to be checked.
|
|
//loops = union_pt_chained_outside_in(loops, false);
|
|
loops = union_pt_chained_outside_in(loops);
|
|
|
|
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, &brimable_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]) }, brimable_area));
|
|
}
|
|
});
|
|
}
|
|
|
|
// BBS: Reduce down to the ordered list of polylines.
|
|
Polylines all_loops_object;
|
|
for (Polylines& polylines : loops_pl_by_levels)
|
|
append(all_loops_object, std::move(polylines));
|
|
loops_pl_by_levels.clear();
|
|
|
|
optimize_polylines_by_reversing(&all_loops_object);
|
|
all_loops_object = connect_brim_lines(std::move(all_loops_object), offset(inner_islands, float(SCALED_EPSILON)), float(flow.scaled_spacing()) * 2.f);
|
|
|
|
Polylines final_loops;
|
|
for (const PrintInstance& instance : object->instances()) {
|
|
size_t dst_idx = final_loops.size();
|
|
final_loops.insert(final_loops.end(), all_loops_object.begin(), all_loops_object.end());
|
|
for (; dst_idx < final_loops.size(); ++dst_idx)
|
|
final_loops[dst_idx].translate(instance.shift.x(), instance.shift.y());
|
|
|
|
}
|
|
extrusion_entities_append_loops_and_paths(brim.entities, std::move(final_loops),
|
|
erBrim, float(flow.mm3_per_mm()), float(flow.width()),
|
|
float(print.skirt_first_layer_height()));
|
|
|
|
//BBS: save all inner island and inner island brim area here, which is necesary if generate inner brim for holes
|
|
//Inner brim of holes must not occupy this area
|
|
ExPolygons islands_area_ex_object = intersection_ex(contour, brimable_area);
|
|
for (const PrintInstance& instance : object->instances())
|
|
append_and_translate(islands_area_ex, islands_area_ex_object, instance);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//BBS: the brim are generated one by one, and sorted by objs/supports and extruders
|
|
static void make_inner_island_brim(const Print& print, const ConstPrintObjectPtrs& top_level_objects_with_brim,
|
|
std::map<ObjectID, ExPolygons>& innerbrimAreaMap,
|
|
std::map<ObjectID, ExPolygons>& innerSupportBrimAreaMap,
|
|
ExPolygons& islands_area_ex, ExPolygons& NobrimArea,
|
|
std::vector<std::pair<ObjectID, unsigned int>>& objPrintVec)
|
|
{
|
|
auto save_polygon_if_is_inner_island = [](const Polygons& holes_area, Polygon& counter, std::map<size_t, Polygons>& hole_island_pair) {
|
|
for (size_t i = 0; i < holes_area.size(); i++) {
|
|
if (diff_ex(Polygons{ counter }, { holes_area[i] }).empty()) {
|
|
// BBS: this is an inner island inside holes_area[i], save
|
|
counter.douglas_peucker(SCALED_RESOLUTION);
|
|
hole_island_pair[i].push_back(counter);
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
unsigned int support_material_extruder = 1;
|
|
if (print.has_support_material()) {
|
|
assert(top_level_objects_with_brim.front()->config().support_filament >= 0);
|
|
if (top_level_objects_with_brim.front()->config().support_filament > 0)
|
|
support_material_extruder = top_level_objects_with_brim.front()->config().support_filament;
|
|
}
|
|
|
|
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);
|
|
|
|
struct brimWritten {
|
|
bool obj;
|
|
bool sup;
|
|
};
|
|
std::map<ObjectID, brimWritten> brimToWrite;
|
|
for (const auto& objectWithExtruder : objPrintVec)
|
|
if (top_level_objects_idx.find(objectWithExtruder.first.id) != top_level_objects_idx.end())
|
|
brimToWrite.insert({ objectWithExtruder.first, {true,true} });
|
|
|
|
Flow flow = print.brim_flow();
|
|
for (unsigned int extruderNo : print.extruders()) {
|
|
++extruderNo;
|
|
for (const auto& objectWithExtruder : objPrintVec) {
|
|
if (top_level_objects_idx.find(objectWithExtruder.first.id) != top_level_objects_idx.end()) {
|
|
const PrintObject* object = print.get_object(objectWithExtruder.first);
|
|
const BrimType brim_type = object->config().brim_type.value;
|
|
// BBS: don't need to handle this object if hasn't enabled outer_brim
|
|
if (brim_type == BrimType::btNoBrim)
|
|
continue;
|
|
|
|
//BBS: 1 collect holes area which is used to limit the brim of inner island
|
|
Polygons holes_area;
|
|
for (const ExPolygon& ex_poly : object->layers().front()->lslices)
|
|
polygons_append(holes_area, ex_poly.holes);
|
|
|
|
|
|
//BBS: 2 get the island polygons inside holes, saved as map
|
|
std::map<size_t, Polygons> hole_island_pair;
|
|
for (const ExPolygon& ex_poly : object->layers().front()->lslices) {
|
|
Polygon counter = ex_poly.contour;
|
|
save_polygon_if_is_inner_island(holes_area, counter, hole_island_pair);
|
|
}
|
|
std::map<size_t, Polygons> hole_island_pair_supports;
|
|
if (!object->support_layers().empty()) {
|
|
for (const Polygon& support_contour : object->support_layers().front()->support_fills.polygons_covered_by_spacing()) {
|
|
Polygon counter = support_contour;
|
|
save_polygon_if_is_inner_island(holes_area, counter, hole_island_pair_supports);
|
|
}
|
|
}
|
|
|
|
if (!object->tree_support_layers().empty()) {
|
|
for (const ExPolygon& ex_poly : object->tree_support_layers().front()->lslices) {
|
|
Polygon counter = ex_poly.contour;
|
|
save_polygon_if_is_inner_island(holes_area, counter, hole_island_pair_supports);
|
|
}
|
|
}
|
|
|
|
//BBS: 3 generate loops, only save part of loop which inside hole
|
|
const float brim_offset = scale_(object->config().brim_object_gap.value);
|
|
const float brim_width = floor(scale_(object->config().brim_width.value) / 2 / flow.scaled_spacing()) * 2 * flow.scaled_spacing();
|
|
if (objectWithExtruder.second == extruderNo && brimToWrite.at(object->id()).obj) {
|
|
if (brim_type == BrimType::btInnerOnly) {
|
|
// If brim_type is btInnerOnly, we actually doesn't generate loops for inner island.
|
|
// Only update islands_area_ex and return
|
|
for (auto it = hole_island_pair.begin(); it != hole_island_pair.end(); it++) {
|
|
ExPolygons islands_area_ex_object = intersection_ex(offset(it->second, brim_offset), offset(holes_area[it->first], -brim_offset));
|
|
for (const PrintInstance& instance : object->instances())
|
|
append_and_translate(islands_area_ex, islands_area_ex_object, instance);
|
|
}
|
|
brimToWrite.at(object->id()).obj == false;
|
|
}
|
|
else {
|
|
for (auto it = hole_island_pair.begin(); it != hole_island_pair.end(); it++) {
|
|
Polygons loops;
|
|
Polygons inner_islands = offset(it->second, brim_offset);
|
|
Polygons brimable_area = offset(holes_area[it->first], -brim_offset); //offset to keep away from hole
|
|
Polygons contour = offset(inner_islands, brim_offset + brim_width, jtRound, SCALED_RESOLUTION);
|
|
for (Polygon& poly : contour)
|
|
poly.douglas_peucker(SCALED_RESOLUTION);
|
|
|
|
|
|
//BBS: save all inner island and inner island brim area here, which is necesary if generate inner brim for holes
|
|
//Inner brim of holes must not occupy this area
|
|
ExPolygons islands_area_ex_object = intersection_ex(contour, brimable_area);
|
|
ExPolygons inner_islands_exp = offset_ex(inner_islands, 0.);
|
|
islands_area_ex_object = diff_ex(islands_area_ex_object, inner_islands_exp);
|
|
for (const PrintInstance& instance : object->instances())
|
|
append_and_translate(islands_area_ex, islands_area_ex_object, instance, print, innerbrimAreaMap);
|
|
}
|
|
brimToWrite.at(object->id()).obj == false;
|
|
}
|
|
if (innerbrimAreaMap.find(object->id()) != innerbrimAreaMap.end())
|
|
expolygons_append(islands_area_ex, innerbrimAreaMap[object->id()]);
|
|
}
|
|
|
|
|
|
if (support_material_extruder == extruderNo && brimToWrite.at(object->id()).sup) {
|
|
if (brim_type == BrimType::btInnerOnly) {
|
|
// If brim_type is btInnerOnly, we actually doesn't generate loops for inner island.
|
|
// Only update islands_area_ex and return
|
|
for (auto it = hole_island_pair_supports.begin(); it != hole_island_pair_supports.end(); it++) {
|
|
ExPolygons islands_area_ex_support = intersection_ex(offset(it->second, 0), offset(holes_area[it->first], 0));
|
|
for (const PrintInstance& instance : object->instances())
|
|
append_and_translate(islands_area_ex, islands_area_ex_support, instance);
|
|
}
|
|
brimToWrite.at(object->id()).sup == false;
|
|
}
|
|
else {
|
|
for (auto it = hole_island_pair_supports.begin(); it != hole_island_pair_supports.end(); it++) {
|
|
Polygons loops;
|
|
Polygons inner_islands = offset(it->second, 0);
|
|
Polygons brimable_area = offset(holes_area[it->first], -float(flow.scaled_spacing())); //offset to keep away from hole
|
|
Polygons contour = offset(inner_islands, brim_width, jtRound, SCALED_RESOLUTION);
|
|
for (Polygon& poly : contour)
|
|
poly.douglas_peucker(SCALED_RESOLUTION);
|
|
|
|
|
|
//BBS: save all inner island and inner island brim area here, which is necesary if generate inner brim for holes
|
|
//Inner brim of holes must not occupy this area
|
|
ExPolygons islands_area_ex_support = intersection_ex(contour, brimable_area);
|
|
ExPolygons inner_islands_exp = offset_ex(inner_islands, 0.);
|
|
islands_area_ex_support = diff_ex(islands_area_ex_support, inner_islands_exp);
|
|
for (const PrintInstance& instance : object->instances())
|
|
append_and_translate(islands_area_ex, islands_area_ex_support, instance, print, innerSupportBrimAreaMap);
|
|
|
|
}
|
|
brimToWrite.at(object->id()).sup == false;
|
|
}
|
|
if (innerSupportBrimAreaMap.find(object->id()) != innerSupportBrimAreaMap.end())
|
|
expolygons_append(islands_area_ex, innerSupportBrimAreaMap[object->id()]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
islands_area_ex = diff_ex(islands_area_ex, NobrimArea);
|
|
for (const PrintObject* object : print.objects()) {
|
|
if (innerbrimAreaMap.find(object->id()) != innerbrimAreaMap.end())
|
|
innerbrimAreaMap[object->id()] = diff_ex(innerbrimAreaMap[object->id()], NobrimArea);
|
|
if (innerSupportBrimAreaMap.find(object->id()) != innerSupportBrimAreaMap.end())
|
|
innerSupportBrimAreaMap[object->id()] = diff_ex(innerSupportBrimAreaMap[object->id()], NobrimArea);
|
|
}
|
|
}
|
|
static void make_inner_brim(const Print &print,
|
|
const ConstPrintObjectPtrs &top_level_objects_with_brim,
|
|
const std::vector<ExPolygons> &bottom_layers_expolygons,
|
|
ExtrusionEntityCollection &brim)
|
|
{
|
|
assert(print.objects().size() == bottom_layers_expolygons.size());
|
|
const auto scaled_resolution = scaled<double>(print.config().resolution.value);
|
|
|
|
//BBS: generate brim for inner island first
|
|
ExPolygons inner_islands_ex;
|
|
make_inner_island_brim(print, top_level_objects_with_brim, brim, inner_islands_ex);
|
|
|
|
#ifdef INNER_ISLAND_BRIM_DEBUG_TO_SVG
|
|
static int irun = 0;
|
|
BoundingBox bbox_svg;
|
|
bbox_svg.merge(get_extents(inner_islands_ex));
|
|
{
|
|
std::stringstream stri;
|
|
stri << "inner_island_and_brim_area_" << irun << ".svg";
|
|
SVG svg(stri.str(), bbox_svg);
|
|
svg.draw(to_polylines(inner_islands_ex), "blue");
|
|
svg.Close();
|
|
}
|
|
++ irun;
|
|
#endif
|
|
|
|
Flow flow = print.brim_flow();
|
|
ExPolygons islands_ex = inner_brim_area(print, top_level_objects_with_brim, bottom_layers_expolygons, float(flow.scaled_spacing()));
|
|
//BBS: brim of hole must not overlap with inner island and inner island brim
|
|
if (!inner_islands_ex.empty()) {
|
|
islands_ex = diff_ex(islands_ex, inner_islands_ex);
|
|
}
|
|
|
|
Polygons loops;
|
|
islands_ex = offset_ex(islands_ex, -0.5f * float(flow.scaled_spacing()));// jtSquare seems not working when expandign the holes
|
|
for (size_t i = 0; !islands_ex.empty(); ++i) {
|
|
for (ExPolygon &poly_ex : islands_ex)
|
|
poly_ex.douglas_peucker(scaled_resolution);
|
|
polygons_append(loops, to_polygons(islands_ex));// jtSquare seems not working when expandign the holes
|
|
islands_ex = offset_ex(islands_ex, -1.3f * float(flow.scaled_spacing()));
|
|
islands_ex = offset_ex(islands_ex, .3f * float(flow.scaled_spacing()));
|
|
}
|
|
|
|
loops = union_pt_chained_outside_in(loops);
|
|
std::reverse(loops.begin(), loops.end());
|
|
extrusion_entities_append_loops(brim.entities, std::move(loops), erBrim, float(flow.mm3_per_mm()),
|
|
float(flow.width()), float(print.skirt_first_layer_height()));
|
|
}
|
|
|
|
// BBS: generate inner brim by objs
|
|
static void make_inner_brim(const Print& print, const ConstPrintObjectPtrs& top_level_objects_with_brim,
|
|
std::map<ObjectID, ExPolygons>& brimAreaMap, std::map<ObjectID, ExPolygons>& supportBrimAreaMap,
|
|
std::vector<std::pair<ObjectID, unsigned int>>& objPrintVec)
|
|
{
|
|
//BBS: generate brim for inner island first
|
|
|
|
|
|
#ifdef INNER_ISLAND_BRIM_DEBUG_TO_SVG
|
|
static int irun = 0;
|
|
BoundingBox bbox_svg;
|
|
bbox_svg.merge(get_extents(inner_islands_ex));
|
|
{
|
|
std::stringstream stri;
|
|
stri << "inner_island_and_brim_area_" << irun << ".svg";
|
|
SVG svg(stri.str(), bbox_svg);
|
|
svg.draw(to_polylines(inner_islands_ex), "blue");
|
|
svg.Close();
|
|
}
|
|
++irun;
|
|
#endif
|
|
|
|
Flow flow = print.brim_flow();
|
|
ExPolygons NoBrim = inner_brim_area(print, top_level_objects_with_brim,
|
|
float(flow.scaled_spacing()), brimAreaMap, supportBrimAreaMap, objPrintVec);
|
|
|
|
ExPolygons inner_islands_ex;
|
|
std::map<ObjectID, ExPolygons> innerBrimAreaMap;
|
|
std::map<ObjectID, ExPolygons> innerSupportBrimAreaMap;
|
|
/*make_inner_island_brim(print, top_level_objects_with_brim, innerBrimAreaMap, innerSupportBrimAreaMap,
|
|
inner_islands_ex, NoBrim, objPrintVec);*/
|
|
|
|
//BBS: brim of hole must not overlap with inner island and inner island brim
|
|
if (!inner_islands_ex.empty()) {
|
|
if (brimAreaMap.size() > 0) {
|
|
for (auto iter = brimAreaMap.begin(); iter != brimAreaMap.end(); ++iter) {
|
|
if (!iter->second.empty()) {
|
|
iter->second = diff_ex(iter->second, inner_islands_ex);
|
|
};
|
|
}
|
|
}
|
|
if (supportBrimAreaMap.size() > 0) {
|
|
for (auto iter = supportBrimAreaMap.begin(); iter != supportBrimAreaMap.end(); ++iter) {
|
|
if (!iter->second.empty()) {
|
|
iter->second = diff_ex(iter->second, inner_islands_ex);
|
|
};
|
|
}
|
|
}
|
|
for (const PrintObject* object : print.objects()) {
|
|
if (innerBrimAreaMap.find(object->id()) != innerBrimAreaMap.end()) {
|
|
append(brimAreaMap[object->id()], innerBrimAreaMap[object->id()]);
|
|
}
|
|
if (innerSupportBrimAreaMap.find(object->id()) != innerSupportBrimAreaMap.end()) {
|
|
append(supportBrimAreaMap[object->id()], innerSupportBrimAreaMap[object->id()]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//BBS: generate out brim by offseting ExPolygons 'islands_area_ex'
|
|
Polygons tryExPolygonOffset(const ExPolygons islandAreaEx, const Print& print)
|
|
{
|
|
const auto scaled_resolution = scaled<double>(print.config().resolution.value);
|
|
Polygons loops;
|
|
ExPolygons islands_ex;
|
|
Flow flow = print.brim_flow();
|
|
|
|
double resolution = 0.0125 / SCALING_FACTOR;
|
|
islands_ex = islandAreaEx;
|
|
for (ExPolygon& poly_ex : islands_ex)
|
|
poly_ex.douglas_peucker(resolution);
|
|
islands_ex = offset_ex(std::move(islands_ex), -0.5f * float(flow.scaled_spacing()), jtRound, resolution);
|
|
for (size_t i = 0; !islands_ex.empty(); ++i) {
|
|
for (ExPolygon& poly_ex : islands_ex)
|
|
poly_ex.douglas_peucker(resolution);
|
|
polygons_append(loops, to_polygons(islands_ex));
|
|
islands_ex = offset_ex(std::move(islands_ex), -1.4f*float(flow.scaled_spacing()), jtRound, resolution);
|
|
for (ExPolygon& poly_ex : islands_ex)
|
|
poly_ex.douglas_peucker(resolution);
|
|
islands_ex = offset_ex(std::move(islands_ex), 0.4f*float(flow.scaled_spacing()), jtRound, resolution);
|
|
}
|
|
return loops;
|
|
}
|
|
//BBS: a function creates the ExtrusionEntityCollection from the brim area defined by ExPolygons
|
|
ExtrusionEntityCollection makeBrimInfill(const ExPolygons& singleBrimArea, const Print& print, const Polygons& islands_area) {
|
|
Polygons loops = tryExPolygonOffset(singleBrimArea, print);
|
|
Flow flow = print.brim_flow();
|
|
loops = union_pt_chained_outside_in(loops);
|
|
|
|
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({ std::move(loops_pl[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();
|
|
|
|
// Flip orientation of open polylines to minimize travel distance.
|
|
optimize_polylines_by_reversing(&all_loops);
|
|
all_loops = connect_brim_lines(std::move(all_loops), offset(singleBrimArea, float(SCALED_EPSILON)), float(flow.scaled_spacing()) * 2.f);
|
|
|
|
extrusion_entities_append_loops_and_paths(brim.entities, std::move(all_loops), erBrim, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()));
|
|
return brim;
|
|
}
|
|
|
|
//BBS: an overload of the orignal brim generator that generates the brim by obj and by extruders
|
|
void make_brim(const Print& print, PrintTryCancel try_cancel, Polygons& islands_area,
|
|
std::map<ObjectID, ExtrusionEntityCollection>& brimMap,
|
|
std::map<ObjectID, ExtrusionEntityCollection>& supportBrimMap,
|
|
std::vector<std::pair<ObjectID, unsigned int>> &objPrintVec,
|
|
std::vector<unsigned int>& printExtruders)
|
|
{
|
|
|
|
double brim_width_max = 0;
|
|
std::map<ObjectID, double> brim_width_map;
|
|
std::map<ObjectID, ExPolygons> brimAreaMap;
|
|
std::map<ObjectID, ExPolygons> supportBrimAreaMap;
|
|
Flow flow = print.brim_flow();
|
|
const auto scaled_resolution = scaled<double>(print.config().resolution.value);
|
|
ExPolygons islands_area_ex = outer_inner_brim_area(print,
|
|
float(flow.scaled_spacing()), brimAreaMap, supportBrimAreaMap, objPrintVec, printExtruders);
|
|
|
|
// BBS: Find boundingbox of the first layer
|
|
for (const ObjectID printObjID : print.print_object_ids()) {
|
|
BoundingBox bbx;
|
|
PrintObject* object = const_cast<PrintObject*>(print.get_object(printObjID));
|
|
for (const ExPolygon& ex_poly : object->layers().front()->lslices)
|
|
for (const PrintInstance& instance : object->instances()) {
|
|
auto ex_poly_translated = ex_poly;
|
|
ex_poly_translated.translate(instance.shift.x(), instance.shift.y());
|
|
bbx.merge(get_extents(ex_poly_translated.contour));
|
|
}
|
|
if (!object->support_layers().empty())
|
|
for (const Polygon& support_contour : object->support_layers().front()->support_fills.polygons_covered_by_spacing())
|
|
for (const PrintInstance& instance : object->instances()) {
|
|
auto ex_poly_translated = support_contour;
|
|
ex_poly_translated.translate(instance.shift.x(), instance.shift.y());
|
|
bbx.merge(get_extents(ex_poly_translated));
|
|
}
|
|
if (!object->tree_support_layers().empty())
|
|
for (const Polygon& ex_poly : object->tree_support_layers().front()->support_fills.polygons_covered_by_spacing())
|
|
for (const PrintInstance& instance : object->instances()) {
|
|
auto ex_poly_translated = ex_poly;
|
|
ex_poly_translated.translate(instance.shift.x(), instance.shift.y());
|
|
bbx.merge(get_extents(ex_poly_translated));
|
|
}
|
|
if (supportBrimAreaMap.find(printObjID) != supportBrimAreaMap.end()) {
|
|
for (const ExPolygon& ex_poly : supportBrimAreaMap.at(printObjID))
|
|
bbx.merge(get_extents(ex_poly.contour));
|
|
}
|
|
if (brimAreaMap.find(printObjID) != brimAreaMap.end()) {
|
|
for (const ExPolygon& ex_poly : brimAreaMap.at(printObjID))
|
|
bbx.merge(get_extents(ex_poly.contour));
|
|
}
|
|
object->firstLayerObjectBrimBoundingBox = bbx;
|
|
}
|
|
|
|
islands_area = to_polygons(islands_area_ex);
|
|
for (auto iter = brimAreaMap.begin(); iter != brimAreaMap.end(); ++iter) {
|
|
if (!iter->second.empty()) {
|
|
brimMap.insert(std::make_pair(iter->first, makeBrimInfill(iter->second, print, islands_area)));
|
|
};
|
|
}
|
|
for (auto iter = supportBrimAreaMap.begin(); iter != supportBrimAreaMap.end(); ++iter) {
|
|
if (!iter->second.empty()) {
|
|
supportBrimMap.insert(std::make_pair(iter->first, makeBrimInfill(iter->second, print, islands_area)));
|
|
};
|
|
}
|
|
|
|
size_t num_loops = size_t(floor(brim_width_max / flow.spacing()));
|
|
BOOST_LOG_TRIVIAL(debug) << "brim_width_max, num_loops: " << brim_width_max << ", " << num_loops;
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
double brim_width_max = 0;
|
|
std::map<ObjectID, double> brim_width_map;
|
|
const auto scaled_resolution = scaled<double>(print.config().resolution.value);
|
|
Flow flow = print.brim_flow();
|
|
std::vector<ExPolygons> bottom_layers_expolygons = get_print_bottom_layers_expolygons(print);
|
|
ConstPrintObjectPtrs top_level_objects_with_brim = get_top_level_objects_with_brim(print, bottom_layers_expolygons);
|
|
Polygons islands = top_level_outer_brim_islands(top_level_objects_with_brim, scaled_resolution);
|
|
ExPolygons islands_area_ex = top_level_outer_brim_area(print, top_level_objects_with_brim, bottom_layers_expolygons, float(flow.scaled_spacing()), brim_width_max, brim_width_map);
|
|
islands_area = to_polygons(islands_area_ex);
|
|
|
|
Polygons loops = tryExPolygonOffset(islands_area_ex, print);
|
|
size_t num_loops = size_t(floor(brim_width_max / flow.spacing()));
|
|
BOOST_LOG_TRIVIAL(debug) << "brim_width_max, num_loops: " << brim_width_max << ", " << num_loops;
|
|
|
|
loops = union_pt_chained_outside_in(loops);
|
|
|
|
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();
|
|
|
|
// Flip orientation of open polylines to minimize travel distance.
|
|
optimize_polylines_by_reversing(&all_loops);
|
|
|
|
#ifdef BRIM_DEBUG_TO_SVG
|
|
static int irun = 0;
|
|
++ irun;
|
|
|
|
{
|
|
SVG svg(debug_out_path("brim-%d.svg", irun).c_str(), get_extents(all_loops));
|
|
svg.draw(union_ex(islands), "blue");
|
|
svg.draw(islands_area_ex, "green");
|
|
svg.draw(all_loops, "black", coord_t(scale_(0.1)));
|
|
}
|
|
#endif // BRIM_DEBUG_TO_SVG
|
|
|
|
all_loops = connect_brim_lines(std::move(all_loops), offset(islands_area_ex, float(SCALED_EPSILON)), float(flow.scaled_spacing()) * 2.f);
|
|
|
|
#ifdef BRIM_DEBUG_TO_SVG
|
|
{
|
|
SVG svg(debug_out_path("brim-connected-%d.svg", irun).c_str(), get_extents(all_loops));
|
|
svg.draw(union_ex(islands), "blue");
|
|
svg.draw(islands_area_ex, "green");
|
|
svg.draw(all_loops, "black", coord_t(scale_(0.1)));
|
|
}
|
|
#endif // BRIM_DEBUG_TO_SVG
|
|
|
|
const bool could_brim_intersects_skirt = std::any_of(print.objects().begin(), print.objects().end(), [&print, &brim_width_map, brim_width_max](PrintObject *object) {
|
|
const BrimType &bt = object->config().brim_type;
|
|
return (bt == btOuterOnly || bt == btOuterAndInner || bt == btAutoBrim) && print.config().skirt_distance.value < brim_width_map[object->id()];
|
|
});
|
|
|
|
const bool draft_shield = print.config().draft_shield != dsDisabled;
|
|
|
|
|
|
// 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 (draft_shield && ! 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::pftNonZero, ClipperLib_Z::pftNonZero);
|
|
}
|
|
|
|
// 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::pftNonZero, ClipperLib_Z::pftNonZero);
|
|
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(erBrim, 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(erBrim, 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 = dynamic_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), erBrim, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()));
|
|
}
|
|
|
|
make_inner_brim(print, top_level_objects_with_brim, bottom_layers_expolygons, brim);
|
|
return brim;
|
|
}
|
|
|
|
} // namespace Slic3r
|