Merge mainstream changes

This commit is contained in:
SoftFever 2022-10-04 12:24:49 +08:00
commit 764b7d62a0
204 changed files with 5425 additions and 1411 deletions

View file

@ -285,6 +285,10 @@ void AppConfig::set_defaults()
set("backup_interval", "10");
}
if (get("curr_bed_type").empty()) {
set("curr_bed_type", "0");
}
// #if BBL_RELEASE_TO_PUBLIC
if (get("iot_environment").empty()) {
set("iot_environment", "3");
@ -295,6 +299,10 @@ void AppConfig::set_defaults()
// }
// #endif
if (get("uniform_scale").empty()) {
set("uniform_scale", "1");
}
// Remove legacy window positions/sizes
erase("app", "main_frame_maximized");
erase("app", "main_frame_pos");
@ -525,7 +533,7 @@ void AppConfig::save()
{
// Returns "undefined" if the thread naming functionality is not supported by the operating system.
std::optional<std::string> current_thread_name = get_current_thread_name();
if (current_thread_name && *current_thread_name != "bambustudio_main")
if (current_thread_name && *current_thread_name != "bambustu_main")
throw CriticalException("Calling AppConfig::save() from a worker thread!");
}
@ -777,7 +785,7 @@ void AppConfig::save()
{
// Returns "undefined" if the thread naming functionality is not supported by the operating system.
std::optional<std::string> current_thread_name = get_current_thread_name();
if (current_thread_name && *current_thread_name != "bambustudio_main")
if (current_thread_name && *current_thread_name != "bambustu_main")
throw CriticalException("Calling AppConfig::save() from a worker thread!");
}

View file

@ -337,7 +337,7 @@ static ExPolygons top_level_outer_brim_area(const Print& print, const ConstPrint
no_brim_area_object.emplace_back(ex_poly.contour);
}
brimToWrite.at(object->id()).obj == false;
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);
@ -375,7 +375,7 @@ static ExPolygons top_level_outer_brim_area(const Print& print, const ConstPrint
no_brim_area_support.emplace_back(ex_poly.contour);
}
}
brimToWrite.at(object->id()).sup == false;
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);
@ -521,7 +521,7 @@ static ExPolygons inner_brim_area(const Print& print, const ConstPrintObjectPtrs
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;
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);
@ -564,7 +564,7 @@ static ExPolygons inner_brim_area(const Print& print, const ConstPrintObjectPtrs
}
}
}
brimToWrite.at(object->id()).sup == false;
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);
@ -1386,7 +1386,7 @@ static void make_inner_island_brim(const Print& print, const ConstPrintObjectPtr
for (const PrintInstance& instance : object->instances())
append_and_translate(islands_area_ex, islands_area_ex_object, instance);
}
brimToWrite.at(object->id()).obj == false;
brimToWrite.at(object->id()).obj = false;
}
else {
for (auto it = hole_island_pair.begin(); it != hole_island_pair.end(); it++) {
@ -1406,7 +1406,7 @@ static void make_inner_island_brim(const Print& print, const ConstPrintObjectPtr
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;
brimToWrite.at(object->id()).obj = false;
}
if (innerbrimAreaMap.find(object->id()) != innerbrimAreaMap.end())
expolygons_append(islands_area_ex, innerbrimAreaMap[object->id()]);
@ -1422,7 +1422,7 @@ static void make_inner_island_brim(const Print& print, const ConstPrintObjectPtr
for (const PrintInstance& instance : object->instances())
append_and_translate(islands_area_ex, islands_area_ex_support, instance);
}
brimToWrite.at(object->id()).sup == false;
brimToWrite.at(object->id()).sup = false;
}
else {
for (auto it = hole_island_pair_supports.begin(); it != hole_island_pair_supports.end(); it++) {
@ -1443,7 +1443,7 @@ static void make_inner_island_brim(const Print& print, const ConstPrintObjectPtr
append_and_translate(islands_area_ex, islands_area_ex_support, instance, print, innerSupportBrimAreaMap);
}
brimToWrite.at(object->id()).sup == false;
brimToWrite.at(object->id()).sup = false;
}
if (innerSupportBrimAreaMap.find(object->id()) != innerSupportBrimAreaMap.end())
expolygons_append(islands_area_ex, innerSupportBrimAreaMap[object->id()]);

View file

@ -67,8 +67,6 @@ set(lisbslic3r_sources
Fill/FillBase.hpp
Fill/FillConcentric.cpp
Fill/FillConcentric.hpp
Fill/FillConcentricWGapFill.cpp
Fill/FillConcentricWGapFill.hpp
Fill/FillConcentricInternal.cpp
Fill/FillConcentricInternal.hpp
Fill/FillHoneycomb.cpp

View file

@ -12,6 +12,7 @@
#include "FillBase.hpp"
#include "FillRectilinear.hpp"
#include "FillConcentricInternal.hpp"
#include "FillConcentric.hpp"
#define NARROW_INFILL_AREA_THRESHOLD 3
@ -407,6 +408,11 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
assert(fill_concentric != nullptr);
fill_concentric->print_config = &this->object()->print()->config();
fill_concentric->print_object_config = &this->object()->config();
} else if (surface_fill.params.pattern == ipConcentric) {
FillConcentric *fill_concentric = dynamic_cast<FillConcentric *>(f.get());
assert(fill_concentric != nullptr);
fill_concentric->print_config = &this->object()->print()->config();
fill_concentric->print_object_config = &this->object()->config();
}
// calculate flow spacing for infill pattern generation
@ -434,6 +440,7 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
params.anchor_length = surface_fill.params.anchor_length;
params.anchor_length_max = surface_fill.params.anchor_length_max;
params.resolution = resolution;
params.use_arachne = surface_fill.params.pattern == ipConcentric;
// BBS
params.flow = surface_fill.params.flow;

View file

@ -9,6 +9,7 @@
#include "../PrintConfig.hpp"
#include "../Surface.hpp"
#include "../libslic3r.h"
#include "../VariableWidth.hpp"
#include "FillBase.hpp"
#include "FillConcentric.hpp"
@ -21,7 +22,6 @@
#include "FillAdaptive.hpp"
#include "FillLightning.hpp"
// BBS: new infill pattern header
#include "FillConcentricWGapFill.hpp"
#include "FillConcentricInternal.hpp"
// #define INFILL_DEBUG_OUTPUT
@ -58,7 +58,6 @@ Fill* Fill::new_from_type(const InfillPattern type)
case ipLightning: return new FillLightning::Filler();
#endif // HAS_LIGHTNING_INFILL
// BBS: for internal solid infill only
case ipConcentricGapFill: return new FillConcentricWGapFill();
case ipConcentricInternal: return new FillConcentricInternal();
// BBS: for bottom and top surface only
case ipMonotonicLine: return new FillMonotonicLineWGapFill();
@ -107,16 +106,31 @@ Polylines Fill::fill_surface(const Surface *surface, const FillParams &params)
return polylines_out;
}
ThickPolylines Fill::fill_surface_arachne(const Surface* surface, const FillParams& params)
{
// Perform offset.
Slic3r::ExPolygons expp = offset_ex(surface->expolygon, float(scale_(this->overlap - 0.5 * this->spacing)));
// Create the infills for each of the regions.
ThickPolylines thick_polylines_out;
for (ExPolygon& expoly : expp)
_fill_surface_single(params, surface->thickness_layers, _infill_direction(surface), std::move(expoly), thick_polylines_out);
return thick_polylines_out;
}
// BBS: this method is used to fill the ExtrusionEntityCollection. It call fill_surface by default
void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& params, ExtrusionEntitiesPtr& out)
{
Polylines polylines;
ThickPolylines thick_polylines;
try {
polylines = this->fill_surface(surface, params);
if (params.use_arachne)
thick_polylines = this->fill_surface_arachne(surface, params);
else
polylines = this->fill_surface(surface, params);
}
catch (InfillFailedException&) {}
if (!polylines.empty()) {
if (!polylines.empty() || !thick_polylines.empty()) {
// calculate actual flow from spacing (which might have been adjusted by the infill
// pattern generator)
double flow_mm3_per_mm = params.flow.mm3_per_mm();
@ -136,10 +150,17 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para
out.push_back(eec = new ExtrusionEntityCollection());
// Only concentric fills are not sorted.
eec->no_sort = this->no_sort();
extrusion_entities_append_paths(
eec->entities, std::move(polylines),
params.extrusion_role,
flow_mm3_per_mm, float(flow_width), params.flow.height());
if (params.use_arachne) {
Flow new_flow = params.flow.with_spacing(float(this->spacing));
variable_width(thick_polylines, params.extrusion_role, new_flow, eec->entities);
thick_polylines.clear();
}
else {
extrusion_entities_append_paths(
eec->entities, std::move(polylines),
params.extrusion_role,
flow_mm3_per_mm, float(flow_width), params.flow.height());
}
}
}

View file

@ -63,6 +63,9 @@ struct FillParams
// in this case we don't try to make more continuous paths
bool complete { false };
// For Concentric infill, to switch between Classic and Arachne.
bool use_arachne{ false };
// BBS
Flow flow;
ExtrusionRole extrusion_role{ ExtrusionRole(0) };
@ -121,6 +124,7 @@ public:
// Perform the fill.
virtual Polylines fill_surface(const Surface *surface, const FillParams &params);
virtual ThickPolylines fill_surface_arachne(const Surface* surface, const FillParams& params);
// BBS: this method is used to fill the ExtrusionEntityCollection.
// It call fill_surface by default
@ -149,6 +153,13 @@ protected:
ExPolygon /* expolygon */,
Polylines & /* polylines_out */) {};
// Used for concentric infill to generate ThickPolylines using Arachne.
virtual void _fill_surface_single(const FillParams& params,
unsigned int thickness_layers,
const std::pair<float, Point>& direction,
ExPolygon expolygon,
ThickPolylines& thick_polylines_out) {}
virtual float _layer_angle(size_t idx) const { return (idx & 1) ? float(M_PI/2.) : 0; }
virtual std::pair<float, Point> _infill_direction(const Surface *surface) const;

View file

@ -1,6 +1,8 @@
#include "../ClipperUtils.hpp"
#include "../ExPolygon.hpp"
#include "../Surface.hpp"
#include "../VariableWidth.hpp"
#include "Arachne/WallToolPaths.hpp"
#include "FillConcentric.hpp"
@ -61,4 +63,84 @@ void FillConcentric::_fill_surface_single(
// We want the loops to be split inside the G-code generator to get optimum path planning.
}
void FillConcentric::_fill_surface_single(const FillParams& params,
unsigned int thickness_layers,
const std::pair<float, Point>& direction,
ExPolygon expolygon,
ThickPolylines& thick_polylines_out)
{
assert(params.use_arachne);
assert(this->print_config != nullptr && this->print_object_config != nullptr);
// no rotation is supported for this infill pattern
Point bbox_size = expolygon.contour.bounding_box().size();
coord_t min_spacing = scaled<coord_t>(this->spacing);
if (params.density > 0.9999f && !params.dont_adjust) {
coord_t loops_count = std::max(bbox_size.x(), bbox_size.y()) / min_spacing + 1;
Polygons polygons = offset(expolygon, float(min_spacing) / 2.f);
double min_nozzle_diameter = *std::min_element(print_config->nozzle_diameter.values.begin(), print_config->nozzle_diameter.values.end());
Arachne::WallToolPathsParams input_params;
input_params.min_bead_width = 0.85 * min_nozzle_diameter;
input_params.min_feature_size = 0.1;
input_params.wall_transition_length = 1.0 * min_nozzle_diameter;
input_params.wall_transition_angle = 10;
input_params.wall_transition_filter_deviation = 0.25 * min_nozzle_diameter;
input_params.wall_distribution_count = 1;
input_params.wall_add_middle_threshold = 0.75;
input_params.wall_split_middle_threshold = 0.5;
Arachne::WallToolPaths wallToolPaths(polygons, min_spacing, min_spacing, loops_count, 0, input_params);
std::vector<Arachne::VariableWidthLines> loops = wallToolPaths.getToolPaths();
std::vector<const Arachne::ExtrusionLine*> all_extrusions;
for (Arachne::VariableWidthLines& loop : loops) {
if (loop.empty())
continue;
for (const Arachne::ExtrusionLine& wall : loop)
all_extrusions.emplace_back(&wall);
}
// Split paths using a nearest neighbor search.
size_t firts_poly_idx = thick_polylines_out.size();
Point last_pos(0, 0);
for (const Arachne::ExtrusionLine* extrusion : all_extrusions) {
if (extrusion->empty())
continue;
ThickPolyline thick_polyline = Arachne::to_thick_polyline(*extrusion);
if (extrusion->is_closed && thick_polyline.points.front() == thick_polyline.points.back() && thick_polyline.width.front() == thick_polyline.width.back()) {
thick_polyline.points.pop_back();
assert(thick_polyline.points.size() * 2 == thick_polyline.width.size());
int nearest_idx = last_pos.nearest_point_index(thick_polyline.points);
std::rotate(thick_polyline.points.begin(), thick_polyline.points.begin() + nearest_idx, thick_polyline.points.end());
std::rotate(thick_polyline.width.begin(), thick_polyline.width.begin() + 2 * nearest_idx, thick_polyline.width.end());
thick_polyline.points.emplace_back(thick_polyline.points.front());
}
thick_polylines_out.emplace_back(std::move(thick_polyline));
last_pos = thick_polylines_out.back().last_point();
}
// clip the paths to prevent the extruder from getting exactly on the first point of the loop
// Keep valid paths only.
size_t j = firts_poly_idx;
for (size_t i = firts_poly_idx; i < thick_polylines_out.size(); ++i) {
thick_polylines_out[i].clip_end(this->loop_clipping);
if (thick_polylines_out[i].is_valid()) {
if (j < i)
thick_polylines_out[j] = std::move(thick_polylines_out[i]);
++j;
}
}
if (j < thick_polylines_out.size())
thick_polylines_out.erase(thick_polylines_out.begin() + int(j), thick_polylines_out.end());
}
else {
Polylines polylines;
this->_fill_surface_single(params, thickness_layers, direction, expolygon, polylines);
append(thick_polylines_out, to_thick_polylines(std::move(polylines), min_spacing));
}
}
} // namespace Slic3r

View file

@ -19,7 +19,18 @@ protected:
ExPolygon expolygon,
Polylines &polylines_out) override;
void _fill_surface_single(const FillParams& params,
unsigned int thickness_layers,
const std::pair<float, Point>& direction,
ExPolygon expolygon,
ThickPolylines& thick_polylines_out) override;
bool no_sort() const override { return true; }
const PrintConfig* print_config = nullptr;
const PrintObjectConfig* print_object_config = nullptr;
friend class Layer;
};
} // namespace Slic3r

View file

@ -1,135 +0,0 @@
#include "../ClipperUtils.hpp"
#include "../ExPolygon.hpp"
#include "../Surface.hpp"
#include "../VariableWidth.hpp"
#include "../ShortestPath.hpp"
#include "FillConcentricWGapFill.hpp"
namespace Slic3r {
const float concentric_overlap_threshold = 0.02;
void FillConcentricWGapFill::fill_surface_extrusion(const Surface* surface, const FillParams& params, ExtrusionEntitiesPtr& out)
{
//BBS: FillConcentricWGapFill.cpp is absolutely newly add by BBL for narrow internal solid infill area to reduce vibration
// Because the area is narrow, we should not use the surface->expolygon which has overlap with perimeter, but
// use no_overlap_expolygons instead to avoid overflow in narrow area.
//Slic3r::ExPolygons expp = offset_ex(surface->expolygon, double(scale_(0 - 0.5 * this->spacing)));
float min_spacing = this->spacing * (1 - concentric_overlap_threshold);
Slic3r::ExPolygons expp = offset2_ex(this->no_overlap_expolygons, -double(scale_(0.5 * this->spacing + 0.5 * min_spacing) - 1),
+double(scale_(0.5 * min_spacing) - 1));
// Create the infills for each of the regions.
Polylines polylines_out;
for (size_t i = 0; i < expp.size(); ++i) {
ExPolygon expolygon = expp[i];
coord_t distance = scale_(this->spacing / params.density);
if (params.density > 0.9999f && !params.dont_adjust) {
distance = scale_(this->spacing);
}
ExPolygons gaps;
Polygons loops = (Polygons)expolygon;
ExPolygons last = { expolygon };
bool first = true;
while (!last.empty()) {
ExPolygons next_onion = offset2_ex(last, -double(distance + scale_(this->spacing) / 2), +double(scale_(this->spacing) / 2));
for (auto it = next_onion.begin(); it != next_onion.end(); it++) {
Polygons temp_loops = (Polygons)(*it);
loops.insert(loops.end(), temp_loops.begin(), temp_loops.end());
}
append(gaps, diff_ex(
offset(last, -0.5f * distance),
offset(next_onion, 0.5f * distance + 10))); // 10 is safty offset
last = next_onion;
if (first && !this->no_overlap_expolygons.empty()) {
gaps = intersection_ex(gaps, this->no_overlap_expolygons);
}
first = false;
}
ExtrusionRole good_role = params.extrusion_role;
ExtrusionEntityCollection *coll_nosort = new ExtrusionEntityCollection();
coll_nosort->no_sort = this->no_sort(); //can be sorted inside the pass
extrusion_entities_append_loops(
coll_nosort->entities, std::move(loops),
good_role,
params.flow.mm3_per_mm(),
params.flow.width(),
params.flow.height());
//BBS: add internal gapfills between infill loops
if (!gaps.empty() && params.density >= 1) {
double min = 0.2 * distance * (1 - INSET_OVERLAP_TOLERANCE);
double max = 2. * distance;
ExPolygons gaps_ex = diff_ex(
offset2_ex(gaps, -float(min / 2), float(min / 2)),
offset2_ex(gaps, -float(max / 2), float(max / 2)),
ApplySafetyOffset::Yes);
//BBS: sort the gap_ex to avoid mess travel
Points ordering_points;
ordering_points.reserve(gaps_ex.size());
ExPolygons gaps_ex_sorted;
gaps_ex_sorted.reserve(gaps_ex.size());
for (const ExPolygon &ex : gaps_ex)
ordering_points.push_back(ex.contour.first_point());
std::vector<Points::size_type> order = chain_points(ordering_points);
for (size_t i : order)
gaps_ex_sorted.emplace_back(std::move(gaps_ex[i]));
ThickPolylines polylines;
for (ExPolygon& ex : gaps_ex_sorted) {
//BBS: Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well.
ex.douglas_peucker(SCALED_RESOLUTION * 0.1);
ex.medial_axis(max, min, &polylines);
}
if (!polylines.empty() && !is_bridge(good_role)) {
ExtrusionEntityCollection gap_fill;
variable_width(polylines, erGapFill, params.flow, gap_fill.entities);
coll_nosort->append(std::move(gap_fill.entities));
}
}
if (!coll_nosort->entities.empty())
out.push_back(coll_nosort);
else
delete coll_nosort;
}
//BBS: add external gapfill between perimeter and infill
ExPolygons external_gaps = diff_ex(this->no_overlap_expolygons, offset_ex(expp, double(scale_(0.5 * this->spacing))), ApplySafetyOffset::Yes);
external_gaps = union_ex(external_gaps);
if (!this->no_overlap_expolygons.empty())
external_gaps = intersection_ex(external_gaps, this->no_overlap_expolygons);
if (!external_gaps.empty()) {
double min = 0.4 * scale_(params.flow.nozzle_diameter()) * (1 - INSET_OVERLAP_TOLERANCE);
double max = 2. * params.flow.scaled_width();
//BBS: collapse, be sure we don't gapfill where the perimeters are already touching each other (negative spacing).
min = std::max(min, (double)Flow::rounded_rectangle_extrusion_width_from_spacing((float)EPSILON, (float)params.flow.height()));
ExPolygons external_gaps_collapsed = offset2_ex(external_gaps, double(-min / 2), double(+min / 2));
ThickPolylines polylines;
for (ExPolygon& ex : external_gaps_collapsed) {
//BBS: Use DP simplify to avoid duplicated points and accelerate medial-axis calculation as well.
ex.douglas_peucker(SCALED_RESOLUTION * 0.1);
ex.medial_axis(max, min, &polylines);
}
ExtrusionEntityCollection* coll_external_gapfill = new ExtrusionEntityCollection();
coll_external_gapfill->no_sort = this->no_sort();
if (!polylines.empty() && !is_bridge(params.extrusion_role)) {
ExtrusionEntityCollection gap_fill;
variable_width(polylines, erGapFill, params.flow, gap_fill.entities);
coll_external_gapfill->append(std::move(gap_fill.entities));
}
if (!coll_external_gapfill->entities.empty())
out.push_back(coll_external_gapfill);
else
delete coll_external_gapfill;
}
}
}

View file

@ -1,21 +0,0 @@
#ifndef slic3r_FillConcentricWGapFil_hpp_
#define slic3r_FillConcentricWGapFil_hpp_
#include "FillBase.hpp"
namespace Slic3r {
class FillConcentricWGapFill : public Fill
{
public:
~FillConcentricWGapFill() override = default;
void fill_surface_extrusion(const Surface *surface, const FillParams &params, ExtrusionEntitiesPtr &out) override;
protected:
Fill* clone() const override { return new FillConcentricWGapFill(*this); };
bool no_sort() const override { return true; }
};
} // namespace Slic3r
#endif // slic3r_FillConcentricWGapFil_hpp_

View file

@ -275,7 +275,7 @@ bool load_step(const char *path, Model *model, ImportStepProgressFn stepFn, Step
}
}
if (aNbTriangles == 0)
if (aNbTriangles == 0 || aNbNodes == 0)
// BBS: No triangulation on the shape.
continue;
@ -354,13 +354,16 @@ bool load_step(const char *path, Model *model, ImportStepProgressFn stepFn, Step
}
}
TriangleMesh triangle_mesh;
triangle_mesh.from_stl(stl[i]);
ModelVolume *new_volume = new_object->add_volume(std::move(triangle_mesh));
new_volume->name = namedSolids[i].name;
new_volume->source.input_file = path;
new_volume->source.object_idx = (int) model->objects.size() - 1;
new_volume->source.volume_idx = (int) new_object->volumes.size() - 1;
//BBS: maybe mesh is empty from step file. Don't add
if (stl[i].stats.number_of_facets > 0) {
TriangleMesh triangle_mesh;
triangle_mesh.from_stl(stl[i]);
ModelVolume* new_volume = new_object->add_volume(std::move(triangle_mesh));
new_volume->name = namedSolids[i].name;
new_volume->source.input_file = path;
new_volume->source.object_idx = (int)model->objects.size() - 1;
new_volume->source.volume_idx = (int)new_object->volumes.size() - 1;
}
}
shapeTool.reset(nullptr);

View file

@ -160,6 +160,8 @@ const unsigned int METADATA_STR_LEN = 9;
static constexpr const char* MODEL_TAG = "model";
static constexpr const char* RESOURCES_TAG = "resources";
static constexpr const char* COLOR_GROUP_TAG = "m:colorgroup";
static constexpr const char* COLOR_TAG = "m:color";
static constexpr const char* OBJECT_TAG = "object";
static constexpr const char* MESH_TAG = "mesh";
static constexpr const char* MESH_STAT_TAG = "mesh_stat";
@ -193,6 +195,7 @@ static constexpr const char* SLICE_HEADER_ITEM_TAG = "header_item";
// BBS: encrypt
static constexpr const char* RELATIONSHIP_TAG = "Relationship";
static constexpr const char* PID_ATTR = "pid";
static constexpr const char* PUUID_ATTR = "p:uuid";
static constexpr const char* PPATH_ATTR = "p:path";
static constexpr const char* OBJECT_UUID_SUFFIX = "-41cb-4c03-9d28-80fed5dfa1dc";
@ -203,6 +206,7 @@ static constexpr const char* RELS_TYPE_ATTR = "Type";
static constexpr const char* UNIT_ATTR = "unit";
static constexpr const char* NAME_ATTR = "name";
static constexpr const char* COLOR_ATTR = "color";
static constexpr const char* TYPE_ATTR = "type";
static constexpr const char* ID_ATTR = "id";
static constexpr const char* X_ATTR = "x";
@ -549,6 +553,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
//int subobject_id;
std::string name;
std::string uuid;
int pid{-1};
//bool is_model_object;
CurrentObject() { reset(); }
@ -713,6 +718,9 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
PlateData* m_curr_plater;
CurrentInstance m_curr_instance;
int m_current_color_group{-1};
std::map<int, std::string> m_group_id_to_color;
public:
_BBS_3MF_Importer();
~_BBS_3MF_Importer();
@ -778,6 +786,12 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
bool _handle_start_object(const char** attributes, unsigned int num_attributes);
bool _handle_end_object();
bool _handle_start_color_group(const char **attributes, unsigned int num_attributes);
bool _handle_end_color_group();
bool _handle_start_color(const char **attributes, unsigned int num_attributes);
bool _handle_end_color();
bool _handle_start_mesh(const char** attributes, unsigned int num_attributes);
bool _handle_end_mesh();
@ -1314,6 +1328,20 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
}
}
std::map<int, int> color_group_id_to_extruder_id_map;
std::map<std::string, int> color_to_extruder_id_map;
int extruder_id = 0;
for (auto group_iter = m_group_id_to_color.begin(); group_iter != m_group_id_to_color.end(); ++group_iter) {
auto color_iter = color_to_extruder_id_map.find(group_iter->second);
if (color_iter == color_to_extruder_id_map.end()) {
++extruder_id;
color_to_extruder_id_map[group_iter->second] = extruder_id;
color_group_id_to_extruder_id_map[group_iter->first] = extruder_id;
} else {
color_group_id_to_extruder_id_map[group_iter->first] = color_iter->second;
}
}
for (const IdToModelObjectMap::value_type& object : m_objects) {
if (object.second >= int(m_model->objects.size())) {
add_error("invalid object, id: "+std::to_string(object.first.second));
@ -1383,6 +1411,18 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
volumes.emplace_back(object_id.second);
}
IdToCurrentObjectMap::const_iterator current_object = m_current_objects.find(object.first);
if (current_object != m_current_objects.end()) {
// get name
model_object->name = current_object->second.name;
// get color
auto extruder_itor = color_group_id_to_extruder_id_map.find(current_object->second.pid);
if (extruder_itor != color_group_id_to_extruder_id_map.end()) {
model_object->config.set_key_value("extruder", new ConfigOptionInt(extruder_itor->second));
}
}
// select as volumes
volumes_ptr = &volumes;
}
@ -2213,6 +2253,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
res = _handle_start_resources(attributes, num_attributes);
else if (::strcmp(OBJECT_TAG, name) == 0)
res = _handle_start_object(attributes, num_attributes);
else if (::strcmp(COLOR_GROUP_TAG, name) == 0)
res = _handle_start_color_group(attributes, num_attributes);
else if (::strcmp(COLOR_TAG, name) == 0)
res = _handle_start_color(attributes, num_attributes);
else if (::strcmp(MESH_TAG, name) == 0)
res = _handle_start_mesh(attributes, num_attributes);
else if (::strcmp(VERTICES_TAG, name) == 0)
@ -2251,6 +2295,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
res = _handle_end_resources();
else if (::strcmp(OBJECT_TAG, name) == 0)
res = _handle_end_object();
else if (::strcmp(COLOR_GROUP_TAG, name) == 0)
res = _handle_end_color_group();
else if (::strcmp(COLOR_TAG, name) == 0)
res = _handle_end_color();
else if (::strcmp(MESH_TAG, name) == 0)
res = _handle_end_mesh();
else if (::strcmp(VERTICES_TAG, name) == 0)
@ -2434,6 +2482,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
m_curr_object->name = bbs_get_attribute_value_string(attributes, num_attributes, NAME_ATTR);
m_curr_object->uuid = bbs_get_attribute_value_string(attributes, num_attributes, PUUID_ATTR);
m_curr_object->pid = bbs_get_attribute_value_int(attributes, num_attributes, PID_ATTR);
}
return true;
@ -2543,6 +2592,31 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
return true;
}
bool _BBS_3MF_Importer::_handle_start_color_group(const char **attributes, unsigned int num_attributes)
{
m_current_color_group = bbs_get_attribute_value_int(attributes, num_attributes, ID_ATTR);
return true;
}
bool _BBS_3MF_Importer::_handle_end_color_group()
{
// do nothing
return true;
}
bool _BBS_3MF_Importer::_handle_start_color(const char **attributes, unsigned int num_attributes)
{
std::string color = bbs_get_attribute_value_string(attributes, num_attributes, COLOR_ATTR);
m_group_id_to_color[m_current_color_group] = color;
return true;
}
bool _BBS_3MF_Importer::_handle_end_color()
{
// do nothing
return true;
}
bool _BBS_3MF_Importer::_handle_start_mesh(const char** attributes, unsigned int num_attributes)
{
// reset current geometry
@ -5950,8 +6024,10 @@ private:
boost::posix_time::ptime start;
};
private:
_BBS_Backup_Manager() : m_thread(boost::ref(*this)) {
_BBS_Backup_Manager() {
m_next_backup = boost::get_system_time() + boost::posix_time::seconds(m_interval);
boost::unique_lock lock(m_mutex);
m_thread = std::move(boost::thread(boost::ref(*this)));
}
~_BBS_Backup_Manager() {

View file

@ -562,18 +562,10 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_
for (int i = int(m_layer_tools.size()) - 2; i >= 0; -- i)
m_layer_tools[i].wipe_tower_partitions = std::max(m_layer_tools[i + 1].wipe_tower_partitions, m_layer_tools[i].wipe_tower_partitions);
// if enable_timelapse_print(), update all layer_tools parameters: wipe_tower_partitions
if (config.timelapse_type == TimelapseType::tlSmooth) {
for (LayerTools& layer_tools : m_layer_tools) {
if (layer_tools.wipe_tower_partitions == 0) {
layer_tools.wipe_tower_partitions = 1;
}
}
}
//FIXME this is a hack to get the ball rolling.
for (LayerTools &lt : m_layer_tools)
lt.has_wipe_tower = (lt.has_object && lt.wipe_tower_partitions > 0) || lt.print_z < object_bottom_z + EPSILON;
lt.has_wipe_tower = (lt.has_object && (config.timelapse_type == TimelapseType::tlSmooth || lt.wipe_tower_partitions > 0))
|| lt.print_z < object_bottom_z + EPSILON;
// Test for a raft, insert additional wipe tower layer to fill in the raft separation gap.
for (size_t i = 0; i + 1 < m_layer_tools.size(); ++ i) {

View file

@ -183,7 +183,7 @@ public:
std::vector<LayerTools>::const_iterator end() const { return m_layer_tools.end(); }
bool empty() const { return m_layer_tools.empty(); }
std::vector<LayerTools>& layer_tools() { return m_layer_tools; }
bool has_wipe_tower() const { return ! m_layer_tools.empty() && m_first_printing_extruder != (unsigned int)-1 && m_layer_tools.front().wipe_tower_partitions > 0; }
bool has_wipe_tower() const { return ! m_layer_tools.empty() && m_first_printing_extruder != (unsigned int)-1 && m_layer_tools.front().has_wipe_tower; }
private:
void initialize_layers(std::vector<coordf_t> &zs);

View file

@ -1066,7 +1066,7 @@ WipeTower::box_coordinates WipeTower::align_perimeter(const WipeTower::box_coord
return aligned_box;
}
WipeTower::ToolChangeResult WipeTower::finish_layer(bool extrude_perimeter)
WipeTower::ToolChangeResult WipeTower::finish_layer(bool extrude_perimeter, bool extruder_fill)
{
assert(! this->layer_finished());
m_current_layer_finished = true;
@ -1083,7 +1083,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer(bool extrude_perimeter)
// Slow down on the 1st layer.
bool first_layer = is_first_layer();
// BBS: speed up perimeter speed to 90mm/s for non-first layer
float feedrate = first_layer ? std::min(m_first_layer_speed * 60.f, 5400.f) : 5400.f;
float feedrate = first_layer ? std::min(m_first_layer_speed * 60.f, 5400.f) : std::min(60.0f * m_filpar[m_current_tool].max_e_speed / m_extrusion_flow, 5400.f);
float fill_box_y = m_layer_info->toolchanges_depth() + m_perimeter_width;
box_coordinates fill_box(Vec2f(m_perimeter_width, fill_box_y),
m_wipe_tower_width - 2 * m_perimeter_width, m_layer_info->depth - fill_box_y);
@ -1105,7 +1105,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer(bool extrude_perimeter)
const float dy = (fill_box.lu.y() - fill_box.ld.y() - m_perimeter_width);
float left = fill_box.lu.x() + 2*m_perimeter_width;
float right = fill_box.ru.x() - 2 * m_perimeter_width;
if (dy > m_perimeter_width)
if (extruder_fill && dy > m_perimeter_width)
{
writer.travel(fill_box.ld + Vec2f(m_perimeter_width * 2, 0.f))
.append(";--------------------\n"
@ -1500,7 +1500,7 @@ void WipeTower::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &
if (m_enable_timelapse_print) {
timelapse_wall = only_generate_out_wall();
}
finish_layer_tcr = finish_layer(m_enable_timelapse_print ? false : true);
finish_layer_tcr = finish_layer(m_enable_timelapse_print ? false : true, layer.extruder_fill);
}
for (int i=0; i<int(layer.tool_changes.size()); ++i) {
@ -1511,7 +1511,7 @@ void WipeTower::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &
if (i == idx) {
layer_result.emplace_back(tool_change(layer.tool_changes[i].new_tool, m_enable_timelapse_print ? false : true));
// finish_layer will be called after this toolchange
finish_layer_tcr = finish_layer(false);
finish_layer_tcr = finish_layer(false, layer.extruder_fill);
}
else {
layer_result.emplace_back(tool_change(layer.tool_changes[i].new_tool));
@ -1541,7 +1541,6 @@ WipeTower::ToolChangeResult WipeTower::only_generate_out_wall()
{
size_t old_tool = m_current_tool;
m_extrusion_flow = 0.038f; // hard code
WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar);
writer.set_extrusion_flow(m_extrusion_flow)
.set_z(m_z_pos)
@ -1551,7 +1550,7 @@ WipeTower::ToolChangeResult WipeTower::only_generate_out_wall()
// Slow down on the 1st layer.
bool first_layer = is_first_layer();
// BBS: speed up perimeter speed to 90mm/s for non-first layer
float feedrate = first_layer ? std::min(m_first_layer_speed * 60.f, 5400.f) : 5400.f;
float feedrate = first_layer ? std::min(m_first_layer_speed * 60.f, 5400.f) : std::min(60.0f * m_filpar[m_current_tool].max_e_speed / m_extrusion_flow, 5400.f);
float fill_box_y = m_layer_info->toolchanges_depth() + m_perimeter_width;
box_coordinates fill_box(Vec2f(m_perimeter_width, fill_box_y), m_wipe_tower_width - 2 * m_perimeter_width, m_layer_info->depth - fill_box_y);

View file

@ -157,8 +157,11 @@ public:
float get_depth() const { return m_wipe_tower_depth; }
float get_brim_width() const { return m_wipe_tower_brim_width_real; }
void set_last_layer_extruder_fill(bool extruder_fill) {
if (!m_plan.empty()) {
m_plan.back().extruder_fill = extruder_fill;
}
}
// Switch to a next layer.
@ -218,7 +221,7 @@ public:
// Fill the unfilled space with a sparse infill.
// Call this method only if layer_finished() is false.
ToolChangeResult finish_layer(bool extruder_perimeter = true);
ToolChangeResult finish_layer(bool extruder_perimeter = true, bool extruder_fill = true);
// Is the current layer finished?
bool layer_finished() const {
@ -378,6 +381,7 @@ private:
float height; // layer height
float depth; // depth of the layer based on all layers above
float extra_spacing;
bool extruder_fill{true};
float toolchanges_depth() const { float sum = 0.f; for (const auto &a : tool_changes) sum += a.required_depth; return sum; }
std::vector<ToolChange> tool_changes;

View file

@ -190,7 +190,7 @@ void Layer::make_perimeters()
if (layerms.size() == 1) { // optimization
(*layerm)->fill_surfaces.surfaces.clear();
(*layerm)->make_perimeters((*layerm)->slices, &(*layerm)->fill_surfaces);
(*layerm)->make_perimeters((*layerm)->slices, &(*layerm)->fill_surfaces, &(*layerm)->fill_no_overlap_expolygons);
(*layerm)->fill_expolygons = to_expolygons((*layerm)->fill_surfaces.surfaces);
} else {
SurfaceCollection new_slices;
@ -212,7 +212,9 @@ void Layer::make_perimeters()
// make perimeters
SurfaceCollection fill_surfaces;
layerm_config->make_perimeters(new_slices, &fill_surfaces);
//BBS
ExPolygons fill_no_overlap;
layerm_config->make_perimeters(new_slices, &fill_surfaces, &fill_no_overlap);
// assign fill_surfaces to each layer
if (!fill_surfaces.surfaces.empty()) {
@ -221,6 +223,8 @@ void Layer::make_perimeters()
ExPolygons expp = intersection_ex(fill_surfaces.surfaces, (*l)->slices.surfaces);
(*l)->fill_expolygons = expp;
(*l)->fill_surfaces.set(std::move(expp), fill_surfaces.surfaces.front());
//BBS: Separate fill_no_overlap
(*l)->fill_no_overlap_expolygons = intersection_ex((*l)->slices.surfaces, fill_no_overlap);
}
}
}

View file

@ -68,7 +68,8 @@ public:
void slices_to_fill_surfaces_clipped();
void prepare_fill_surfaces();
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces);
//BBS
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces, ExPolygons* fill_no_overlap);
void process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered);
double infill_area_threshold() const;
// Trim surfaces by trimming polygons. Used by the elephant foot compensation at the 1st layer.
@ -124,6 +125,7 @@ public:
// BBS
mutable ExPolygons sharp_tails;
mutable ExPolygons cantilevers;
mutable std::map<const ExPolygon*, float> sharp_tails_height;
// Collection of expolygons generated by slicing the possibly multiple meshes of the source geometry

View file

@ -63,7 +63,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
}
}
void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces)
void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces, ExPolygons* fill_no_overlap)
{
this->perimeters.clear();
this->thin_fills.clear();
@ -90,7 +90,9 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
// output:
&this->perimeters,
&this->thin_fills,
fill_surfaces
fill_surfaces,
//BBS
fill_no_overlap
);
if (this->layer()->lower_layer != nullptr)
@ -105,9 +107,6 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
g.solid_infill_flow = this->flow(frSolidInfill);
g.process();
// BBS
this->fill_no_overlap_expolygons = g.fill_no_overlap;
}
//#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 3.
@ -117,7 +116,9 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered)
{
const bool has_infill = this->region().config().sparse_infill_density.value > 0.;
const float margin = float(scale_(EXTERNAL_INFILL_MARGIN));
//BBS
auto nozzle_diameter = this->region().nozzle_dmr_avg(this->layer()->object()->print()->config());
const float margin = std::min(float(scale_(EXTERNAL_INFILL_MARGIN)), float(scale_(nozzle_diameter * EXTERNAL_INFILL_MARGIN / 0.4)));
// BBS
const PrintObjectConfig& object_config = this->layer()->object()->config();

View file

@ -565,14 +565,13 @@ bool Model::looks_like_multipart_object() const
for (const ModelObject *obj : this->objects) {
if (obj->volumes.size() > 1 || obj->config.keys().size() > 1)
return false;
for (const ModelVolume *vol : obj->volumes) {
double zmin_this = vol->mesh().bounding_box().min(2);
if (zmin == std::numeric_limits<double>::max())
zmin = zmin_this;
else if (std::abs(zmin - zmin_this) > EPSILON)
// The volumes don't share zmin.
return true;
}
double zmin_this = obj->get_min_z();
if (zmin == std::numeric_limits<double>::max())
zmin = zmin_this;
else if (std::abs(zmin - zmin_this) > EPSILON)
// The Object don't share zmin.
return true;
}
return false;
}
@ -608,11 +607,15 @@ void Model::convert_multipart_object(unsigned int max_extruders)
// Revert the centering operation.
trafo_volume.set_offset(trafo_volume.get_offset() - o->origin_translation);
int counter = 1;
auto copy_volume = [o, max_extruders, &counter, &extruder_counter](ModelVolume *new_v) {
auto copy_volume = [o, v, max_extruders, &counter, &extruder_counter](ModelVolume *new_v) {
assert(new_v != nullptr);
new_v->name = (counter > 1) ? o->name + "_" + std::to_string(counter++) : o->name;
//BBS: use default extruder id
//new_v->config.set("extruder", auto_extruder_id(max_extruders, extruder_counter));
//BBS: Use extruder priority: volumn > object > default
if (v->config.option("extruder"))
new_v->config.set("extruder", v->config.extruder());
else if (o->config.option("extruder"))
new_v->config.set("extruder", o->config.extruder());
return new_v;
};
if (o->instances.empty()) {

View file

@ -982,44 +982,6 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vector<std::vector<Col
const ColoredLine colored_line = lines_colored[edge_it->cell()->source_index()];
const ColoredLine contour_line_prev = get_prev_contour_line(edge_it);
const ColoredLine contour_line_next = get_next_contour_line(edge_it);
bool has_color_change = false;
{
const double tolerance = 15 * SCALED_EPSILON;
double acc_len = 0.0;
size_t contour_line_local_idx = lines_colored[edge_it->cell()->source_index()].local_line_idx;
size_t poly_idx = lines_colored[edge_it->cell()->source_index()].poly_idx;
size_t contour_line_size = color_poly[poly_idx].size();
size_t contour_prev_local_idx = (contour_line_local_idx > 0) ? contour_line_local_idx - 1 : contour_line_size - 1;
while (!has_color_change) {
ColoredLine& prev_line = lines_colored[graph.get_global_index(poly_idx, contour_prev_local_idx)];
if (!has_same_color(prev_line, colored_line)) {
has_color_change = true;
break;
}
acc_len += prev_line.line.length();
if (acc_len >= tolerance)
break;
contour_prev_local_idx = (contour_prev_local_idx > 0) ? contour_prev_local_idx - 1 : contour_line_size - 1;
}
acc_len = 0.0;
size_t contour_next_local_idx = (contour_line_local_idx + 1) % contour_line_size;
while (!has_color_change) {
ColoredLine& next_line = lines_colored[graph.get_global_index(poly_idx, contour_next_local_idx)];
if (!has_same_color(colored_line, next_line)) {
has_color_change = true;
break;
}
acc_len += next_line.line.length();
if (acc_len >= tolerance)
break;
contour_next_local_idx = (contour_next_local_idx + 1) % contour_line_size;
}
}
if (edge_it->vertex0()->color() >= graph.nodes_count() || edge_it->vertex1()->color() >= graph.nodes_count()) {
enum class Vertex { VERTEX0, VERTEX1 };
@ -1054,12 +1016,12 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vector<std::vector<Col
const size_t to_idx = edge_it->vertex1()->color();
if (graph.is_vertex_on_contour(edge_it->vertex0())) {
if (is_point_closer_to_beginning_of_line(contour_line, edge_line.a)) {
if ((has_color_change || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line_prev.line, contour_line, edge_line.b)) {
if ((!has_same_color(contour_line_prev, colored_line) || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line_prev.line, contour_line, edge_line.b)) {
graph.append_edge(from_idx, to_idx);
force_edge_adding[colored_line.poly_idx] = false;
}
} else {
if ((has_color_change || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line, contour_line_next.line, edge_line.b)) {
if ((!has_same_color(contour_line_next, colored_line) || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line, contour_line_next.line, edge_line.b)) {
graph.append_edge(from_idx, to_idx);
force_edge_adding[colored_line.poly_idx] = false;
}
@ -1067,12 +1029,12 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vector<std::vector<Col
} else {
assert(graph.is_vertex_on_contour(edge_it->vertex1()));
if (is_point_closer_to_beginning_of_line(contour_line, edge_line.b)) {
if ((has_color_change || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line_prev.line, contour_line, edge_line.a)) {
if ((!has_same_color(contour_line_prev, colored_line) || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line_prev.line, contour_line, edge_line.a)) {
graph.append_edge(from_idx, to_idx);
force_edge_adding[colored_line.poly_idx] = false;
}
} else {
if ((has_color_change || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line, contour_line_next.line, edge_line.a)) {
if ((!has_same_color(contour_line_next, colored_line) || force_edge_adding[colored_line.poly_idx]) && points_inside(contour_line, contour_line_next.line, edge_line.a)) {
graph.append_edge(from_idx, to_idx);
force_edge_adding[colored_line.poly_idx] = false;
}
@ -1086,22 +1048,30 @@ static MMU_Graph build_graph(size_t layer_idx, const std::vector<std::vector<Col
Point real_v1 = Point(coord_t(real_v1_double.x()), coord_t(real_v1_double.y()));
if (is_point_closer_to_beginning_of_line(contour_line, intersection)) {
if (has_color_change)
{
if (points_inside(contour_line_prev.line, contour_line, real_v0))
Line first_part(intersection, real_v0);
Line second_part(intersection, real_v1);
if (!has_same_color(contour_line_prev, colored_line)) {
if (points_inside(contour_line_prev.line, contour_line, first_part.b))
graph.append_edge(edge_it->vertex0()->color(), graph.get_border_arc(edge_it->cell()->source_index()).from_idx);
if (points_inside(contour_line_prev.line, contour_line, real_v1))
if (points_inside(contour_line_prev.line, contour_line, second_part.b))
graph.append_edge(edge_it->vertex1()->color(), graph.get_border_arc(edge_it->cell()->source_index()).from_idx);
}
} else {
if (has_color_change)
{
if (points_inside(contour_line, contour_line_next.line, real_v0))
graph.append_edge(edge_it->vertex0()->color(), graph.get_border_arc(edge_it->cell()->source_index()).to_idx);
const size_t int_point_idx = graph.get_border_arc(edge_it->cell()->source_index()).to_idx;
const Vec2d int_point_double = graph.nodes[int_point_idx].point;
const Point int_point = Point(coord_t(int_point_double.x()), coord_t(int_point_double.y()));
if (points_inside(contour_line, contour_line_next.line, real_v1))
graph.append_edge(edge_it->vertex1()->color(), graph.get_border_arc(edge_it->cell()->source_index()).to_idx);
const Line first_part(int_point, real_v0);
const Line second_part(int_point, real_v1);
if (!has_same_color(contour_line_next, colored_line)) {
if (points_inside(contour_line, contour_line_next.line, first_part.b))
graph.append_edge(edge_it->vertex0()->color(), int_point_idx);
if (points_inside(contour_line, contour_line_next.line, second_part.b))
graph.append_edge(edge_it->vertex1()->color(), int_point_idx);
}
}
}
@ -1147,13 +1117,17 @@ static std::vector<ExPolygons> extract_colored_segments(const MMU_Graph &graph,
{
std::vector<bool> used_arcs(graph.arcs.size(), false);
// When there is no next arc, then is returned original_arc or edge with is marked as used
auto get_next = [&graph, &used_arcs](const Linef &process_line, const MMU_Graph::Arc &original_arc) -> const MMU_Graph::Arc & {
auto get_next = [&graph, &used_arcs](const Linef &process_line, const MMU_Graph::Arc &original_arc, const int color) -> const MMU_Graph::Arc & {
std::vector<std::pair<const MMU_Graph::Arc *, double>> sorted_arcs;
for (const size_t &arc_idx : graph.nodes[original_arc.to_idx].arc_idxs) {
const MMU_Graph::Arc &arc = graph.arcs[arc_idx];
if (graph.nodes[arc.to_idx].point == process_line.a || used_arcs[arc_idx])
continue;
// BBS
if (original_arc.type == MMU_Graph::ARC_TYPE::BORDER && original_arc.color != color)
continue;
assert(original_arc.to_idx == arc.from_idx);
Vec2d process_line_vec_n = (process_line.a - process_line.b).normalized();
Vec2d neighbour_line_vec_n = (graph.nodes[arc.to_idx].point - graph.nodes[arc.from_idx].point).normalized();
@ -1202,7 +1176,7 @@ static std::vector<ExPolygons> extract_colored_segments(const MMU_Graph &graph,
Linef p_vec = process_line;
const MMU_Graph::Arc *p_arc = &arc;
do {
const MMU_Graph::Arc &next = get_next(p_vec, *p_arc);
const MMU_Graph::Arc& next = get_next(p_vec, *p_arc, arc.color);
size_t next_arc_idx = &next - &graph.arcs.front();
face_lines.emplace_back(graph.nodes[next.from_idx].point, graph.nodes[next.to_idx].point);
if (used_arcs[next_arc_idx])
@ -1509,10 +1483,9 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
top_ex = opening_ex(top_ex, stat.small_region_threshold);
if (! top_ex.empty()) {
append(triangles_by_color_top[color_idx][layer_idx + layer_idx_offset], top_ex);
// BBS: propagate only 1 layer below
float offset = 0.f;
ExPolygons layer_slices_trimmed = input_expolygons[layer_idx];
for (int last_idx = int(layer_idx) - 1; last_idx >= std::max(int(layer_idx - 1), int(0)); --last_idx) {
for (int last_idx = int(layer_idx) - 1; last_idx >= std::max(int(layer_idx - stat.top_shell_layers), int(0)); --last_idx) {
//BBS: offset width should be 2*spacing to avoid too narrow area which has overlap of wall line
//offset -= stat.extrusion_width ;
offset -= (stat.extrusion_spacing + stat.extrusion_width);
@ -1530,10 +1503,9 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
bottom_ex = opening_ex(bottom_ex, stat.small_region_threshold);
if (! bottom_ex.empty()) {
append(triangles_by_color_bottom[color_idx][layer_idx + layer_idx_offset], bottom_ex);
// BBS: propogate only 1 layer above
float offset = 0.f;
ExPolygons layer_slices_trimmed = input_expolygons[layer_idx];
for (size_t last_idx = layer_idx + 1; last_idx < std::min(layer_idx + 2, num_layers); ++last_idx) {
for (size_t last_idx = layer_idx + 1; last_idx < std::min(layer_idx + stat.bottom_shell_layers, num_layers); ++last_idx) {
//BBS: offset width should be 2*spacing to avoid too narrow area which has overlap of wall line
//offset -= stat.extrusion_width;
offset -= (stat.extrusion_spacing + stat.extrusion_width);

View file

@ -765,7 +765,7 @@ void PerimeterGenerator::process()
double(-inset - infill_peri_overlap));
if (!top_fills.empty())
polyWithoutOverlap = union_ex(polyWithoutOverlap, top_infill_exp);
this->fill_no_overlap.insert(this->fill_no_overlap.end(), polyWithoutOverlap.begin(), polyWithoutOverlap.end());
this->fill_no_overlap->insert(this->fill_no_overlap->end(), polyWithoutOverlap.begin(), polyWithoutOverlap.end());
}
} // for each island

View file

@ -29,13 +29,14 @@ public:
ExtrusionEntityCollection *loops;
ExtrusionEntityCollection *gap_fill;
SurfaceCollection *fill_surfaces;
//BBS
ExPolygons *fill_no_overlap;
//BBS
Flow smaller_ext_perimeter_flow;
std::map<int, Polygons> m_lower_polygons_series;
std::map<int, Polygons> m_external_lower_polygons_series;
std::map<int, Polygons> m_smaller_external_lower_polygons_series;
ExPolygons fill_no_overlap;
PerimeterGenerator(
// Input:
@ -52,14 +53,16 @@ public:
// Gaps without the thin walls
ExtrusionEntityCollection* gap_fill,
// Infills without the gap fills
SurfaceCollection* fill_surfaces)
SurfaceCollection* fill_surfaces,
//BBS
ExPolygons* fill_no_overlap)
: slices(slices), upper_slices(nullptr), lower_slices(nullptr), layer_height(layer_height),
layer_id(-1), perimeter_flow(flow), ext_perimeter_flow(flow),
overhang_flow(flow), solid_infill_flow(flow),
config(config), object_config(object_config), print_config(print_config),
m_spiral_vase(spiral_mode),
m_scaled_resolution(scaled<double>(print_config->resolution.value > EPSILON ? print_config->resolution.value : EPSILON)),
loops(loops), gap_fill(gap_fill), fill_surfaces(fill_surfaces),
loops(loops), gap_fill(gap_fill), fill_surfaces(fill_surfaces), fill_no_overlap(fill_no_overlap),
m_ext_mm3_per_mm(-1), m_mm3_per_mm(-1), m_mm3_per_mm_overhang(-1), m_ext_mm3_per_mm_smaller_width(-1)
{}

View file

@ -239,6 +239,18 @@ public:
std::pair<bool,bool> endpoints;
};
inline ThickPolylines to_thick_polylines(Polylines&& polylines, const coordf_t width)
{
ThickPolylines out;
out.reserve(polylines.size());
for (Polyline& polyline : polylines) {
out.emplace_back();
out.back().width.assign((polyline.points.size() - 1) * 2, width);
out.back().points = std::move(polyline.points);
}
return out;
}
class Polyline3 : public MultiPoint3
{
public:

View file

@ -307,8 +307,8 @@ void Preset::update_suffix_modified(const std::string& new_suffix_modified)
// This converts a UI name to a unique preset identifier.
std::string Preset::remove_suffix_modified(const std::string &name)
{
return boost::algorithm::ends_with(name, g_suffix_modified) ?
name.substr(0, name.size() - g_suffix_modified.size()) :
return boost::algorithm::starts_with(name, g_suffix_modified) ?
name.substr(g_suffix_modified.size()) :
name;
}
@ -530,9 +530,10 @@ void Preset::save(DynamicPrintConfig* parent_config)
}
// Return a label of this preset, consisting of a name and a "(modified)" suffix, if this preset is dirty.
std::string Preset::label() const
std::string Preset::label(bool no_alias) const
{
return this->name + (this->is_dirty ? g_suffix_modified : "");
return (this->is_dirty ? g_suffix_modified : "")
+ ((no_alias || this->alias.empty()) ? this->name : this->alias);
}
bool is_compatible_with_print(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_print, const PresetWithVendorProfile &active_printer)
@ -667,7 +668,7 @@ static std::vector<std::string> s_Preset_print_options {
//"independent_support_layer_height",
"support_angle", "support_interface_top_layers", "support_interface_bottom_layers",
"support_interface_pattern", "support_interface_spacing", "support_interface_loop_pattern",
"support_top_z_distance", "support_on_build_plate_only", "bridge_no_support", "thick_bridges", "max_bridge_length", "print_sequence",
"support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "max_bridge_length", "print_sequence",
"filename_format", "wall_filament",
"sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament",
"ooze_prevention", "standby_temperature_delta", "interface_shells", "line_width", "initial_layer_line_width",

View file

@ -245,7 +245,7 @@ public:
void save(DynamicPrintConfig* parent_config);
// Return a label of this preset, consisting of a name and a "(modified)" suffix, if this preset is dirty.
std::string label() const;
std::string label(bool no_alias) const;
// Set the is_dirty flag if the provided config is different from the active one.
void set_dirty(const DynamicPrintConfig &config) { this->is_dirty = ! this->config.diff(config).empty(); }

View file

@ -1314,6 +1314,31 @@ void PresetBundle::set_num_filaments(unsigned int n, std::string new_color)
update_multi_material_filament_presets();
}
unsigned int PresetBundle::sync_ams_list()
{
std::vector<std::string> filament_presets;
std::vector<std::string> filament_colors;
for (auto &ams : filament_ams_list) {
auto filament_id = ams.opt_string("filament_id", 0u);
auto filament_color = ams.opt_string("filament_colour", 0u);
auto iter = std::find_if(filaments.begin(), filaments.end(), [&filament_id](auto &f) { return f.is_compatible && f.is_system && f.filament_id == filament_id; });
if (iter == filaments.end()) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": filament_id %1% not found or system or compatible") % filament_id;
continue;
}
filament_presets.push_back(iter->name);
filament_colors.push_back(filament_color);
}
if (filament_presets.empty())
return 0;
this->filament_presets = filament_presets;
ConfigOptionStrings *filament_color = project_config.option<ConfigOptionStrings>("filament_colour");
filament_color->resize(filament_presets.size());
filament_color->values = filament_colors;
update_multi_material_filament_presets();
return filament_presets.size();
}
//BBS: check whether this is the only edited filament
bool PresetBundle::is_the_only_edited_filament(unsigned int filament_index)
{

View file

@ -79,6 +79,7 @@ public:
// BBS
void set_num_filaments(unsigned int n, std::string new_col = "");
unsigned int sync_ams_list();
//BBS: check whether this is the only edited filament
bool is_the_only_edited_filament(unsigned int filament_index);

View file

@ -38,6 +38,54 @@ PrintRegion::PrintRegion(PrintRegionConfig &&config) : PrintRegion(std::move(con
//BBS
float Print::min_skirt_length = 0;
void dfs_get_all_sorted_extruders(const std::vector<std::vector<float>>& wipe_volumes,
const std::vector<unsigned int>& all_extruders,
std::vector<unsigned int> & sorted_extruders,
float flush_volume,
std::map<float, std::vector<unsigned int>> & volumes_to_extruder_order)
{
if (sorted_extruders.size() == all_extruders.size()) {
volumes_to_extruder_order.insert(std::pair(flush_volume, sorted_extruders));
return;
}
for (auto extruder_id : all_extruders) {
if (sorted_extruders.empty()) {
sorted_extruders.push_back(extruder_id);
dfs_get_all_sorted_extruders(wipe_volumes, all_extruders, sorted_extruders, flush_volume, volumes_to_extruder_order);
sorted_extruders.pop_back();
} else {
auto itor = std::find(sorted_extruders.begin(), sorted_extruders.end(), extruder_id);
if (itor == sorted_extruders.end()) {
float delta_flush_volume = wipe_volumes[sorted_extruders.back()][extruder_id];
flush_volume += delta_flush_volume;
sorted_extruders.push_back(extruder_id);
dfs_get_all_sorted_extruders(wipe_volumes, all_extruders, sorted_extruders, flush_volume, volumes_to_extruder_order);
flush_volume -= delta_flush_volume;
sorted_extruders.pop_back();
}
}
}
}
std::vector<unsigned int> get_extruders_order(const std::vector<std::vector<float>> &wipe_volumes,
std::vector<unsigned int> all_extruders,
unsigned int start_extruder_id)
{
if (all_extruders.size() > 1) {
std::vector<unsigned int> sorted_extruders;
auto iter = std::find(all_extruders.begin(), all_extruders.end(), start_extruder_id);
if (iter != all_extruders.end()) {
sorted_extruders.push_back(start_extruder_id);
}
std::map<float, std::vector<unsigned int>> volumes_to_extruder_order;
dfs_get_all_sorted_extruders(wipe_volumes, all_extruders, sorted_extruders, 0, volumes_to_extruder_order);
if(volumes_to_extruder_order.size() > 0)
return volumes_to_extruder_order.begin()->second;
}
return all_extruders;
}
void Print::clear()
{
std::scoped_lock<std::mutex> lock(this->state_mutex());
@ -674,15 +722,16 @@ static StringObjectException layered_print_cleareance_valid(const Print &print,
float depth = print.wipe_tower_data(filaments_count).depth;
//float brim_width = print.wipe_tower_data(filaments_count).brim_width;
Polygon wipe_tower_convex_hull;
wipe_tower_convex_hull.points.emplace_back(scale_(x), scale_(y));
wipe_tower_convex_hull.points.emplace_back(scale_(x + width), scale_(y));
wipe_tower_convex_hull.points.emplace_back(scale_(x + width), scale_(y + depth));
wipe_tower_convex_hull.points.emplace_back(scale_(x), scale_(y + depth));
wipe_tower_convex_hull.rotate(a);
Polygons convex_hulls_temp;
convex_hulls_temp.push_back(wipe_tower_convex_hull);
if (print.has_wipe_tower()) {
Polygon wipe_tower_convex_hull;
wipe_tower_convex_hull.points.emplace_back(scale_(x), scale_(y));
wipe_tower_convex_hull.points.emplace_back(scale_(x + width), scale_(y));
wipe_tower_convex_hull.points.emplace_back(scale_(x + width), scale_(y + depth));
wipe_tower_convex_hull.points.emplace_back(scale_(x), scale_(y + depth));
wipe_tower_convex_hull.rotate(a);
convex_hulls_temp.push_back(wipe_tower_convex_hull);
}
if (!intersection(convex_hulls_other, convex_hulls_temp).empty()) {
if (warning) {
warning->string += L("Prime Tower") + L(" is too close to others, and collisions may be caused.\n");
@ -756,6 +805,9 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
}
if (m_config.print_sequence == PrintSequence::ByObject) {
if (m_config.timelapse_type == TimelapseType::tlSmooth)
return {L("Smooth mode of timelapse is not supported when \"by object\" sequence is enabled.")};
//BBS: refine seq-print validation logic
auto ret = sequential_print_clearance_valid(*this, collison_polygons, height_polygons);
if (!ret.string.empty())
@ -1850,6 +1902,9 @@ void Print::_make_wipe_tower()
if (!layer_tools.has_wipe_tower) continue;
bool first_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.front();
wipe_tower.plan_toolchange((float)layer_tools.print_z, (float)layer_tools.wipe_tower_layer_height, current_extruder_id, current_extruder_id, false);
layer_tools.extruders = get_extruders_order(wipe_volumes, layer_tools.extruders, current_extruder_id);
for (const auto extruder_id : layer_tools.extruders) {
// BBS: priming logic is removed, so no need to do toolchange for first extruder
if (/*(first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || */extruder_id != current_extruder_id) {
@ -1875,8 +1930,11 @@ void Print::_make_wipe_tower()
layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this);
// if enable timelapse, slice all layer
if (enable_timelapse_print())
if (enable_timelapse_print()) {
if (layer_tools.wipe_tower_partitions == 0)
wipe_tower.set_last_layer_extruder_fill(false);
continue;
}
if (&layer_tools == &m_wipe_tower_data.tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0)
break;

View file

@ -914,16 +914,16 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("zig-zag");
def->enum_values.push_back("monotonic");
def->enum_values.push_back("monotonicline");
//def->enum_values.push_back("alignedrectilinear");
//def->enum_values.push_back("hilbertcurve");
def->enum_values.push_back("alignedrectilinear");
def->enum_values.push_back("hilbertcurve");
//def->enum_values.push_back("archimedeanchords");
//def->enum_values.push_back("octagramspiral");
def->enum_labels.push_back(L("Concentric"));
def->enum_labels.push_back(L("Zig zag"));
def->enum_labels.push_back(L("Rectilinear"));
def->enum_labels.push_back(L("Monotonic"));
def->enum_labels.push_back(L("Monotonic line"));
//def->enum_labels.push_back(L("Aligned Rectilinear"));
//def->enum_labels.push_back(L("Hilbert Curve"));
def->enum_labels.push_back(L("Aligned Rectilinear"));
def->enum_labels.push_back(L("Hilbert Curve"));
//def->enum_labels.push_back(L("Archimedean Chords"));
//def->enum_labels.push_back(L("Octagram Spiral"));
def->set_default_value(new ConfigOptionEnum<InfillPattern>(ipRectilinear));
@ -1232,7 +1232,7 @@ void PrintConfigDef::init_fff_params()
def->enum_values.push_back("gyroid");
def->enum_values.push_back("honeycomb");
def->enum_values.push_back("adaptivecubic");
//def->enum_values.push_back("alignedrectilinear");
def->enum_values.push_back("alignedrectilinear");
//def->enum_values.push_back("3dhoneycomb");
//def->enum_values.push_back("hilbertcurve");
//def->enum_values.push_back("archimedeanchords");
@ -1242,7 +1242,7 @@ void PrintConfigDef::init_fff_params()
//def->enum_values.push_back("lightning");
#endif // HAS_LIGHTNING_INFILL
def->enum_labels.push_back(L("Concentric"));
def->enum_labels.push_back(L("Zig zag"));
def->enum_labels.push_back(L("Rectilinear"));
def->enum_labels.push_back(L("Grid"));
def->enum_labels.push_back(L("Line"));
def->enum_labels.push_back(L("Cubic"));
@ -1251,7 +1251,7 @@ void PrintConfigDef::init_fff_params()
def->enum_labels.push_back(L("Gyroid"));
def->enum_labels.push_back(L("Honeycomb"));
def->enum_labels.push_back(L("Adaptive Cubic"));
//def->enum_labels.push_back(L("Aligned Rectilinear"));
def->enum_labels.push_back(L("Aligned Rectilinear"));
//def->enum_labels.push_back(L("3D Honeycomb"));
//def->enum_labels.push_back(L("Hilbert Curve"));
//def->enum_labels.push_back(L("Archimedean Chords"));
@ -1985,7 +1985,7 @@ void PrintConfigDef::init_fff_params()
def->label = L("Wall loops");
def->category = L("Strength");
def->tooltip = L("Number of walls of every layer");
def->min = 1;
def->min = 0;
def->max = 1000;
def->set_default_value(new ConfigOptionInt(2));
@ -2397,6 +2397,14 @@ void PrintConfigDef::init_fff_params()
def->mode = comSimple;
def->set_default_value(new ConfigOptionBool(false));
// BBS
def = this->add("support_critical_regions_only", coBool);
def->label = L("Support critical regions only");
def->category = L("Support");
def->tooltip = L("Only create support for critical regions including sharp tail, cantilever, etc.");
def->mode = comSimple;
def->set_default_value(new ConfigOptionBool(false));
// BBS: change type to common float.
// It may be rounded to mulitple layer height when independent_support_layer_height is false.
def = this->add("support_top_z_distance", coFloat);
@ -3680,8 +3688,6 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
//But now these key-value must be absolute value.
//Reset to default value by erasing these key to avoid parsing error.
opt_key = "";
} else if (opt_key == "filament_type" && value == "PA-CF") {
value == "PA";
} else if (opt_key == "inherits_cummulative") {
opt_key = "inherits_group";
} else if (opt_key == "compatible_printers_condition_cummulative") {

View file

@ -54,7 +54,7 @@ enum AuthorizationType {
enum InfillPattern : int {
ipConcentric, ipRectilinear, ipGrid, ipLine, ipCubic, ipTriangles, ipStars, ipGyroid, ipHoneycomb, ipAdaptiveCubic, ipMonotonic, ipMonotonicLine, ipAlignedRectilinear, ip3DHoneycomb,
ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipSupportCubic, ipSupportBase, ipConcentricGapFill, ipConcentricInternal,
ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipSupportCubic, ipSupportBase, ipConcentricInternal,
#if HAS_LIGHTNING_INFILL
ipLightning,
#endif // HAS_LIGHTNING_INFILL
@ -605,6 +605,7 @@ PRINT_CONFIG_CLASS_DEFINE(
// Direction of the support pattern (in XY plane).`
((ConfigOptionFloat, support_angle))
((ConfigOptionBool, support_on_build_plate_only))
((ConfigOptionBool, support_critical_regions_only))
((ConfigOptionFloat, support_top_z_distance))
((ConfigOptionInt, enforce_support_layers))
((ConfigOptionInt, support_filament))

View file

@ -702,6 +702,7 @@ bool PrintObject::invalidate_state_by_config_options(
opt_key == "support_type"
|| opt_key == "support_angle"
|| opt_key == "support_on_build_plate_only"
|| opt_key == "support_critical_regions_only"
|| opt_key == "enforce_support_layers"
|| opt_key == "support_filament"
|| opt_key == "support_line_width"

View file

@ -38,6 +38,8 @@
// Enable style editor in develop mode
#define ENABLE_IMGUI_STYLE_EDITOR 0
// Enable rework of Reload from disk command
#define ENABLE_RELOAD_FROM_DISK_REWORK 1
//====================
// 2.4.0.beta1 techs

View file

@ -673,6 +673,7 @@ void TreeSupport::detect_object_overhangs()
const coordf_t extrusion_width_scaled = scale_(extrusion_width);
const coordf_t max_bridge_length = scale_(config.max_bridge_length.value);
const bool bridge_no_support = max_bridge_length > 0;// config.bridge_no_support.value;
const bool support_critical_regions_only = config.support_critical_regions_only.value;
const int enforce_support_layers = config.enforce_support_layers.value;
const double area_thresh_well_supported = SQ(scale_(6)); // min: 6x6=36mm^2
const double length_thresh_well_supported = scale_(6); // min: 6mm
@ -694,6 +695,7 @@ void TreeSupport::detect_object_overhangs()
int min_layer = 1e7;
int max_layer = 0;
coordf_t offset = 0;
bool is_cantilever = false;
OverhangCluster(const ExPolygon* expoly, int layer_nr) {
push_back(expoly, layer_nr);
}
@ -822,8 +824,11 @@ void TreeSupport::detect_object_overhangs()
// normal overhang
ExPolygons lower_layer_offseted = offset_ex(lower_polys, support_offset_scaled, SUPPORT_SURFACES_OFFSET_PARAMETERS);
ExPolygons overhang_areas = std::move(diff_ex(curr_polys, lower_layer_offseted));
//overhang_areas = std::move(offset2_ex(overhang_areas, -0.1 * extrusion_width_scaled, 0.1 * extrusion_width_scaled));
overhang_areas.erase(std::remove_if(overhang_areas.begin(), overhang_areas.end(), [extrusion_width_scaled](ExPolygon& area) {return offset_ex(area, -0.1 * extrusion_width_scaled).empty(); }), overhang_areas.end());
// overhang_areas = std::move(offset2_ex(overhang_areas, -0.1 * extrusion_width_scaled, 0.1 * extrusion_width_scaled));
overhang_areas.erase(std::remove_if(overhang_areas.begin(), overhang_areas.end(),
[extrusion_width_scaled](ExPolygon &area) { return offset_ex(area, -0.1 * extrusion_width_scaled).empty(); }),
overhang_areas.end());
ExPolygons overhangs_sharp_tail;
if (is_auto && g_config_support_sharp_tails)
@ -854,7 +859,7 @@ void TreeSupport::detect_object_overhangs()
overhang_areas = union_ex(overhang_areas, overhangs_sharp_tail);
}
#else
// BBS
// BBS detect sharp tail
const ExPolygons& lower_layer_sharptails = lower_layer->sharp_tails;
auto& lower_layer_sharptails_height = lower_layer->sharp_tails_height;
for (ExPolygon& expoly : layer->lslices) {
@ -939,19 +944,8 @@ void TreeSupport::detect_object_overhangs()
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers);
for (ExPolygon& poly : overhang_areas) {
// NOTE: must push something into ts_layer->overhang_areas, can't be empty for any layer,
// otherwise remove_small_overhangs can't correctly cluster overhangs
#if 0
ExPolygons poly_simp = poly.simplify(scale_(radius_sample_resolution));
// simplify method may delete the entire polygon which is unwanted
if(!poly_simp.empty())
append(ts_layer->overhang_areas, poly_simp);
else
ts_layer->overhang_areas.emplace_back(poly);
#else
if (!offset_ex(poly, -0.1 * extrusion_width_scaled).empty())
ts_layer->overhang_areas.emplace_back(poly);
#endif
}
if (is_auto && g_config_remove_small_overhangs) {
@ -1004,6 +998,34 @@ void TreeSupport::detect_object_overhangs()
m_object->project_and_append_custom_facets(false, EnforcerBlockerType::ENFORCER, enforcers);
m_object->project_and_append_custom_facets(false, EnforcerBlockerType::BLOCKER, blockers);
// check whether the overhang cluster is cantilever (far awary from main body)
for (auto& cluster : overhangClusters) {
Layer* layer = m_object->get_layer(cluster.min_layer);
if (layer->lower_layer == NULL) continue;
Layer* lower_layer = layer->lower_layer;
auto cluster_boundary = intersection(cluster.merged_poly, offset(lower_layer->lslices, scale_(0.5)));
double dist_max = 0;
Points cluster_pts;
for (auto& poly : cluster.merged_poly)
append(cluster_pts, poly.contour.points);
for (auto& pt : cluster_pts) {
double dist_pt = std::numeric_limits<double>::max();
for (auto& poly : cluster_boundary) {
double d = poly.distance_to(pt);
dist_pt = std::min(dist_pt, d);
}
dist_max = std::max(dist_max, dist_pt);
}
if (dist_max > scale_(5)) { // this cluster is cantilever, add all expolygons to sharp tail
for (auto it = cluster.layer_overhangs.begin(); it != cluster.layer_overhangs.end(); it++) {
int layer_nr = it->first;
auto p_overhang = it->second;
m_object->get_layer(layer_nr)->cantilevers.emplace_back(*p_overhang);
}
cluster.is_cantilever = true;
}
}
if (is_auto && g_config_remove_small_overhangs) {
if (blockers.size() < m_object->layer_count())
blockers.resize(m_object->layer_count());
@ -1045,25 +1067,8 @@ void TreeSupport::detect_object_overhangs()
}
if (is_sharp_tail) continue;
// 4. check whether the overhang cluster is cantilever (far awary from main body)
Layer* layer = m_object->get_layer(cluster.min_layer);
if (layer->lower_layer == NULL) continue;
Layer* lower_layer = layer->lower_layer;
auto cluster_boundary = intersection(cluster.merged_poly, offset(lower_layer->lslices, scale_(0.5)));
double dist_max = 0;
Points cluster_pts;
for (auto& poly : cluster.merged_poly)
append(cluster_pts, poly.contour.points);
for (auto& pt : cluster_pts) {
double dist_pt = std::numeric_limits<double>::max();
for (auto& poly : cluster_boundary) {
double d = poly.distance_to(pt);
dist_pt = std::min(dist_pt, d);
}
dist_max = std::max(dist_max, dist_pt);
}
if (dist_max > scale_(5))
continue;
// 4. check whether the overhang cluster is cantilever
if (cluster.is_cantilever) continue;
for (auto it = cluster.layer_overhangs.begin(); it != cluster.layer_overhangs.end(); it++) {
int layer_nr = it->first;
@ -1088,6 +1093,11 @@ void TreeSupport::detect_object_overhangs()
break;
TreeSupportLayer* ts_layer = m_object->get_tree_support_layer(layer_nr + m_raft_layers);
if (support_critical_regions_only) {
auto layer = m_object->get_layer(layer_nr);
ts_layer->overhang_areas = layer->sharp_tails;
append(ts_layer->overhang_areas, layer->cantilevers);
}
if (layer_nr < blockers.size()) {
Polygons& blocker = blockers[layer_nr];
@ -1822,6 +1832,24 @@ inline coordf_t calc_branch_radius(coordf_t base_radius, size_t layers_to_top, s
return radius;
}
ExPolygons avoid_object_remove_extra_small_parts(ExPolygons &expolys, const ExPolygons &avoid_region) {
ExPolygons expolys_out;
for (auto expoly : expolys) {
auto expolys_avoid = diff_ex(expoly, avoid_region);
int idx_max_area = -1;
float max_area = 0;
for (int i = 0; i < expolys_avoid.size(); ++i) {
auto a = expolys_avoid[i].area();
if (a > max_area) {
max_area = a;
idx_max_area = i;
}
}
if (idx_max_area >= 0) expolys_out.emplace_back(std::move(expolys_avoid[idx_max_area]));
}
return expolys_out;
}
void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_nodes)
{
const PrintObjectConfig &config = m_object->config();
@ -1898,7 +1926,6 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
//Draw the support areas and add the roofs appropriately to the support roof instead of normal areas.
ts_layer->lslices.reserve(contact_nodes[layer_nr].size());
#if 1
for (const Node* p_node : contact_nodes[layer_nr])
{
if (print->canceled())
@ -1949,59 +1976,7 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
if (layer_nr < brim_skirt_layers)
ts_layer->lslices.emplace_back(area);
}
#else
// some nodes may not have radius set
for (Node* p_node : contact_nodes[layer_nr])
{
size_t layers_to_top = p_node->distance_to_top;// std::min(node.distance_to_top, (size_t)300);
double scale = static_cast<double>(layers_to_top + 1) / tip_layers;
scale = layers_to_top < tip_layers ? (0.5 + scale / 2) : (1 + static_cast<double>(layers_to_top - tip_layers) * diameter_angle_scale_factor);
p_node->radius = scale * branch_radius;
}
{
// now this method is extremely slow. Need to optimize the speed before we can use it.
Polygons layer_contours = std::move(m_ts_data->get_contours_with_holes(layer_nr));
std::vector<double> radiis;
std::vector<bool> is_interface;
//Polygons lines = spanning_tree_to_polygon(m_spanning_trees[layer_nr], layer_contours, layer_nr, radiis);
Polygons lines = contact_nodes_to_polygon(contact_nodes[layer_nr], layer_contours, layer_nr, radiis, is_interface);
for (int k = 0; k < lines.size(); k++) {
auto line = lines[k];
double radius = radiis[k];
Polygons line_expanded;
if (line.size() == 1)
{
Polygon circle;
double scale = radiis[k] / branch_radius;
for (auto iter = branch_circle.points.begin(); iter != branch_circle.points.end(); iter++)
{
Point corner = (*iter) * scale;
circle.append(line.first_point() + corner);
}
line_expanded.emplace_back(circle);
}
else {
line_expanded = offset(line, scale_(radius), jtRound, scale_(g_config_tree_support_collision_resolution));
}
if (line_expanded.empty())
continue;
if (is_interface[k])
roof_areas.emplace_back(line_expanded[0]);
else
base_areas.emplace_back(line_expanded[0]);
if (layer_nr < brim_skirt_layers)
ts_layer->lslices.emplace_back(line_expanded[0]);
//if (radius > config.support_base_pattern_spacing * 2)
// ts_layer->need_infill = true;
}
#ifdef SUPPORT_TREE_DEBUG_TO_SVG
draw_contours_and_nodes_to_svg( layer_nr, base_areas, to_expolygons(lines), m_ts_data->m_layer_outlines_below[layer_nr], {}, {}, "circles", { "lines","base_areas","outlines" });
#endif
}
#endif
ts_layer->lslices = std::move(union_ex(ts_layer->lslices));
//Must update bounding box which is used in avoid crossing perimeter
@ -2020,15 +1995,18 @@ void TreeSupport::draw_circles(const std::vector<std::vector<Node*>>& contact_no
// avoid object
auto avoid_region_interface = m_ts_data->get_collision(m_ts_data->m_xy_distance, layer_nr);
roof_areas = std::move(diff_ex(roof_areas, avoid_region_interface));
roof_1st_layer = std::move(diff_ex(roof_1st_layer, avoid_region_interface));
//roof_areas = std::move(diff_ex(roof_areas, avoid_region_interface));
//roof_1st_layer = std::move(diff_ex(roof_1st_layer, avoid_region_interface));
roof_areas = avoid_object_remove_extra_small_parts(roof_areas, avoid_region_interface);
roof_1st_layer = avoid_object_remove_extra_small_parts(roof_1st_layer, avoid_region_interface);
// roof_1st_layer and roof_areas may intersect, so need to subtract roof_areas from roof_1st_layer
roof_1st_layer = std::move(diff_ex(roof_1st_layer, roof_areas));
// let supports touch objects when brim is on
auto avoid_region = m_ts_data->get_collision((layer_nr == 0 && has_brim) ? config.brim_object_gap : m_ts_data->m_xy_distance, layer_nr);
base_areas = std::move(diff_ex(base_areas, avoid_region));
// base_areas = std::move(diff_ex(base_areas, avoid_region));
base_areas = avoid_object_remove_extra_small_parts(base_areas, avoid_region);
base_areas = std::move(diff_ex(base_areas, roof_areas));
base_areas = std::move(diff_ex(base_areas, roof_1st_layer));

View file

@ -372,7 +372,7 @@ inline std::string short_time(const std::string &time)
else if (hours > 0)
::sprintf(buffer, "%dh%dm", hours, minutes);
else if (minutes > 0)
::sprintf(buffer, "%dm", minutes);
::sprintf(buffer, "%dm%ds", minutes, seconds);
else
::sprintf(buffer, "%ds", seconds);
return buffer;