Files
OrcaSlicer-KX/src/libslic3r/Brim.cpp
SoftFever 1c30aa8abe Feature/merge 1.8.4 (#3827)
* FIX: the logic of buried points that were not buried

JIRA: none

Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com>
Change-Id: Id95174659c5fce7feba409eb5e14916608745fa4

* ci: update network module based on commit bc7ca98

Change-Id: I923526f0bf9ce5a288144fa1f9b0f2fc640f41b7

* Fix Firefox
Co-authored-by: hadess <hadess@hadess.net>

* FIX: cali: custom created filament from AMS displayed as incompatible

jira: new

remove the condition: is_system

Change-Id: Ib1366966bbdbe01bc9e2483d9914d270ebefa976

* FIX: duplicated items in comboBox at calibration completed page

jira: new

Change-Id: I4749a2206df16c438e0d3098e36274b2a20f313e

* ENH:update support for P1S plus

jira:[for p1s plus]

Change-Id: Id577d4e94e2162cb0045d261dfaa5f396ecded2f

* ENH: CLI: add mk information support

JIRA: no jira
Change-Id: Idd89b143d439de50d9f52eb8aec95b262d66875d

* ENH:calibration support p1p plus

jira:[plus]

Change-Id: Ia290d3a8a8b9adaac7a6ee26d9a8b5ea0c1b3aee

* FIX: add log for base_id and filament_id

github: #3087

Change-Id: Iebfbd0f224fce49f33fc81c71e6108f6e3abb5ff

* FIX: sync whole preset vendor directory

Change-Id: I191dbe979a87ff35d38cab1149b7975664344838
Jira: STUDIO-5534
(cherry picked from commit 628866608116336453804aa1217dd55db04d47ad)

* FIX: use t_utc for debug only

Change-Id: Ia05d8969d4de3dd38908980d6e17a3ebb11ca279
Github 3045

Change-Id: I77935df53bbf2772b1146e5c330c537165a3a2e6

* FIX:make sort_volumes right

Jira: STUDIO-5645
Change-Id: If324c9115bfaaf0c1b7b4be7c7ee96ba6b8ac890

* ENH:keep an unload logic

jira:[for unload]

Change-Id: Id30ec71ffa5b2dac89346ea47ca48a62479e3ab1

* FIX: several problems with mesh boolean

1. Cut with multiple volumes are OK now.
2. Close mesh boolean fail error with new object or open object
3. Fix wrong name and config of boolean resulting object

github: #3118
jira: none

Change-Id: If2c9dbfb36cbdfe4917a2371217923891bb7909c
(cherry picked from commit 982c0ecb92cf7c2b5ae5972ab900a6b10e7dda50)

* NEW:limit the length of project name

jira:[project name]

Change-Id: I955620f7073b3b7fda280d1118524f561d047751

* ENH:adjusting the warning level of timelpase

jira:[STUDIO-5662]

Change-Id: I4902b22d316f5e09a97a62c88b8a98e55c405434

* FIX: 3mf specification: change namespace form slic3rpe to BambuStudio

Jira: XXXX

Change-Id: Id705affc875ef23fdf2ac6d79f0cb0aafc4f7050

* NEW: Open MakerWorld With BambuStudio GetParam

JIRA: none
Change-Id: I0d65b364f1cd2d634a88882ab072c3e61ea89167
(cherry picked from commit 8eaf45e5359439a7c796fd79876c86775abcf48e)

* FIX: Filament issue generated when creating a printer

Jira: XXXX

Change-Id: I976770b69b47641bd54aa7a9c56fba7f58d1ab68
(cherry picked from commit ba42188b93c58b3954234d72acdd9769a68e3d3c)

* FIX: Blank page appears when editing presets

Jira: 5563

Change-Id: I4c49e05515b1beff55991e92f8079c4499c27eab
(cherry picked from commit e86517d290f4cd0765a230d811b0ddf2c9f34c17)

* FIX: context menu didn't update UI

jira: STUDIO-5691

Change-Id: Ia66b8623d832eba805aff5320941233a68ff258b

* FIX: crash of "filling bed"

"get_arrange_settings() const" gets trapped in infinite recursive calling.
Now we delete this function.

jira: STUDIO-5688
Change-Id: Ia39974734bb37b2a2f06b5bf78185f01be726872

* FIX: boolean hangs in the middle of color painting

Can't do splits in combine_mesh_fff, as do_boolean of mcut will split meshes.

jira: STUDIO-5693
Change-Id: Idddb7d20dd7ca386c39ddd3d87d9defc3136aa5d
(cherry picked from commit 6c67d015941458e37faaf0015b6509b5a0eadc0e)

* Fix: Fix a number of compilation problems

issues found when using gcc version 13.2.0 (GCC) in a Flatpak sandbox

github : https://github.com/bambulab/BambuStudio/issues/3074
github pull request: https://github.com/bambulab/BambuStudio/pull/3096

Change-Id: I08aeac593eb1ce7675894df72e8489200bae713d
(cherry picked from commit 069d133d66bfa682de4a860e379d5dc16b3d907c)

* fix: macos icns issue when icon was not attached

github pull request:https://github.com/bambulab/BambuStudio/pull/3116

Change-Id: I49072ad49f3af7669a6d307c791594ade210da50
(cherry picked from commit c977e5582e3a30ad16dd267810037423aad9a53c)

* FIX: Add flush_length for change_filament_gcode

Change-Id: I30f4b97d3d61c2a57f0e92f157cbd31c38aa7265
Jira: XXXX
(cherry picked from commit 92eb2bac977a0c4095b316cbbc6580fb5228b710)

* FIX: edit preset dialog can't close on mac

Jira: 5696

Change-Id: Ib33dfd07cc588ddd3805e3490a4d8c36dcd890ac

* ENH: add dev_ota_version in ssdp

JIRA: STUDIO-5740

Change-Id: Ic80e6d4b9bf82813fdc4a76604a3d36213d12b03
Signed-off-by: Stone Li <stone.li@bambulab.com>

* NEW:Adapt to multicolour and gradient colour

JIRA:xxxx
Change-Id: I8084cab603d5681cbcaf2d6f5e0d7ad5419cb2af

* NEW:Adaptation of semi transparent materials

JIRA: XXXX
Change-Id: Ie32d8ce67c37b85eb6d7d6594cb514a696307e68

* FIX: disable flush options if prime tower is unchecked

jira: STUDIO-5639

Change-Id: I25081584d430bc7d062e14bcc2cdbf7522cf9d99

* ENH: refine GetVersion for HMS query

JIRA: STUDIO-5763

Change-Id: Ia3ccc07d79cc0736eb12e9782da50211abb74772
Signed-off-by: Stone Li <stone.li@bambulab.com>

* FIX: Prefer old selection when sync AMS not compatible

Change-Id: I6b18db51887132a997cf78d70fff9a92e23bc44a
Jira: STUDIO-5416
(cherry picked from commit 077fae29823cf4f3071d408b1b40f55ee0cb33c6)

* FIX: The flushing was not auto-calc when sync ams list

JIRA: STUDIO-5551

1. flushing volume auto-calc when sync ams list
2. flushing volume takes the larger calculation value when filament has
   multi-colors

Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com>
Change-Id: I72e6f9780ea56103a44c2da6068440a4615c254d

* FIX:fixed invalid links

jira:[fixed link]

Change-Id: I036a38b6e8e94da762f93805bd7be706538771fe

* FIX: Prompt to delete problematic presets

Jira: XXXX

Change-Id: Ic43f7bb782794d7ab0b6acbffbb5d73e94f6ed73

* FIX:fixed incorrect HMS content

jira:[STUDIO-5818]

Change-Id: Ia2896d6f0ab1ffedbc850e54168acece8e47bdbb

* FIX:external transparent material display error

JIRA: STUDIO-5845
Change-Id: I0a4f05ac5d5c0ac49d85a704ee65a7221c5f1e1d

* FIX: [5846] Custom Filament Page show System Filament

Simultaneously solve: When downloading Preset from the cloud, the filament_id of the preset in m_preset is null.

Jira: 5846

Change-Id: I6ba1b46fe92e345614b6a4af3fffa87d81fa2456

* FIX:A1 and p1 series do not support custom materials

JIRA:XXXX
Change-Id: Ib0459273d1f9a7152a5563757204634a8d0cd6f5

* FIX: exception when comparing profiles

jira:[NEW]

Signed-off-by: XunZhangBambu <xun.zhang@bambulab.com>
Change-Id: I946b5fcd35f779d271df2b3de731fdcada5aab29
(cherry picked from commit 00e739570812e5c4be3e0f7702ce8c72c0f9e72b)

* FIX: hide_id_middle_string

Change-Id: I28f32ec526b443d31d7992971b80ab1cb737deb6
Github: STUDIO-5825

* ENH: modify some logs level

JIRA: STUDIO-5958

Change-Id: I5a8592dfb8ffa9a81952535cb30944f867aa0e22
Signed-off-by: Stone Li <stone.li@bambulab.com>

* NEW:build plate marker detect

Change-Id: I70f03efea688bb6ce71c3f5990bb3c50605ab184

* FIX: Studio UI Freeze when saving user preset

github: #3335

Change-Id: Idaf53f673a3e46408826c06bdde2c592395d358b

* update bbl plugin version

* fix build errors

* update bbl profiles

* update color

---------

Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com>
Signed-off-by: Stone Li <stone.li@bambulab.com>
Co-authored-by: Kunlong Ma <kunlong.ma@bambulab.com>
Co-authored-by: gerrit <gerrit@bambulab.com>
Co-authored-by: liz.li <liz.li@bambulab.com>
Co-authored-by: tao wang <tao.wang@bambulab.com>
Co-authored-by: lane.wei <lane.wei@bambulab.com>
Co-authored-by: maosheng.wei <maosheng.wei@bambulab.com>
Co-authored-by: chunmao.guo <chunmao.guo@bambulab.com>
Co-authored-by: zhou.xu <zhou.xu@bambulab.com>
Co-authored-by: Arthur <arthur.tang@bambulab.com>
Co-authored-by: Bastien Nocera <hadess@hadess.net>
Co-authored-by: zhimin.zeng <zhimin.zeng@bambulab.com>
Co-authored-by: hu.wang <hu.wang@bambulab.com>
Co-authored-by: Stone Li <stone.li@bambulab.com>
Co-authored-by: XunZhangBambu <xun.zhang@bambulab.com>
2024-01-26 20:18:10 +08:00

1932 lines
98 KiB
C++

///|/ Copyright (c) Prusa Research 2021 - 2023 Vojtěch Bubník @bubnikv, Lukáš Matěna @lukasmatena, Lukáš Hejl @hejllukas
///|/ Copyright (c) SuperSlicer 2023 Remi Durand @supermerill
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#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>
#include <boost/log/trivial.hpp>
#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);
Point instance_shift = instance.shift_without_plate_offset();
for (; dst_idx < dst.size(); ++dst_idx)
dst[dst_idx].translate(instance_shift);
}
// 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;
Point instance_shift = instance.shift_without_plate_offset();
for (size_t src_idx = 0; src_idx < srcShifted.size(); ++src_idx)
srcShifted[src_idx].translate(instance_shift);
srcShifted = diff_ex(srcShifted, dst);
//expolygons_append(dst, temp2);
expolygons_append(brimAreaMap[instance.print_object->id()], std::move(srcShifted));
}
static void append_and_translate(Polygons &dst, const Polygons &src, const PrintInstance &instance) {
size_t dst_idx = dst.size();
polygons_append(dst, src);
Point instance_shift = instance.shift_without_plate_offset();
for (; dst_idx < dst.size(); ++dst_idx)
dst[dst_idx].translate(instance_shift);
}
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()) {
Point instance_shift = instance.shift_without_plate_offset();
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));
}
}
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);
}
}
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);
}
}
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);
}
}
}
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 == "PETG") {
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, double &groupHeight)
{
// 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);
groupHeight = height;
// 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)) * height / 1920;
double brim_width = adhension * std::min(std::min(std::max(height_to_area * maxSpeed, 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;
}
// Generate ears
// Ported from SuperSlicer: https://github.com/supermerill/SuperSlicer/blob/45d0532845b63cd5cefe7de7dc4ef0e0ed7e030a/src/libslic3r/Brim.cpp#L1116
static ExPolygons make_brim_ears(ExPolygons& obj_expoly, coord_t size_ear, coord_t ear_detection_length,
coordf_t brim_ears_max_angle, bool is_outer_brim) {
ExPolygons mouse_ears_ex;
if (size_ear <= 0) {
return mouse_ears_ex;
}
// Detect places to put ears
const coordf_t angle_threshold = (180 - brim_ears_max_angle) * PI / 180.0;
Points pt_ears;
for (ExPolygon &poly : obj_expoly) {
Polygon decimated_polygon = poly.contour;
if (ear_detection_length > 0) {
// decimate polygon
Points points = poly.contour.points;
points.push_back(points.front());
points = MultiPoint::_douglas_peucker(points, ear_detection_length);
if (points.size() > 4) { // don't decimate if it's going to be below 4 points, as it's surely enough to fill everything anyway
points.erase(points.end() - 1);
decimated_polygon.points = points;
}
}
append(pt_ears, is_outer_brim ? decimated_polygon.convex_points(angle_threshold)
: decimated_polygon.concave_points(angle_threshold));
}
// Then add ears
// create ear pattern
Polygon point_round;
for (size_t i = 0; i < POLY_SIDES; i++) {
double angle = (2.0 * PI * i) / POLY_SIDES;
point_round.points.emplace_back(size_ear * cos(angle), size_ear * sin(angle));
}
// create ears
for (Point &pt : pt_ears) {
mouse_ears_ex.emplace_back();
mouse_ears_ex.back().contour = point_round;
mouse_ears_ex.back().contour.translate(pt);
}
return mouse_ears_ex;
}
//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;
Flow flow = print.brim_flow();
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} });
ExPolygons objectIslands;
auto bedPoly = Model::getBedPolygon();
auto bedExPoly = diff_ex((offset(bedPoly, scale_(30.), jtRound, SCALED_RESOLUTION)), { bedPoly });
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;
const bool use_brim_ears = object->config().brim_type == btEar;
const bool has_inner_brim = brim_type == btInnerOnly || brim_type == btOuterAndInner || use_brim_ears;
const bool has_outer_brim = brim_type == btOuterOnly || brim_type == btOuterAndInner || brim_type == btAutoBrim || use_brim_ears;
coord_t ear_detection_length = scale_(object->config().brim_ears_detection_length.value);
coordf_t brim_ears_max_angle = object->config().brim_ears_max_angle.value;
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;
//BBS: support shared object logic
const PrintObject* shared_object = object->get_shared_object();
if (!shared_object)
shared_object = object;
for (auto volumePtr : shared_object->model_object()->volumes) {
if (volumePtr->id() == volumeID) {
currentModelVolumePtr = volumePtr;
break;
}
}
if (currentModelVolumePtr != nullptr) groupVolumePtrs.push_back(currentModelVolumePtr);
}
if (groupVolumePtrs.empty()) continue;
double groupHeight = 0.;
// config brim width in auto-brim mode
if (has_brim_auto) {
double brimWidthRaw = configBrimWidthByVolumeGroups(adhension, maxSpeed, groupVolumePtrs, volumeGroup.slices, groupHeight);
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 && groupHeight > 10.) {
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 (has_outer_brim) {
// 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);
auto &clipExpoly = innerExpoly;
if (use_brim_ears) {
coord_t size_ear = (brim_width_mod - brim_offset - flow.scaled_spacing());
append(brim_area_object, diff_ex(make_brim_ears(innerExpoly, size_ear, ear_detection_length, brim_ears_max_angle, true), clipExpoly));
} else {
// Normal brims
append(brim_area_object, diff_ex(offset_ex(innerExpoly, brim_width_mod, jtRound, SCALED_RESOLUTION), clipExpoly));
}
}
if (has_inner_brim) {
auto outerExpoly = offset_ex(ex_poly_holes_reversed, -brim_offset);
auto clipExpoly = offset_ex(ex_poly_holes_reversed, -brim_width - brim_offset);
if (use_brim_ears) {
coord_t size_ear = (brim_width - brim_offset - flow.scaled_spacing());
append(brim_area_object, diff_ex(make_brim_ears(outerExpoly, size_ear, ear_detection_length, brim_ears_max_angle, false), clipExpoly));
} else {
// Normal brims
append(brim_area_object, diff_ex(outerExpoly, clipExpoly));
}
}
if (!has_inner_brim) {
// 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 (!has_outer_brim)
append(no_brim_area_object, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly_holes_reversed));
if (!has_inner_brim && !has_outer_brim)
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(objectIslands, 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() && object->support_layers().front()->support_type==stInnerNormal) {
for (const Polygon& support_contour : object->support_layers().front()->support_fills.polygons_covered_by_spacing()) {
// Brim will not be generated for supports
/*
if (has_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 (has_inner_brim || has_outer_brim)
append(no_brim_area_support, offset_ex(support_contour, 0));
*/
no_brim_area_support.emplace_back(support_contour);
}
}
// BBS
if (!object->support_layers().empty() && object->support_layers().front()->support_type == stInnerTree) {
for (const ExPolygon &ex_poly : object->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 (has_outer_brim) {
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 (has_inner_brim)
append(brim_area_support, diff_ex(offset_ex(ex_poly.holes, -brim_offset), offset_ex(ex_poly.holes, -brim_width - brim_offset)));
*/
if (!has_outer_brim)
append(no_brim_area_support, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly.holes));
if (!has_inner_brim && !has_outer_brim)
append(no_brim_area_support, offset_ex(ex_poly.holes, -no_brim_offset));
append(holes_support, ex_poly.holes);
if (has_inner_brim || has_outer_brim)
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()]);
}
}
}
if (!bedExPoly.empty()){
no_brim_area.push_back(bedExPoly.front());
}
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.clear();
for (const PrintObject* object : print.objects()) {
// BBS: brim should be contacted to at least one object's island or brim area
if (brimAreaMap.find(object->id()) != brimAreaMap.end()) {
// find other objects' brim area
ExPolygons otherExPolys;
for (const PrintObject* otherObject : print.objects()) {
if ((otherObject->id() != object->id()) && (brimAreaMap.find(otherObject->id()) != brimAreaMap.end())) {
expolygons_append(otherExPolys, brimAreaMap[otherObject->id()]);
}
}
auto tempArea = brimAreaMap[object->id()];
brimAreaMap[object->id()].clear();
for (int ia = 0; ia != tempArea.size(); ++ia) {
// find this object's other brim area
ExPolygons otherExPoly;
for (int iao = 0; iao != tempArea.size(); ++iao)
if (iao != ia) otherExPoly.push_back(tempArea[iao]);
auto offsetedTa = offset_ex(tempArea[ia], print.brim_flow().scaled_spacing() * 2, jtRound, SCALED_RESOLUTION);
if (!intersection_ex(offsetedTa, objectIslands).empty() ||
!intersection_ex(offsetedTa, otherExPoly).empty() ||
!intersection_ex(offsetedTa, otherExPolys).empty())
brimAreaMap[object->id()].push_back(tempArea[ia]);
}
expolygons_append(brim_area, brimAreaMap[object->id()]);
}
}
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);
}
}
//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());
Point instance_shift = instance.shift_without_plate_offset();
for (; dst_idx < final_loops.size(); ++dst_idx)
final_loops[dst_idx].translate(instance_shift);
}
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);
}
}
//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.3f*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.3f*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);
//BBS: finally apply the plate offset which may very large
auto plate_offset = print.get_plate_origin();
Point scaled_plate_offset = Point(scaled(plate_offset.x()), scaled(plate_offset.y()));
for (Polyline& one_loop : all_loops)
one_loop.translate(scaled_plate_offset);
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_without_plate_offset());
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_without_plate_offset());
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);
// BBS: plate offset is applied
const Vec3d plate_offset = print.get_plate_origin();
Point plate_shift = Point(scaled(plate_offset.x()), scaled(plate_offset.y()));
for (size_t iia = 0; iia < islands_area.size(); ++iia)
islands_area[iia].translate(plate_shift);
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(std::move(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