* 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>
1932 lines
98 KiB
C++
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
|