mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-25 15:44:12 -06:00
Merge mainstream changes
This commit is contained in:
commit
764b7d62a0
204 changed files with 5425 additions and 1411 deletions
|
@ -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!");
|
||||
}
|
||||
|
||||
|
|
|
@ -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()]);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ¶ms)
|
|||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 ¶ms);
|
||||
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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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 ¶ms, 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_
|
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 < : 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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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") {
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue