mirror of
https://git.mirrors.martin98.com/https://github.com/bambulab/BambuStudio.git
synced 2025-09-21 00:23:13 +08:00
ENH: smooth z dir speed and layer time
Jira: none generate continuitious outer wall speed in z dir generate continuitious layer time in z dir Signed-off-by: qing.zhang <qing.zhang@bambulab.com> Change-Id: I0ade543d2f9ee40f5cd65c533eb261d85e5eaa34
This commit is contained in:
parent
4ed1cd9cb9
commit
b52a9de509
@ -16,6 +16,24 @@ using ExPolygons = std::vector<ExPolygon>;
|
|||||||
class ExtrusionEntityCollection;
|
class ExtrusionEntityCollection;
|
||||||
class Extruder;
|
class Extruder;
|
||||||
|
|
||||||
|
struct LoopNode
|
||||||
|
{
|
||||||
|
//store outer wall and mark if it's loop
|
||||||
|
std::pair<ExPolygon, bool> exp;
|
||||||
|
int node_id;
|
||||||
|
int region_id;
|
||||||
|
int perimeter_id;
|
||||||
|
int loop_id = 0;
|
||||||
|
BoundingBox bbox;
|
||||||
|
int merged_id = -1;
|
||||||
|
|
||||||
|
//upper loop info
|
||||||
|
std::vector<int> upper_node_id;
|
||||||
|
|
||||||
|
//lower loop info
|
||||||
|
std::vector<int> lower_node_id;
|
||||||
|
};
|
||||||
|
|
||||||
// Each ExtrusionRole value identifies a distinct set of { extruder, speed }
|
// Each ExtrusionRole value identifies a distinct set of { extruder, speed }
|
||||||
enum ExtrusionRole : uint8_t {
|
enum ExtrusionRole : uint8_t {
|
||||||
erNone,
|
erNone,
|
||||||
|
@ -31,9 +31,12 @@ public:
|
|||||||
|
|
||||||
ExtrusionEntitiesPtr entities; // we own these entities
|
ExtrusionEntitiesPtr entities; // we own these entities
|
||||||
bool no_sort;
|
bool no_sort;
|
||||||
|
|
||||||
|
std::pair<int, int> loop_node_range;
|
||||||
ExtrusionEntityCollection(): no_sort(false) {}
|
ExtrusionEntityCollection(): no_sort(false) {}
|
||||||
ExtrusionEntityCollection(const ExtrusionEntityCollection &other) : no_sort(other.no_sort), is_reverse(other.is_reverse) { this->append(other.entities); }
|
ExtrusionEntityCollection(const ExtrusionEntityCollection &other) : no_sort(other.no_sort), is_reverse(other.is_reverse), loop_node_range(other.loop_node_range) { this->append(other.entities); }
|
||||||
ExtrusionEntityCollection(ExtrusionEntityCollection &&other) : entities(std::move(other.entities)), no_sort(other.no_sort), is_reverse(other.is_reverse) {}
|
ExtrusionEntityCollection(ExtrusionEntityCollection &&other)
|
||||||
|
: entities(std::move(other.entities)), no_sort(other.no_sort), is_reverse(other.is_reverse), loop_node_range(other.loop_node_range) {}
|
||||||
explicit ExtrusionEntityCollection(const ExtrusionPaths &paths);
|
explicit ExtrusionEntityCollection(const ExtrusionPaths &paths);
|
||||||
ExtrusionEntityCollection& operator=(const ExtrusionEntityCollection &other);
|
ExtrusionEntityCollection& operator=(const ExtrusionEntityCollection &other);
|
||||||
ExtrusionEntityCollection& operator=(ExtrusionEntityCollection &&other)
|
ExtrusionEntityCollection& operator=(ExtrusionEntityCollection &&other)
|
||||||
@ -41,6 +44,7 @@ public:
|
|||||||
this->entities = std::move(other.entities);
|
this->entities = std::move(other.entities);
|
||||||
this->no_sort = other.no_sort;
|
this->no_sort = other.no_sort;
|
||||||
is_reverse = other.is_reverse;
|
is_reverse = other.is_reverse;
|
||||||
|
loop_node_range = other.loop_node_range;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
~ExtrusionEntityCollection() { clear(); }
|
~ExtrusionEntityCollection() { clear(); }
|
||||||
|
@ -67,6 +67,7 @@ using namespace std::literals::string_view_literals;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <libslic3r/GCode/Smoothing.hpp>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -2405,8 +2406,18 @@ void GCode::process_layers(
|
|||||||
const std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> &layers_to_print,
|
const std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> &layers_to_print,
|
||||||
GCodeOutputStream &output_stream)
|
GCodeOutputStream &output_stream)
|
||||||
{
|
{
|
||||||
// The pipeline is variable: The vase mode filter is optional.
|
//BBS: get object label id
|
||||||
size_t layer_to_print_idx = 0;
|
size_t layer_to_print_idx = 0;
|
||||||
|
std::vector<int> object_label;
|
||||||
|
|
||||||
|
for (const PrintInstance *instance : print_object_instances_ordering)
|
||||||
|
object_label.push_back(instance->model_instance->get_labeled_id());
|
||||||
|
|
||||||
|
SmoothCalculator smooth_calculator(layers_to_print.size(), print.objects().size());
|
||||||
|
std::vector<std::vector<PerExtruderAdjustments>> layers_extruder_adjustments;
|
||||||
|
layers_extruder_adjustments.resize(layers_to_print.size());
|
||||||
|
|
||||||
|
// The pipeline is variable: The vase mode filter is optional.
|
||||||
const auto generator = tbb::make_filter<void, GCode::LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
const auto generator = tbb::make_filter<void, GCode::LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||||
[this, &print, &tool_ordering, &print_object_instances_ordering, &layers_to_print, &layer_to_print_idx](tbb::flow_control& fc) -> GCode::LayerResult {
|
[this, &print, &tool_ordering, &print_object_instances_ordering, &layers_to_print, &layer_to_print_idx](tbb::flow_control& fc) -> GCode::LayerResult {
|
||||||
if (layer_to_print_idx == layers_to_print.size()) {
|
if (layer_to_print_idx == layers_to_print.size()) {
|
||||||
@ -2435,19 +2446,72 @@ void GCode::process_layers(
|
|||||||
bool last_layer = in.layer_id == layers_to_print.size() - 1;
|
bool last_layer = in.layer_id == layers_to_print.size() - 1;
|
||||||
return { spiral_mode.process_layer(std::move(in.gcode), last_layer), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush};
|
return { spiral_mode.process_layer(std::move(in.gcode), last_layer), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush};
|
||||||
});
|
});
|
||||||
const auto cooling = tbb::make_filter<GCode::LayerResult, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
|
||||||
[&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in) -> std::string {
|
|
||||||
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
|
|
||||||
});
|
|
||||||
const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order,
|
|
||||||
[&output_stream](std::string s) { output_stream.write(s); }
|
|
||||||
);
|
|
||||||
|
|
||||||
|
std::vector<GCode::LayerResult> layers_results;
|
||||||
|
layers_results.resize(layers_to_print.size());
|
||||||
|
const auto cooling = tbb::make_filter<GCode::LayerResult, void>(slic3r_tbb_filtermode::serial_in_order,
|
||||||
|
[&cooling_buffer = *this->m_cooling_buffer.get(), object_label, &smooth_calculator, &layers_extruder_adjustments, &layers_results](GCode::LayerResult in) {
|
||||||
|
layers_results[in.layer_id] = in;
|
||||||
|
std::string out = cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, smooth_calculator, layers_extruder_adjustments[in.layer_id], object_label, in.cooling_buffer_flush, false);
|
||||||
|
if (in.layer_id == 0)
|
||||||
|
layers_results[in.layer_id].gcode = out;
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto cooling_vase = tbb::make_filter<GCode::LayerResult, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
||||||
|
[ &cooling_buffer = *this->m_cooling_buffer.get(), object_label, &smooth_calculator, &layers_extruder_adjustments](GCode::LayerResult in) -> std::string {
|
||||||
|
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, smooth_calculator, layers_extruder_adjustments[in.layer_id], object_label, in.cooling_buffer_flush, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// BBS: apply new feedrate of outwall and recalculate layer time
|
||||||
|
int layer_idx = 0;
|
||||||
|
bool layer_time_smoothing = m_config.layer_time_smoothing;
|
||||||
|
const auto cooling_smooth= tbb::make_filter<void, std::string>(slic3r_tbb_filtermode::serial_in_order, [&cooling_buffer = *this->m_cooling_buffer.get(), &layer_idx, object_label, &smooth_calculator, &layers_extruder_adjustments, &layers_to_print, &layers_results, layer_time_smoothing](tbb::flow_control& fc) -> std::string {
|
||||||
|
if(layer_idx == layers_to_print.size()){
|
||||||
|
fc.stop();
|
||||||
|
return{};
|
||||||
|
}else{
|
||||||
|
if (layer_idx > 0) {
|
||||||
|
const std::pair<coordf_t, std::vector<LayerToPrint>> &layer = layers_to_print[layer_idx++];
|
||||||
|
return cooling_buffer.apply_smooth_layer_time(std::move(layers_results[layer_idx - 1].gcode), smooth_calculator, layers_extruder_adjustments[layer_idx - 1], layer_idx - 1, layer_time_smoothing);
|
||||||
|
} else {
|
||||||
|
return layers_results[layer_idx++].gcode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order,
|
||||||
|
[&output_stream](std::string s) { output_stream.write(s); });
|
||||||
|
|
||||||
|
// BBS: apply cooling
|
||||||
// The pipeline elements are joined using const references, thus no copying is performed.
|
// The pipeline elements are joined using const references, thus no copying is performed.
|
||||||
if (m_spiral_vase)
|
if (m_spiral_vase)
|
||||||
tbb::parallel_pipeline(12, generator & spiral_mode & cooling & output);
|
tbb::parallel_pipeline(12, generator & spiral_mode & cooling_vase & output);
|
||||||
else
|
else if (!(layer_time_smoothing || m_config.z_direction_outwall_speed_continuous))
|
||||||
tbb::parallel_pipeline(12, generator & cooling & output);
|
tbb::parallel_pipeline(12, generator & cooling_vase & output);
|
||||||
|
else {
|
||||||
|
tbb::parallel_pipeline(12, generator & cooling);
|
||||||
|
|
||||||
|
if (m_config.z_direction_outwall_speed_continuous) {
|
||||||
|
smooth_calculator.smooth_layer_speed();
|
||||||
|
|
||||||
|
tbb::parallel_for(tbb::blocked_range<size_t>(0, layers_to_print.size()), [this, &print, &cooling_buffer = *this->m_cooling_buffer.get(), object_label, &smooth_calculator, &layers_extruder_adjustments, &layers_to_print, &layers_results](const tbb::blocked_range<size_t> &range) {
|
||||||
|
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||||
|
print.throw_if_canceled();
|
||||||
|
if (layer_idx > 0) {
|
||||||
|
const std::pair<coordf_t, std::vector<LayerToPrint>> &layer = layers_to_print[layer_idx];
|
||||||
|
cooling_buffer.apply_smooth_speed(smooth_calculator, layers_extruder_adjustments[layer_idx], layer_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//smooth layer time
|
||||||
|
if (layer_time_smoothing)
|
||||||
|
smooth_calculator.smooth_layer_time();
|
||||||
|
|
||||||
|
tbb::parallel_pipeline(12, cooling_smooth & output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process all layers of a single object instance (sequential mode) with a parallel pipeline:
|
// Process all layers of a single object instance (sequential mode) with a parallel pipeline:
|
||||||
@ -2462,8 +2526,17 @@ void GCode::process_layers(
|
|||||||
// BBS
|
// BBS
|
||||||
const bool prime_extruder)
|
const bool prime_extruder)
|
||||||
{
|
{
|
||||||
|
// BBS: get object label id
|
||||||
|
size_t layer_to_print_idx = 0;
|
||||||
|
std::vector<int> object_label;
|
||||||
|
for (LayerToPrint layer : layers_to_print)
|
||||||
|
object_label.push_back(layer.original_object->instances()[single_object_idx].model_instance->get_labeled_id());
|
||||||
|
|
||||||
|
SmoothCalculator smooth_calculator(layers_to_print.size(), print.objects().size());
|
||||||
|
std::vector<std::vector<PerExtruderAdjustments>> layers_extruder_adjustments;
|
||||||
|
layers_extruder_adjustments.resize(layers_to_print.size());
|
||||||
|
|
||||||
// The pipeline is variable: The vase mode filter is optional.
|
// The pipeline is variable: The vase mode filter is optional.
|
||||||
size_t layer_to_print_idx = 0;
|
|
||||||
const auto generator = tbb::make_filter<void, GCode::LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
const auto generator = tbb::make_filter<void, GCode::LayerResult>(slic3r_tbb_filtermode::serial_in_order,
|
||||||
[this, &print, &tool_ordering, &layers_to_print, &layer_to_print_idx, single_object_idx, prime_extruder](tbb::flow_control& fc) -> GCode::LayerResult {
|
[this, &print, &tool_ordering, &layers_to_print, &layer_to_print_idx, single_object_idx, prime_extruder](tbb::flow_control& fc) -> GCode::LayerResult {
|
||||||
if (layer_to_print_idx == layers_to_print.size()) {
|
if (layer_to_print_idx == layers_to_print.size()) {
|
||||||
@ -2489,19 +2562,75 @@ void GCode::process_layers(
|
|||||||
bool last_layer = in.layer_id == layers_to_print.size() - 1;
|
bool last_layer = in.layer_id == layers_to_print.size() - 1;
|
||||||
return { spiral_mode.process_layer(std::move(in.gcode), last_layer), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush };
|
return { spiral_mode.process_layer(std::move(in.gcode), last_layer), in.layer_id, in.spiral_vase_enable, in.cooling_buffer_flush };
|
||||||
});
|
});
|
||||||
const auto cooling = tbb::make_filter<GCode::LayerResult, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
|
||||||
[&cooling_buffer = *this->m_cooling_buffer.get()](GCode::LayerResult in)->std::string {
|
|
||||||
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, in.cooling_buffer_flush);
|
|
||||||
});
|
|
||||||
const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order,
|
|
||||||
[&output_stream](std::string s) { output_stream.write(s); }
|
|
||||||
);
|
|
||||||
|
|
||||||
|
//BBS: get objects and nodes info, for better arrange
|
||||||
|
const ConstPrintObjectPtrsAdaptor &objects = print.objects();
|
||||||
|
|
||||||
|
std::vector<GCode::LayerResult> layers_results;
|
||||||
|
layers_results.resize(layers_to_print.size());
|
||||||
|
const auto cooling = tbb::make_filter<GCode::LayerResult, void>(slic3r_tbb_filtermode::serial_in_order,
|
||||||
|
[&cooling_buffer = *this->m_cooling_buffer.get(), &smooth_calculator, &layers_extruder_adjustments, object_label, &layers_results](GCode::LayerResult in) {
|
||||||
|
layers_results[in.layer_id] = in;
|
||||||
|
std::string out = cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, smooth_calculator, layers_extruder_adjustments[in.layer_id], object_label, in.cooling_buffer_flush, false);
|
||||||
|
if (in.layer_id == 0)
|
||||||
|
layers_results[in.layer_id].gcode = out;
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto cooling_vase = tbb::make_filter<GCode::LayerResult, std::string>(slic3r_tbb_filtermode::serial_in_order,
|
||||||
|
[ &cooling_buffer = *this->m_cooling_buffer.get(), object_label, &smooth_calculator, &layers_extruder_adjustments](GCode::LayerResult in) -> std::string {
|
||||||
|
return cooling_buffer.process_layer(std::move(in.gcode), in.layer_id, smooth_calculator,layers_extruder_adjustments[in.layer_id], object_label, in.cooling_buffer_flush, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
// BBS: apply new feedrate of outwall and recalculate layer time
|
||||||
|
int layer_idx = 0;
|
||||||
|
bool layer_time_smoothing = m_config.layer_time_smoothing;
|
||||||
|
const auto cooling_smooth= tbb::make_filter<void, std::string>(slic3r_tbb_filtermode::serial_in_order, [&cooling_buffer = *this->m_cooling_buffer.get(), &layer_idx, object_label, &layers_to_print, &layers_results, layer_time_smoothing, &smooth_calculator, &layers_extruder_adjustments](tbb::flow_control& fc) -> std::string {
|
||||||
|
if(layer_idx == layers_to_print.size()){
|
||||||
|
fc.stop();
|
||||||
|
return{};
|
||||||
|
}else{
|
||||||
|
if (layer_idx > 0) {
|
||||||
|
LayerToPrint &layer = layers_to_print[layer_idx++];
|
||||||
|
return cooling_buffer.apply_smooth_layer_time(std::move(layers_results[layer_idx - 1].gcode), smooth_calculator, layers_extruder_adjustments[layer_idx - 1], layer_idx - 1, layer_time_smoothing);
|
||||||
|
} else {
|
||||||
|
return layers_results[layer_idx++].gcode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const auto output = tbb::make_filter<std::string, void>(slic3r_tbb_filtermode::serial_in_order,
|
||||||
|
[&output_stream](std::string s) { output_stream.write(s); });
|
||||||
|
|
||||||
|
// BBS: apply cooling
|
||||||
// The pipeline elements are joined using const references, thus no copying is performed.
|
// The pipeline elements are joined using const references, thus no copying is performed.
|
||||||
if (m_spiral_vase)
|
if (m_spiral_vase)
|
||||||
tbb::parallel_pipeline(12, generator & spiral_mode & cooling & output);
|
tbb::parallel_pipeline(12, generator & spiral_mode & cooling_vase & output);
|
||||||
else
|
else if (!(layer_time_smoothing || m_config.z_direction_outwall_speed_continuous))
|
||||||
tbb::parallel_pipeline(12, generator & cooling & output);
|
tbb::parallel_pipeline(12, generator & cooling_vase & output);
|
||||||
|
else {
|
||||||
|
tbb::parallel_pipeline(12, generator & cooling);
|
||||||
|
|
||||||
|
if (m_config.z_direction_outwall_speed_continuous) {
|
||||||
|
smooth_calculator.smooth_layer_speed();
|
||||||
|
|
||||||
|
tbb::parallel_for(tbb::blocked_range<size_t>(0, layers_to_print.size()), [this, &print, &cooling_buffer = *this->m_cooling_buffer.get(), object_label, &smooth_calculator, &layers_extruder_adjustments, &layers_to_print, &layers_results](const tbb::blocked_range<size_t> &range) {
|
||||||
|
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||||
|
print.throw_if_canceled();
|
||||||
|
if (layer_idx > 0) {
|
||||||
|
LayerToPrint &layer = layers_to_print[layer_idx++];
|
||||||
|
cooling_buffer.apply_smooth_speed(smooth_calculator, layers_extruder_adjustments[layer_idx], layer_idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// smooth layer time
|
||||||
|
if (layer_time_smoothing)
|
||||||
|
smooth_calculator.smooth_layer_time();
|
||||||
|
|
||||||
|
tbb::parallel_pipeline(12, cooling_smooth & output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override)
|
std::string GCode::placeholder_parser_process(const std::string &name, const std::string &templ, unsigned int current_extruder_id, const DynamicConfig *config_override)
|
||||||
@ -3364,7 +3493,8 @@ GCode::LayerResult GCode::process_layer(
|
|||||||
// The process is almost the same for perimeters and infills - we will do it in a cycle that repeats twice:
|
// The process is almost the same for perimeters and infills - we will do it in a cycle that repeats twice:
|
||||||
std::vector<unsigned int> printing_extruders;
|
std::vector<unsigned int> printing_extruders;
|
||||||
for (const ObjectByExtruder::Island::Region::Type entity_type : { ObjectByExtruder::Island::Region::INFILL, ObjectByExtruder::Island::Region::PERIMETERS }) {
|
for (const ObjectByExtruder::Island::Region::Type entity_type : { ObjectByExtruder::Island::Region::INFILL, ObjectByExtruder::Island::Region::PERIMETERS }) {
|
||||||
for (const ExtrusionEntity *ee : (entity_type == ObjectByExtruder::Island::Region::INFILL) ? layerm->fills.entities : layerm->perimeters.entities) {
|
bool is_infill = entity_type == ObjectByExtruder::Island::Region::INFILL;
|
||||||
|
for (const ExtrusionEntity *ee : is_infill ? layerm->fills.entities : layerm->perimeters.entities) {
|
||||||
// extrusions represents infill or perimeter extrusions of a single island.
|
// extrusions represents infill or perimeter extrusions of a single island.
|
||||||
assert(dynamic_cast<const ExtrusionEntityCollection*>(ee) != nullptr);
|
assert(dynamic_cast<const ExtrusionEntityCollection*>(ee) != nullptr);
|
||||||
const auto *extrusions = static_cast<const ExtrusionEntityCollection*>(ee);
|
const auto *extrusions = static_cast<const ExtrusionEntityCollection*>(ee);
|
||||||
@ -3417,6 +3547,15 @@ GCode::LayerResult GCode::process_layer(
|
|||||||
if (islands[island_idx].by_region.empty())
|
if (islands[island_idx].by_region.empty())
|
||||||
islands[island_idx].by_region.assign(print.num_print_regions(), ObjectByExtruder::Island::Region());
|
islands[island_idx].by_region.assign(print.num_print_regions(), ObjectByExtruder::Island::Region());
|
||||||
islands[island_idx].by_region[region.print_region_id()].append(entity_type, extrusions, entity_overrides);
|
islands[island_idx].by_region[region.print_region_id()].append(entity_type, extrusions, entity_overrides);
|
||||||
|
int start = extrusions->loop_node_range.first;
|
||||||
|
int end = extrusions->loop_node_range.second;
|
||||||
|
//BBS: add merged node infor
|
||||||
|
if (!is_infill) {
|
||||||
|
for (; start < end; ++start) {
|
||||||
|
const LoopNode *node = &layer.loop_nodes[start];
|
||||||
|
islands[island_idx].by_region[region.print_region_id()].merged_node.emplace_back(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3551,6 +3690,8 @@ GCode::LayerResult GCode::process_layer(
|
|||||||
if (m_config.reduce_crossing_wall)
|
if (m_config.reduce_crossing_wall)
|
||||||
m_avoid_crossing_perimeters.init_layer(*m_layer);
|
m_avoid_crossing_perimeters.init_layer(*m_layer);
|
||||||
|
|
||||||
|
//BBS: label object id, prepare for cooling
|
||||||
|
gcode += "; OBJECT_ID: " + std::to_string(instance_to_print.label_object_id) + "\n";
|
||||||
std::string temp_start_str;
|
std::string temp_start_str;
|
||||||
if (m_enable_label_object) {
|
if (m_enable_label_object) {
|
||||||
std::string start_str = std::string("; start printing object, unique label id: ") + std::to_string(instance_to_print.label_object_id) + "\n";
|
std::string start_str = std::string("; start printing object, unique label id: ") + std::to_string(instance_to_print.label_object_id) + "\n";
|
||||||
@ -4268,8 +4409,18 @@ std::string GCode::extrude_perimeters(const Print &print, const std::vector<Obje
|
|||||||
if (! region.perimeters.empty()) {
|
if (! region.perimeters.empty()) {
|
||||||
m_config.apply(print.get_print_region(®ion - &by_region.front()).config());
|
m_config.apply(print.get_print_region(®ion - &by_region.front()).config());
|
||||||
|
|
||||||
for (const ExtrusionEntity* ee : region.perimeters)
|
// BBS: output merged node id
|
||||||
|
int curr_node=0;
|
||||||
|
|
||||||
|
for (size_t perimeter_idx = 0; perimeter_idx < region.perimeters.size(); ++perimeter_idx) {
|
||||||
|
const ExtrusionEntity *ee = region.perimeters[perimeter_idx];
|
||||||
|
if (curr_node < region.merged_node.size() && perimeter_idx == region.merged_node[curr_node]->loop_id) {
|
||||||
|
gcode += "; COOLING_NODE: " + std::to_string(region.merged_node[curr_node]->merged_id) + "\n";
|
||||||
|
curr_node++;
|
||||||
|
}
|
||||||
|
|
||||||
gcode += this->extrude_entity(*ee, "perimeter", -1.);
|
gcode += this->extrude_entity(*ee, "perimeter", -1.);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return gcode;
|
return gcode;
|
||||||
}
|
}
|
||||||
@ -4894,26 +5045,29 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string comment;
|
std::string comment;
|
||||||
|
bool cooling_extrude = false;
|
||||||
if (m_enable_cooling_markers) {
|
if (m_enable_cooling_markers) {
|
||||||
if (EXTRUDER_CONFIG(enable_overhang_bridge_fan)) {
|
if (EXTRUDER_CONFIG(enable_overhang_bridge_fan)) {
|
||||||
//BBS: Overhang_threshold_none means Overhang_threshold_1_4 and forcing cooling for all external perimeter
|
//BBS: Overhang_threshold_none means Overhang_threshold_1_4 and forcing cooling for all external perimeter
|
||||||
int overhang_threshold = EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none ?
|
int overhang_threshold = EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none ?
|
||||||
Overhang_threshold_none : EXTRUDER_CONFIG(overhang_fan_threshold) - 1;
|
Overhang_threshold_none : EXTRUDER_CONFIG(overhang_fan_threshold) - 1;
|
||||||
if ((EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none && path.role() == erExternalPerimeter)) {
|
if ((EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none && path.role() == erExternalPerimeter) || (path.get_overhang_degree() > overhang_threshold ||
|
||||||
gcode += ";_OVERHANG_FAN_START\n";
|
is_bridge(path.role()))) {
|
||||||
} else if (path.get_overhang_degree() > overhang_threshold ||
|
|
||||||
is_bridge(path.role()))
|
|
||||||
gcode += ";_OVERHANG_FAN_START\n";
|
gcode += ";_OVERHANG_FAN_START\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int overhang_boundary_for_cooling = EXTRUDER_CONFIG(overhang_threshold_participating_cooling);
|
int overhang_boundary_for_cooling = EXTRUDER_CONFIG(overhang_threshold_participating_cooling);
|
||||||
|
|
||||||
if (!is_bridge(path.role()) && path.get_overhang_degree() <= overhang_boundary_for_cooling) {
|
if (!is_bridge(path.role()) && path.get_overhang_degree() <= overhang_boundary_for_cooling) {
|
||||||
|
cooling_extrude = true;
|
||||||
comment = ";_EXTRUDE_SET_SPEED";
|
comment = ";_EXTRUDE_SET_SPEED";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path.role() == erExternalPerimeter)
|
if (path.role() == erExternalPerimeter)
|
||||||
comment += ";_EXTERNAL_PERIMETER";
|
comment += ";_EXTERNAL_PERIMETER";
|
||||||
|
else if (path.role() == erPerimeter)
|
||||||
|
comment += ";_PERIMETER";
|
||||||
}
|
}
|
||||||
|
|
||||||
// F is mm per minute.
|
// F is mm per minute.
|
||||||
@ -5003,22 +5157,16 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (m_enable_cooling_markers) {
|
if (m_enable_cooling_markers) {
|
||||||
if (EXTRUDER_CONFIG(enable_overhang_bridge_fan)) {
|
if (cooling_extrude)
|
||||||
|
gcode += ";_EXTRUDE_END\n";
|
||||||
|
|
||||||
|
if ( EXTRUDER_CONFIG(enable_overhang_bridge_fan)) {
|
||||||
//BBS: Overhang_threshold_none means Overhang_threshold_1_4 and forcing cooling for all external perimeter
|
//BBS: Overhang_threshold_none means Overhang_threshold_1_4 and forcing cooling for all external perimeter
|
||||||
int overhang_threshold = EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none ?
|
int overhang_threshold = EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none ?
|
||||||
Overhang_threshold_none : EXTRUDER_CONFIG(overhang_fan_threshold) - 1;
|
Overhang_threshold_none : EXTRUDER_CONFIG(overhang_fan_threshold) - 1;
|
||||||
if ((EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none && path.role() == erExternalPerimeter)) {
|
if ((EXTRUDER_CONFIG(overhang_fan_threshold) == Overhang_threshold_none && path.role() == erExternalPerimeter) || (path.get_overhang_degree() > overhang_threshold ||
|
||||||
gcode += ";_EXTRUDE_END\n";
|
is_bridge(path.role())))
|
||||||
gcode += ";_OVERHANG_FAN_END\n";
|
gcode += ";_OVERHANG_FAN_END\n";
|
||||||
|
|
||||||
} else if (path.get_overhang_degree() > overhang_threshold ||
|
|
||||||
is_bridge(path.role()))
|
|
||||||
gcode += ";_OVERHANG_FAN_END\n";
|
|
||||||
else
|
|
||||||
gcode += ";_EXTRUDE_END\n";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
gcode += ";_EXTRUDE_END\n";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,7 +366,7 @@ private:
|
|||||||
ExtrusionEntitiesPtr perimeters;
|
ExtrusionEntitiesPtr perimeters;
|
||||||
// Non-owned references to LayerRegion::fills::entities
|
// Non-owned references to LayerRegion::fills::entities
|
||||||
ExtrusionEntitiesPtr infills;
|
ExtrusionEntitiesPtr infills;
|
||||||
|
std::vector<const LoopNode*> merged_node;
|
||||||
std::vector<const WipingExtrusions::ExtruderPerCopy*> infills_overrides;
|
std::vector<const WipingExtrusions::ExtruderPerCopy*> infills_overrides;
|
||||||
std::vector<const WipingExtrusions::ExtruderPerCopy*> perimeters_overrides;
|
std::vector<const WipingExtrusions::ExtruderPerCopy*> perimeters_overrides;
|
||||||
|
|
||||||
|
@ -41,202 +41,6 @@ void CoolingBuffer::reset(const Vec3d &position)
|
|||||||
m_current_fan_speed = -1;
|
m_current_fan_speed = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CoolingLine
|
|
||||||
{
|
|
||||||
enum Type {
|
|
||||||
TYPE_SET_TOOL = 1 << 0,
|
|
||||||
TYPE_EXTRUDE_END = 1 << 1,
|
|
||||||
TYPE_OVERHANG_FAN_START = 1 << 2,
|
|
||||||
TYPE_OVERHANG_FAN_END = 1 << 3,
|
|
||||||
TYPE_G0 = 1 << 4,
|
|
||||||
TYPE_G1 = 1 << 5,
|
|
||||||
TYPE_ADJUSTABLE = 1 << 6,
|
|
||||||
TYPE_EXTERNAL_PERIMETER = 1 << 7,
|
|
||||||
// The line sets a feedrate.
|
|
||||||
TYPE_HAS_F = 1 << 8,
|
|
||||||
TYPE_WIPE = 1 << 9,
|
|
||||||
TYPE_G4 = 1 << 10,
|
|
||||||
TYPE_G92 = 1 << 11,
|
|
||||||
//BBS: add G2 G3 type
|
|
||||||
TYPE_G2 = 1 << 12,
|
|
||||||
TYPE_G3 = 1 << 13,
|
|
||||||
TYPE_FORCE_RESUME_FAN = 1 << 14,
|
|
||||||
TYPE_SET_FAN_CHANGING_LAYER = 1 << 15,
|
|
||||||
};
|
|
||||||
|
|
||||||
CoolingLine(unsigned int type, size_t line_start, size_t line_end) :
|
|
||||||
type(type), line_start(line_start), line_end(line_end),
|
|
||||||
length(0.f), feedrate(0.f), time(0.f), time_max(0.f), slowdown(false) {}
|
|
||||||
|
|
||||||
bool adjustable(bool slowdown_external_perimeters) const {
|
|
||||||
return (this->type & TYPE_ADJUSTABLE) &&
|
|
||||||
(! (this->type & TYPE_EXTERNAL_PERIMETER) || slowdown_external_perimeters) &&
|
|
||||||
this->time < this->time_max;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool adjustable() const {
|
|
||||||
return (this->type & TYPE_ADJUSTABLE) && this->time < this->time_max;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t type;
|
|
||||||
// Start of this line at the G-code snippet.
|
|
||||||
size_t line_start;
|
|
||||||
// End of this line at the G-code snippet.
|
|
||||||
size_t line_end;
|
|
||||||
// XY Euclidian length of this segment.
|
|
||||||
float length;
|
|
||||||
// Current feedrate, possibly adjusted.
|
|
||||||
float feedrate;
|
|
||||||
// Current duration of this segment.
|
|
||||||
float time;
|
|
||||||
// Maximum duration of this segment.
|
|
||||||
float time_max;
|
|
||||||
// If marked with the "slowdown" flag, the line has been slowed down.
|
|
||||||
bool slowdown;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Calculate the required per extruder time stretches.
|
|
||||||
struct PerExtruderAdjustments
|
|
||||||
{
|
|
||||||
// Calculate the total elapsed time per this extruder, adjusted for the slowdown.
|
|
||||||
float elapsed_time_total() const {
|
|
||||||
float time_total = 0.f;
|
|
||||||
for (const CoolingLine &line : lines)
|
|
||||||
time_total += line.time;
|
|
||||||
return time_total;
|
|
||||||
}
|
|
||||||
// Calculate the total elapsed time when slowing down
|
|
||||||
// to the minimum extrusion feed rate defined for the current material.
|
|
||||||
float maximum_time_after_slowdown(bool slowdown_external_perimeters) const {
|
|
||||||
float time_total = 0.f;
|
|
||||||
for (const CoolingLine &line : lines)
|
|
||||||
if (line.adjustable(slowdown_external_perimeters)) {
|
|
||||||
if (line.time_max == FLT_MAX)
|
|
||||||
return FLT_MAX;
|
|
||||||
else
|
|
||||||
time_total += line.time_max;
|
|
||||||
} else
|
|
||||||
time_total += line.time;
|
|
||||||
return time_total;
|
|
||||||
}
|
|
||||||
// Calculate the adjustable part of the total time.
|
|
||||||
float adjustable_time(bool slowdown_external_perimeters) const {
|
|
||||||
float time_total = 0.f;
|
|
||||||
for (const CoolingLine &line : lines)
|
|
||||||
if (line.adjustable(slowdown_external_perimeters))
|
|
||||||
time_total += line.time;
|
|
||||||
return time_total;
|
|
||||||
}
|
|
||||||
// Calculate the non-adjustable part of the total time.
|
|
||||||
float non_adjustable_time(bool slowdown_external_perimeters) const {
|
|
||||||
float time_total = 0.f;
|
|
||||||
for (const CoolingLine &line : lines)
|
|
||||||
if (! line.adjustable(slowdown_external_perimeters))
|
|
||||||
time_total += line.time;
|
|
||||||
return time_total;
|
|
||||||
}
|
|
||||||
// Slow down the adjustable extrusions to the minimum feedrate allowed for the current extruder material.
|
|
||||||
// Used by both proportional and non-proportional slow down.
|
|
||||||
float slowdown_to_minimum_feedrate(bool slowdown_external_perimeters) {
|
|
||||||
float time_total = 0.f;
|
|
||||||
for (CoolingLine &line : lines) {
|
|
||||||
if (line.adjustable(slowdown_external_perimeters)) {
|
|
||||||
assert(line.time_max >= 0.f && line.time_max < FLT_MAX);
|
|
||||||
line.slowdown = true;
|
|
||||||
line.time = line.time_max;
|
|
||||||
line.feedrate = line.length / line.time;
|
|
||||||
}
|
|
||||||
time_total += line.time;
|
|
||||||
}
|
|
||||||
return time_total;
|
|
||||||
}
|
|
||||||
// Slow down each adjustable G-code line proportionally by a factor.
|
|
||||||
// Used by the proportional slow down.
|
|
||||||
float slow_down_proportional(float factor, bool slowdown_external_perimeters) {
|
|
||||||
assert(factor >= 1.f);
|
|
||||||
float time_total = 0.f;
|
|
||||||
for (CoolingLine &line : lines) {
|
|
||||||
if (line.adjustable(slowdown_external_perimeters)) {
|
|
||||||
line.slowdown = true;
|
|
||||||
line.time = std::min(line.time_max, line.time * factor);
|
|
||||||
line.feedrate = line.length / line.time;
|
|
||||||
}
|
|
||||||
time_total += line.time;
|
|
||||||
}
|
|
||||||
return time_total;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort the lines, adjustable first, higher feedrate first.
|
|
||||||
// Used by non-proportional slow down.
|
|
||||||
void sort_lines_by_decreasing_feedrate() {
|
|
||||||
std::sort(lines.begin(), lines.end(), [](const CoolingLine &l1, const CoolingLine &l2) {
|
|
||||||
bool adj1 = l1.adjustable();
|
|
||||||
bool adj2 = l2.adjustable();
|
|
||||||
return (adj1 == adj2) ? l1.feedrate > l2.feedrate : adj1;
|
|
||||||
});
|
|
||||||
for (n_lines_adjustable = 0;
|
|
||||||
n_lines_adjustable < lines.size() && this->lines[n_lines_adjustable].adjustable();
|
|
||||||
++ n_lines_adjustable);
|
|
||||||
time_non_adjustable = 0.f;
|
|
||||||
for (size_t i = n_lines_adjustable; i < lines.size(); ++ i)
|
|
||||||
time_non_adjustable += lines[i].time;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the maximum time stretch when slowing down to min_feedrate.
|
|
||||||
// Slowdown to min_feedrate shall be allowed for this extruder's material.
|
|
||||||
// Used by non-proportional slow down.
|
|
||||||
float time_stretch_when_slowing_down_to_feedrate(float min_feedrate) const {
|
|
||||||
float time_stretch = 0.f;
|
|
||||||
assert(this->slow_down_min_speed < min_feedrate + EPSILON);
|
|
||||||
for (size_t i = 0; i < n_lines_adjustable; ++ i) {
|
|
||||||
const CoolingLine &line = lines[i];
|
|
||||||
if (line.feedrate > min_feedrate)
|
|
||||||
time_stretch += line.time * (line.feedrate / min_feedrate - 1.f);
|
|
||||||
}
|
|
||||||
return time_stretch;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slow down all adjustable lines down to min_feedrate.
|
|
||||||
// Slowdown to min_feedrate shall be allowed for this extruder's material.
|
|
||||||
// Used by non-proportional slow down.
|
|
||||||
void slow_down_to_feedrate(float min_feedrate) {
|
|
||||||
assert(this->slow_down_min_speed < min_feedrate + EPSILON);
|
|
||||||
for (size_t i = 0; i < n_lines_adjustable; ++ i) {
|
|
||||||
CoolingLine &line = lines[i];
|
|
||||||
if (line.feedrate > min_feedrate) {
|
|
||||||
line.time *= std::max(1.f, line.feedrate / min_feedrate);
|
|
||||||
line.feedrate = min_feedrate;
|
|
||||||
line.slowdown = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extruder, for which the G-code will be adjusted.
|
|
||||||
unsigned int extruder_id = 0;
|
|
||||||
// Is the cooling slow down logic enabled for this extruder's material?
|
|
||||||
bool cooling_slow_down_enabled = false;
|
|
||||||
// Slow down the print down to slow_down_min_speed if the total layer time is below slow_down_layer_time.
|
|
||||||
float slow_down_layer_time = 0.f;
|
|
||||||
// Minimum print speed allowed for this extruder.
|
|
||||||
float slow_down_min_speed = 0.f;
|
|
||||||
|
|
||||||
// Parsed lines.
|
|
||||||
std::vector<CoolingLine> lines;
|
|
||||||
// The following two values are set by sort_lines_by_decreasing_feedrate():
|
|
||||||
// Number of adjustable lines, at the start of lines.
|
|
||||||
size_t n_lines_adjustable = 0;
|
|
||||||
// Non-adjustable time of lines starting with n_lines_adjustable.
|
|
||||||
float time_non_adjustable = 0;
|
|
||||||
// Current total time for this extruder.
|
|
||||||
float time_total = 0;
|
|
||||||
// Maximum time for this extruder, when the maximum slow down is applied.
|
|
||||||
float time_maximum = 0;
|
|
||||||
|
|
||||||
// Temporaries for processing the slow down. Both thresholds go from 0 to n_lines_adjustable.
|
|
||||||
size_t idx_line_begin = 0;
|
|
||||||
size_t idx_line_end = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Calculate a new feedrate when slowing down by time_stretch for segments faster than min_feedrate.
|
// Calculate a new feedrate when slowing down by time_stretch for segments faster than min_feedrate.
|
||||||
// Used by non-proportional slow down.
|
// Used by non-proportional slow down.
|
||||||
float new_feedrate_to_reach_time_stretch(
|
float new_feedrate_to_reach_time_stretch(
|
||||||
@ -294,7 +98,77 @@ finished:
|
|||||||
return new_feedrate;
|
return new_feedrate;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CoolingBuffer::process_layer(std::string &&gcode, size_t layer_id, bool flush)
|
static void exclude_participate_in_speed_slowdown(std::vector<std::pair<int, int>> &lines, std::vector<PerExtruderAdjustments> &per_extruder_adjustments, CoolingNode &node, bool is_outwall)
|
||||||
|
{
|
||||||
|
for (std::pair<int, int> line_pos : lines) {
|
||||||
|
CoolingLine &line = per_extruder_adjustments[line_pos.second].lines[line_pos.first];
|
||||||
|
if (line.feedrate > node.filter_feedrate) {
|
||||||
|
if(is_outwall)
|
||||||
|
line.feedrate = node.filter_feedrate;
|
||||||
|
else
|
||||||
|
line.feedrate *= node.rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
// not adjust outwal line speed
|
||||||
|
line.type = line.type & (~CoolingLine::TYPE_ADJUSTABLE);
|
||||||
|
// update time cost
|
||||||
|
if (line.feedrate == 0 || line.length == 0)
|
||||||
|
line.time = 0;
|
||||||
|
else
|
||||||
|
line.time = line.length / line.feedrate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//BBS: apply the smooth outwall speed and recalculate the layer time
|
||||||
|
void CoolingBuffer::apply_smooth_speed(SmoothCalculator &smooth_calculator, std::vector<PerExtruderAdjustments> &extruder_adjustments, const int layer_id)
|
||||||
|
{
|
||||||
|
//rewrite feedrate
|
||||||
|
for (size_t obj_id = 0; obj_id < smooth_calculator.layers_wall_collection[layer_id].size(); ++obj_id) {
|
||||||
|
for (size_t node_id = 0; node_id < smooth_calculator.layers_wall_collection[layer_id][obj_id].cooling_nodes.size(); ++node_id) {
|
||||||
|
CoolingNode &node = smooth_calculator.layers_wall_collection[layer_id][obj_id].cooling_nodes[node_id];
|
||||||
|
node.rate = node.filter_feedrate / node.max_feedrate;
|
||||||
|
//set outwall speed
|
||||||
|
exclude_participate_in_speed_slowdown(node.outwall_line, extruder_adjustments, node, true);
|
||||||
|
|
||||||
|
//set inner wall speed
|
||||||
|
exclude_participate_in_speed_slowdown(node.innerwall_line, extruder_adjustments, node, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//recalculate cooling of other paths
|
||||||
|
smooth_calculator.layers_cooling_time[layer_id] = this->calculate_layer_slowdown(extruder_adjustments);
|
||||||
|
|
||||||
|
}
|
||||||
|
//BBS: apply new layer time and rewite the gcode feedrate
|
||||||
|
std::string CoolingBuffer::apply_smooth_layer_time(std::string && gcode,
|
||||||
|
SmoothCalculator & smooth_calculator,
|
||||||
|
std::vector<PerExtruderAdjustments> &extruder_adjustments,
|
||||||
|
const int layer_id,
|
||||||
|
const bool smooth_layer_time)
|
||||||
|
{
|
||||||
|
if (smooth_layer_time) {
|
||||||
|
// update layer time
|
||||||
|
for (PerExtruderAdjustments &adj : extruder_adjustments)
|
||||||
|
if (adj.slow_down_layer_time < smooth_calculator.layers_cooling_time[layer_id])
|
||||||
|
adj.slow_down_layer_time = smooth_calculator.layers_cooling_time[layer_id];
|
||||||
|
|
||||||
|
// recalculate feedrate
|
||||||
|
smooth_calculator.layers_cooling_time[layer_id] = this->calculate_layer_slowdown(extruder_adjustments);
|
||||||
|
}
|
||||||
|
// apply cooling
|
||||||
|
std::string out;
|
||||||
|
out = this->apply_layer_cooldown(gcode, layer_id, smooth_calculator.layers_cooling_time[layer_id], extruder_adjustments);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CoolingBuffer::process_layer(std::string && gcode,
|
||||||
|
const size_t layer_id,
|
||||||
|
SmoothCalculator & smooth_calculator,
|
||||||
|
std::vector<PerExtruderAdjustments> &per_extruder_adjustments,
|
||||||
|
const std::vector<int> & object_label,
|
||||||
|
const bool flush,
|
||||||
|
const bool spiral_vase)
|
||||||
{
|
{
|
||||||
// Cache the input G-code.
|
// Cache the input G-code.
|
||||||
if (m_gcode.empty())
|
if (m_gcode.empty())
|
||||||
@ -306,17 +180,96 @@ std::string CoolingBuffer::process_layer(std::string &&gcode, size_t layer_id, b
|
|||||||
if (flush) {
|
if (flush) {
|
||||||
// This is either an object layer or the very last print layer. Calculate cool down over the collected support layers
|
// This is either an object layer or the very last print layer. Calculate cool down over the collected support layers
|
||||||
// and one object layer.
|
// and one object layer.
|
||||||
std::vector<PerExtruderAdjustments> per_extruder_adjustments = this->parse_layer_gcode(m_gcode, m_current_pos);
|
// initial and arrange node collection seq
|
||||||
float layer_time_stretched = this->calculate_layer_slowdown(per_extruder_adjustments);
|
if (!spiral_vase && layer_id > 0)
|
||||||
out = this->apply_layer_cooldown(m_gcode, layer_id, layer_time_stretched, per_extruder_adjustments);
|
smooth_calculator.init_wall_collection(layer_id, object_label);
|
||||||
|
|
||||||
|
//record parse gcode info
|
||||||
|
per_extruder_adjustments = this->parse_layer_gcode(m_gcode, m_current_pos, object_label, spiral_vase, layer_id > 0);
|
||||||
|
|
||||||
|
//adjustments will be sorted on calculate layer slowdown
|
||||||
|
smooth_calculator.layers_cooling_time[layer_id] = this->calculate_layer_slowdown(per_extruder_adjustments);
|
||||||
|
if (spiral_vase || layer_id == 0) {
|
||||||
|
out = this->apply_layer_cooldown(m_gcode, layer_id, smooth_calculator.layers_cooling_time[layer_id], per_extruder_adjustments);
|
||||||
|
} else if (layer_id > 0) {
|
||||||
|
//BBS: update outwall feedrate
|
||||||
|
// update feedrate of outwall after initial cooling process
|
||||||
|
|
||||||
|
for (size_t extruder_idx = 0; extruder_idx < per_extruder_adjustments.size(); ++extruder_idx) {
|
||||||
|
PerExtruderAdjustments &extruder_adjustments = per_extruder_adjustments[extruder_idx];
|
||||||
|
for (size_t line_idx = 0; line_idx < extruder_adjustments.lines.size(); ++line_idx) {
|
||||||
|
CoolingLine &line = extruder_adjustments.lines[line_idx];
|
||||||
|
if (line.outwall_smooth_mark) {
|
||||||
|
line.slowdown = true;
|
||||||
|
|
||||||
|
// search node id
|
||||||
|
if (smooth_calculator.layers_wall_collection[layer_id][line.object_id].cooling_nodes.count(line.cooling_node_id) < 0) {
|
||||||
|
CoolingNode node;
|
||||||
|
smooth_calculator.layers_wall_collection[layer_id][line.object_id].cooling_nodes.emplace(line.cooling_node_id, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
CoolingNode &node = smooth_calculator.layers_wall_collection[layer_id][line.object_id].cooling_nodes[line.cooling_node_id];
|
||||||
|
if( line.type & CoolingLine::TYPE_EXTERNAL_PERIMETER ) {
|
||||||
|
node.outwall_line.emplace_back(line_idx, extruder_idx);
|
||||||
|
if (node.max_feedrate < line.feedrate) {
|
||||||
|
node.max_feedrate = line.feedrate;
|
||||||
|
node.filter_feedrate = node.max_feedrate;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
node.innerwall_line.emplace_back(line_idx, extruder_idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
line.slowdown = false;
|
||||||
|
line.feedrate = line.origin_feedrate;
|
||||||
|
// update time cost
|
||||||
|
if (line.feedrate == 0 || line.length == 0)
|
||||||
|
line.time = 0;
|
||||||
|
else
|
||||||
|
line.time = line.length / line.feedrate;
|
||||||
|
line.time_max = line.origin_time_max;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
m_gcode.clear();
|
m_gcode.clear();
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void mark_node_pos(bool &flag, int &line_idx, std::pair<int, int> &node_pos, const std::vector<int> &object_label, int cooling_node_id, int object_id, PerExtruderAdjustments* adjustment)
|
||||||
|
{
|
||||||
|
for (size_t object_idx = 0; object_idx < object_label.size(); ++object_idx) {
|
||||||
|
if (object_label[object_idx] == object_id) {
|
||||||
|
if (cooling_node_id == -1) break;
|
||||||
|
line_idx = adjustment->lines.size();
|
||||||
|
flag = true;
|
||||||
|
node_pos.first = object_idx;
|
||||||
|
node_pos.second = cooling_node_id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void record_wall_lines(bool &flag, int &line_idx, PerExtruderAdjustments *adjustment, const std::pair<int, int> &node_pos)
|
||||||
|
{
|
||||||
|
if (flag && line_idx < adjustment->lines.size()) {
|
||||||
|
CoolingLine &ptr = adjustment->lines[line_idx];
|
||||||
|
ptr.outwall_smooth_mark = true;
|
||||||
|
ptr.object_id = node_pos.first;
|
||||||
|
ptr.cooling_node_id = node_pos.second;
|
||||||
|
flag = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//native-resource://sandbox_fs/webcontent/resource/assets/img/41ecc25c56.png
|
||||||
// Parse the layer G-code for the moves, which could be adjusted.
|
// Parse the layer G-code for the moves, which could be adjusted.
|
||||||
// Return the list of parsed lines, bucketed by an extruder.
|
// Return the list of parsed lines, bucketed by an extruder.
|
||||||
std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::string &gcode, std::vector<float> ¤t_pos) const
|
std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::string & gcode,
|
||||||
|
std::vector<float> & current_pos,
|
||||||
|
const std::vector<int> & object_label,
|
||||||
|
bool spiral_vase,
|
||||||
|
bool join_z_smooth) const
|
||||||
{
|
{
|
||||||
std::vector<PerExtruderAdjustments> per_extruder_adjustments(m_extruder_ids.size());
|
std::vector<PerExtruderAdjustments> per_extruder_adjustments(m_extruder_ids.size());
|
||||||
std::vector<size_t> map_extruder_to_per_extruder_adjustment(m_num_extruders, 0);
|
std::vector<size_t> map_extruder_to_per_extruder_adjustment(m_num_extruders, 0);
|
||||||
@ -337,7 +290,15 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
|
|||||||
// Index of an existing CoolingLine of the current adjustment, which holds the feedrate setting command
|
// Index of an existing CoolingLine of the current adjustment, which holds the feedrate setting command
|
||||||
// for a sequence of extrusion moves.
|
// for a sequence of extrusion moves.
|
||||||
size_t active_speed_modifier = size_t(-1);
|
size_t active_speed_modifier = size_t(-1);
|
||||||
|
int object_id = -1;
|
||||||
|
int cooling_node_id = -1;
|
||||||
|
std::string object_id_string = "; OBJECT_ID: ";
|
||||||
|
std::string cooling_node_label = "; COOLING_NODE: ";
|
||||||
|
bool append_wall_ptr = false;
|
||||||
|
bool append_inner_wall_ptr = false;
|
||||||
|
|
||||||
|
std::pair<int, int> node_pos;
|
||||||
|
int line_idx = -1;
|
||||||
for (; *line_start != 0; line_start = line_end)
|
for (; *line_start != 0; line_start = line_end)
|
||||||
{
|
{
|
||||||
while (*line_end != '\n' && *line_end != 0)
|
while (*line_end != '\n' && *line_end != 0)
|
||||||
@ -358,6 +319,15 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
|
|||||||
line.type = CoolingLine::TYPE_G2;
|
line.type = CoolingLine::TYPE_G2;
|
||||||
else if (boost::starts_with(sline, "G3 "))
|
else if (boost::starts_with(sline, "G3 "))
|
||||||
line.type = CoolingLine::TYPE_G3;
|
line.type = CoolingLine::TYPE_G3;
|
||||||
|
//BBS: parse object id & node id
|
||||||
|
else if (boost::starts_with(sline, object_id_string)) {
|
||||||
|
std::string sub = sline.substr(object_id_string.size());
|
||||||
|
object_id = std::stoi(sub);
|
||||||
|
} else if (boost::starts_with(sline, cooling_node_label)) {
|
||||||
|
std::string sub = sline.substr(cooling_node_label.size());
|
||||||
|
cooling_node_id = std::stoi(sub);
|
||||||
|
}
|
||||||
|
|
||||||
if (line.type) {
|
if (line.type) {
|
||||||
// G0, G1 or G92
|
// G0, G1 or G92
|
||||||
// Parse the G-code line.
|
// Parse the G-code line.
|
||||||
@ -392,14 +362,32 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
|
|||||||
}
|
}
|
||||||
bool external_perimeter = boost::contains(sline, ";_EXTERNAL_PERIMETER");
|
bool external_perimeter = boost::contains(sline, ";_EXTERNAL_PERIMETER");
|
||||||
bool wipe = boost::contains(sline, ";_WIPE");
|
bool wipe = boost::contains(sline, ";_WIPE");
|
||||||
if (external_perimeter)
|
bool inner_perimeter = boost::contains(sline,";_PERIMETER");
|
||||||
line.type |= CoolingLine::TYPE_EXTERNAL_PERIMETER;
|
|
||||||
|
record_wall_lines(append_inner_wall_ptr, line_idx, adjustment, node_pos);
|
||||||
|
|
||||||
if (wipe)
|
if (wipe)
|
||||||
line.type |= CoolingLine::TYPE_WIPE;
|
line.type |= CoolingLine::TYPE_WIPE;
|
||||||
if (boost::contains(sline, ";_EXTRUDE_SET_SPEED") && ! wipe) {
|
if (boost::contains(sline, ";_EXTRUDE_SET_SPEED") && ! wipe) {
|
||||||
line.type |= CoolingLine::TYPE_ADJUSTABLE;
|
line.type |= CoolingLine::TYPE_ADJUSTABLE;
|
||||||
active_speed_modifier = adjustment->lines.size();
|
active_speed_modifier = adjustment->lines.size();
|
||||||
|
|
||||||
|
if (inner_perimeter && join_z_smooth && !spiral_vase) {
|
||||||
|
// BBS: collect innerwall info
|
||||||
|
mark_node_pos(append_inner_wall_ptr, line_idx, node_pos, object_label, cooling_node_id, object_id, adjustment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
record_wall_lines(append_wall_ptr, line_idx, adjustment, node_pos);
|
||||||
|
|
||||||
|
if (external_perimeter) {
|
||||||
|
line.type |= CoolingLine::TYPE_EXTERNAL_PERIMETER;
|
||||||
|
if (line.type & CoolingLine::TYPE_ADJUSTABLE && join_z_smooth && !spiral_vase) {
|
||||||
|
// BBS: collect outwall info
|
||||||
|
mark_node_pos(append_wall_ptr, line_idx, node_pos, object_label, cooling_node_id, object_id, adjustment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ((line.type & CoolingLine::TYPE_G92) == 0) {
|
if ((line.type & CoolingLine::TYPE_G92) == 0) {
|
||||||
//BBS: G0, G1, G2, G3. Calculate the duration.
|
//BBS: G0, G1, G2, G3. Calculate the duration.
|
||||||
if (m_config.use_relative_e_distances.value)
|
if (m_config.use_relative_e_distances.value)
|
||||||
@ -429,12 +417,19 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
|
|||||||
line.length = std::abs(dif[3]);
|
line.length = std::abs(dif[3]);
|
||||||
}
|
}
|
||||||
line.feedrate = new_pos[4];
|
line.feedrate = new_pos[4];
|
||||||
|
line.origin_feedrate = new_pos[4];
|
||||||
|
|
||||||
assert((line.type & CoolingLine::TYPE_ADJUSTABLE) == 0 || line.feedrate > 0.f);
|
assert((line.type & CoolingLine::TYPE_ADJUSTABLE) == 0 || line.feedrate > 0.f);
|
||||||
if (line.length > 0)
|
if (line.length > 0)
|
||||||
line.time = line.length / line.feedrate;
|
line.time = line.length / line.feedrate;
|
||||||
|
|
||||||
|
if (line.feedrate == 0)
|
||||||
|
line.time = 0;
|
||||||
|
|
||||||
line.time_max = line.time;
|
line.time_max = line.time;
|
||||||
if ((line.type & CoolingLine::TYPE_ADJUSTABLE) || active_speed_modifier != size_t(-1))
|
if ((line.type & CoolingLine::TYPE_ADJUSTABLE) || active_speed_modifier != size_t(-1))
|
||||||
line.time_max = (adjustment->slow_down_min_speed == 0.f) ? FLT_MAX : std::max(line.time, line.length / adjustment->slow_down_min_speed);
|
line.time_max = (adjustment->slow_down_min_speed == 0.f) ? FLT_MAX : std::max(line.time, line.length / adjustment->slow_down_min_speed);
|
||||||
|
line.origin_time_max = line.time_max;
|
||||||
// BBS: add G2 and G3 support
|
// BBS: add G2 and G3 support
|
||||||
if (active_speed_modifier < adjustment->lines.size() && ((line.type & CoolingLine::TYPE_G1) ||
|
if (active_speed_modifier < adjustment->lines.size() && ((line.type & CoolingLine::TYPE_G1) ||
|
||||||
(line.type & CoolingLine::TYPE_G2) ||
|
(line.type & CoolingLine::TYPE_G2) ||
|
||||||
@ -450,6 +445,8 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
|
|||||||
sm.time_max = FLT_MAX;
|
sm.time_max = FLT_MAX;
|
||||||
else
|
else
|
||||||
sm.time_max += line.time_max;
|
sm.time_max += line.time_max;
|
||||||
|
|
||||||
|
sm.origin_time_max = sm.time_max;
|
||||||
}
|
}
|
||||||
// Don't store this line.
|
// Don't store this line.
|
||||||
line.type = 0;
|
line.type = 0;
|
||||||
@ -489,6 +486,7 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
|
|||||||
line.time = line.time_max = float(
|
line.time = line.time_max = float(
|
||||||
(pos_S > 0) ? atof(sline.c_str() + pos_S + 1) :
|
(pos_S > 0) ? atof(sline.c_str() + pos_S + 1) :
|
||||||
(pos_P > 0) ? atof(sline.c_str() + pos_P + 1) * 0.001 : 0.);
|
(pos_P > 0) ? atof(sline.c_str() + pos_P + 1) * 0.001 : 0.);
|
||||||
|
line.origin_time_max = line.time_max;
|
||||||
} else if (boost::starts_with(sline, ";_FORCE_RESUME_FAN_SPEED")) {
|
} else if (boost::starts_with(sline, ";_FORCE_RESUME_FAN_SPEED")) {
|
||||||
line.type = CoolingLine::TYPE_FORCE_RESUME_FAN;
|
line.type = CoolingLine::TYPE_FORCE_RESUME_FAN;
|
||||||
} else if (boost::starts_with(sline, ";_SET_FAN_SPEED_CHANGING_LAYER")) {
|
} else if (boost::starts_with(sline, ";_SET_FAN_SPEED_CHANGING_LAYER")) {
|
||||||
|
@ -4,13 +4,215 @@
|
|||||||
#include "../libslic3r.h"
|
#include "../libslic3r.h"
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <libslic3r/GCode/Smoothing.hpp>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
class GCode;
|
class GCode;
|
||||||
class Layer;
|
class Layer;
|
||||||
struct PerExtruderAdjustments;
|
|
||||||
|
|
||||||
|
struct CoolingLine
|
||||||
|
{
|
||||||
|
enum Type {
|
||||||
|
TYPE_SET_TOOL = 1 << 0,
|
||||||
|
TYPE_EXTRUDE_END = 1 << 1,
|
||||||
|
TYPE_OVERHANG_FAN_START = 1 << 2,
|
||||||
|
TYPE_OVERHANG_FAN_END = 1 << 3,
|
||||||
|
TYPE_G0 = 1 << 4,
|
||||||
|
TYPE_G1 = 1 << 5,
|
||||||
|
TYPE_ADJUSTABLE = 1 << 6,
|
||||||
|
TYPE_EXTERNAL_PERIMETER = 1 << 7,
|
||||||
|
// The line sets a feedrate.
|
||||||
|
TYPE_HAS_F = 1 << 8,
|
||||||
|
TYPE_WIPE = 1 << 9,
|
||||||
|
TYPE_G4 = 1 << 10,
|
||||||
|
TYPE_G92 = 1 << 11,
|
||||||
|
// BBS: add G2 G3 type
|
||||||
|
TYPE_G2 = 1 << 12,
|
||||||
|
TYPE_G3 = 1 << 13,
|
||||||
|
TYPE_FORCE_RESUME_FAN = 1 << 14,
|
||||||
|
TYPE_SET_FAN_CHANGING_LAYER = 1 << 15,
|
||||||
|
};
|
||||||
|
|
||||||
|
CoolingLine(unsigned int type, size_t line_start, size_t line_end)
|
||||||
|
: type(type), line_start(line_start), line_end(line_end), length(0.f), feedrate(0.f), origin_feedrate(0.f), time(0.f), time_max(0.f), slowdown(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool adjustable(bool slowdown_external_perimeters) const
|
||||||
|
{
|
||||||
|
return (this->type & TYPE_ADJUSTABLE) && (!(this->type & TYPE_EXTERNAL_PERIMETER) || slowdown_external_perimeters) && this->time < this->time_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool adjustable() const { return (this->type & TYPE_ADJUSTABLE) && this->time < this->time_max; }
|
||||||
|
|
||||||
|
size_t type;
|
||||||
|
// Start of this line at the G-code snippet.
|
||||||
|
size_t line_start;
|
||||||
|
// End of this line at the G-code snippet.
|
||||||
|
size_t line_end;
|
||||||
|
// XY Euclidian length of this segment.
|
||||||
|
float length;
|
||||||
|
// Current feedrate, possibly adjusted.
|
||||||
|
float feedrate;
|
||||||
|
// Current duration of this segment.
|
||||||
|
float time;
|
||||||
|
// Maximum duration of this segment.
|
||||||
|
float time_max;
|
||||||
|
// If marked with the "slowdown" flag, the line has been slowed down.
|
||||||
|
bool slowdown;
|
||||||
|
// Current feedrate, possibly adjusted.
|
||||||
|
float origin_feedrate = 0;
|
||||||
|
float origin_time_max = 0;
|
||||||
|
// Current duration of this segment.
|
||||||
|
//float origin_time;
|
||||||
|
bool outwall_smooth_mark = false;
|
||||||
|
int object_id = -1;
|
||||||
|
int cooling_node_id = -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PerExtruderAdjustments
|
||||||
|
{
|
||||||
|
// Calculate the total elapsed time per this extruder, adjusted for the slowdown.
|
||||||
|
float elapsed_time_total() const
|
||||||
|
{
|
||||||
|
float time_total = 0.f;
|
||||||
|
for (const CoolingLine &line : lines) time_total += line.time;
|
||||||
|
return time_total;
|
||||||
|
}
|
||||||
|
// Calculate the total elapsed time when slowing down
|
||||||
|
// to the minimum extrusion feed rate defined for the current material.
|
||||||
|
float maximum_time_after_slowdown(bool slowdown_external_perimeters) const
|
||||||
|
{
|
||||||
|
float time_total = 0.f;
|
||||||
|
for (const CoolingLine &line : lines)
|
||||||
|
if (line.adjustable(slowdown_external_perimeters)) {
|
||||||
|
if (line.time_max == FLT_MAX)
|
||||||
|
return FLT_MAX;
|
||||||
|
else
|
||||||
|
time_total += line.time_max;
|
||||||
|
} else
|
||||||
|
time_total += line.time;
|
||||||
|
return time_total;
|
||||||
|
}
|
||||||
|
// Calculate the adjustable part of the total time.
|
||||||
|
float adjustable_time(bool slowdown_external_perimeters) const
|
||||||
|
{
|
||||||
|
float time_total = 0.f;
|
||||||
|
for (const CoolingLine &line : lines)
|
||||||
|
if (line.adjustable(slowdown_external_perimeters)) time_total += line.time;
|
||||||
|
return time_total;
|
||||||
|
}
|
||||||
|
// Calculate the non-adjustable part of the total time.
|
||||||
|
float non_adjustable_time(bool slowdown_external_perimeters) const
|
||||||
|
{
|
||||||
|
float time_total = 0.f;
|
||||||
|
for (const CoolingLine &line : lines)
|
||||||
|
if (!line.adjustable(slowdown_external_perimeters)) time_total += line.time;
|
||||||
|
return time_total;
|
||||||
|
}
|
||||||
|
// Slow down the adjustable extrusions to the minimum feedrate allowed for the current extruder material.
|
||||||
|
// Used by both proportional and non-proportional slow down.
|
||||||
|
float slowdown_to_minimum_feedrate(bool slowdown_external_perimeters)
|
||||||
|
{
|
||||||
|
float time_total = 0.f;
|
||||||
|
for (CoolingLine &line : lines) {
|
||||||
|
if (line.adjustable(slowdown_external_perimeters)) {
|
||||||
|
assert(line.time_max >= 0.f && line.time_max < FLT_MAX);
|
||||||
|
line.slowdown = true;
|
||||||
|
line.time = line.time_max;
|
||||||
|
line.feedrate = line.length / line.time;
|
||||||
|
}
|
||||||
|
time_total += line.time;
|
||||||
|
}
|
||||||
|
return time_total;
|
||||||
|
}
|
||||||
|
// Slow down each adjustable G-code line proportionally by a factor.
|
||||||
|
// Used by the proportional slow down.
|
||||||
|
float slow_down_proportional(float factor, bool slowdown_external_perimeters)
|
||||||
|
{
|
||||||
|
assert(factor >= 1.f);
|
||||||
|
float time_total = 0.f;
|
||||||
|
for (CoolingLine &line : lines) {
|
||||||
|
if (line.adjustable(slowdown_external_perimeters)) {
|
||||||
|
line.slowdown = true;
|
||||||
|
line.time = std::min(line.time_max, line.time * factor);
|
||||||
|
line.feedrate = line.length / line.time;
|
||||||
|
}
|
||||||
|
time_total += line.time;
|
||||||
|
}
|
||||||
|
return time_total;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort the lines, adjustable first, higher feedrate first.
|
||||||
|
// Used by non-proportional slow down.
|
||||||
|
void sort_lines_by_decreasing_feedrate()
|
||||||
|
{
|
||||||
|
std::sort(lines.begin(), lines.end(), [](const CoolingLine &l1, const CoolingLine &l2) {
|
||||||
|
bool adj1 = l1.adjustable();
|
||||||
|
bool adj2 = l2.adjustable();
|
||||||
|
return (adj1 == adj2) ? l1.feedrate > l2.feedrate : adj1;
|
||||||
|
});
|
||||||
|
for (n_lines_adjustable = 0; n_lines_adjustable < lines.size() && this->lines[n_lines_adjustable].adjustable(); ++n_lines_adjustable)
|
||||||
|
;
|
||||||
|
time_non_adjustable = 0.f;
|
||||||
|
for (size_t i = n_lines_adjustable; i < lines.size(); ++i) time_non_adjustable += lines[i].time;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the maximum time stretch when slowing down to min_feedrate.
|
||||||
|
// Slowdown to min_feedrate shall be allowed for this extruder's material.
|
||||||
|
// Used by non-proportional slow down.
|
||||||
|
float time_stretch_when_slowing_down_to_feedrate(float min_feedrate) const
|
||||||
|
{
|
||||||
|
float time_stretch = 0.f;
|
||||||
|
assert(this->slow_down_min_speed < min_feedrate + EPSILON);
|
||||||
|
for (size_t i = 0; i < n_lines_adjustable; ++i) {
|
||||||
|
const CoolingLine &line = lines[i];
|
||||||
|
if (line.feedrate > min_feedrate) time_stretch += line.time * (line.feedrate / min_feedrate - 1.f);
|
||||||
|
}
|
||||||
|
return time_stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Slow down all adjustable lines down to min_feedrate.
|
||||||
|
// Slowdown to min_feedrate shall be allowed for this extruder's material.
|
||||||
|
// Used by non-proportional slow down.
|
||||||
|
void slow_down_to_feedrate(float min_feedrate)
|
||||||
|
{
|
||||||
|
assert(this->slow_down_min_speed < min_feedrate + EPSILON);
|
||||||
|
for (size_t i = 0; i < n_lines_adjustable; ++i) {
|
||||||
|
CoolingLine &line = lines[i];
|
||||||
|
if (line.feedrate > min_feedrate) {
|
||||||
|
line.time *= std::max(1.f, line.feedrate / min_feedrate);
|
||||||
|
line.feedrate = min_feedrate;
|
||||||
|
line.slowdown = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extruder, for which the G-code will be adjusted.
|
||||||
|
unsigned int extruder_id = 0;
|
||||||
|
// Is the cooling slow down logic enabled for this extruder's material?
|
||||||
|
bool cooling_slow_down_enabled = false;
|
||||||
|
// Slow down the print down to slow_down_min_speed if the total layer time is below slow_down_layer_time.
|
||||||
|
float slow_down_layer_time = 0.f;
|
||||||
|
// Minimum print speed allowed for this extruder.
|
||||||
|
float slow_down_min_speed = 0.f;
|
||||||
|
|
||||||
|
// Parsed lines.
|
||||||
|
std::vector<CoolingLine> lines;
|
||||||
|
// The following two values are set by sort_lines_by_decreasing_feedrate():
|
||||||
|
// Number of adjustable lines, at the start of lines.
|
||||||
|
size_t n_lines_adjustable = 0;
|
||||||
|
// Non-adjustable time of lines starting with n_lines_adjustable.
|
||||||
|
float time_non_adjustable = 0;
|
||||||
|
// Current total time for this extruder.
|
||||||
|
float time_total = 0;
|
||||||
|
// Maximum time for this extruder, when the maximum slow down is applied.
|
||||||
|
float time_maximum = 0;
|
||||||
|
|
||||||
|
// Temporaries for processing the slow down. Both thresholds go from 0 to n_lines_adjustable.
|
||||||
|
size_t idx_line_begin = 0;
|
||||||
|
size_t idx_line_end = 0;
|
||||||
|
};
|
||||||
// A standalone G-code filter, to control cooling of the print.
|
// A standalone G-code filter, to control cooling of the print.
|
||||||
// The G-code is processed per layer. Once a layer is collected, fan start / stop commands are edited
|
// The G-code is processed per layer. Once a layer is collected, fan start / stop commands are edited
|
||||||
// and the print is modified to stretch over a minimum layer time.
|
// and the print is modified to stretch over a minimum layer time.
|
||||||
@ -25,16 +227,29 @@ public:
|
|||||||
CoolingBuffer(GCode &gcodegen);
|
CoolingBuffer(GCode &gcodegen);
|
||||||
void reset(const Vec3d &position);
|
void reset(const Vec3d &position);
|
||||||
void set_current_extruder(unsigned int extruder_id) { m_current_extruder = extruder_id; }
|
void set_current_extruder(unsigned int extruder_id) { m_current_extruder = extruder_id; }
|
||||||
std::string process_layer(std::string &&gcode, size_t layer_id, bool flush);
|
std::string process_layer(std::string && gcode,
|
||||||
|
const size_t layer_id,
|
||||||
|
SmoothCalculator & smooth_calculator,
|
||||||
|
std::vector<PerExtruderAdjustments> &per_extruder_adjustments,
|
||||||
|
const std::vector<int> & object_label,
|
||||||
|
const bool flush,
|
||||||
|
const bool spiral_vase);
|
||||||
|
|
||||||
|
//BBS:
|
||||||
|
void apply_smooth_speed(SmoothCalculator &smooth_calculator, std::vector<PerExtruderAdjustments> &extruder_adjustments, const int layer_id);
|
||||||
|
std::string apply_smooth_layer_time(std::string &&gcode, SmoothCalculator &smooth_calculator, std::vector<PerExtruderAdjustments> &extruder_adjustments, const int layer_id, const bool smooth_layer_time);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CoolingBuffer& operator=(const CoolingBuffer&) = delete;
|
CoolingBuffer& operator=(const CoolingBuffer&) = delete;
|
||||||
std::vector<PerExtruderAdjustments> parse_layer_gcode(const std::string &gcode, std::vector<float> ¤t_pos) const;
|
std::vector<PerExtruderAdjustments> parse_layer_gcode(const std::string & gcode,
|
||||||
|
std::vector<float> & current_pos,
|
||||||
|
const std::vector<int> & object_label,
|
||||||
|
bool spiral_vase,
|
||||||
|
bool join_z_smooth) const;
|
||||||
float calculate_layer_slowdown(std::vector<PerExtruderAdjustments> &per_extruder_adjustments);
|
float calculate_layer_slowdown(std::vector<PerExtruderAdjustments> &per_extruder_adjustments);
|
||||||
// Apply slow down over G-code lines stored in per_extruder_adjustments, enable fan if needed.
|
// Apply slow down over G-code lines stored in per_extruder_adjustments, enable fan if needed.
|
||||||
// Returns the adjusted G-code.
|
// Returns the adjusted G-code.
|
||||||
std::string apply_layer_cooldown(const std::string &gcode, size_t layer_id, float layer_time, std::vector<PerExtruderAdjustments> &per_extruder_adjustments);
|
std::string apply_layer_cooldown(const std::string &gcode, size_t layer_id, float layer_time, std::vector<PerExtruderAdjustments> &per_extruder_adjustments);
|
||||||
|
|
||||||
// G-code snippet cached for the support layers preceding an object layer.
|
// G-code snippet cached for the support layers preceding an object layer.
|
||||||
std::string m_gcode;
|
std::string m_gcode;
|
||||||
// Internal data.
|
// Internal data.
|
||||||
|
219
src/libslic3r/GCode/Smoothing.hpp
Normal file
219
src/libslic3r/GCode/Smoothing.hpp
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
#ifndef slic3r_Smoothing_hpp_
|
||||||
|
#define slic3r_Smoothing_hpp_
|
||||||
|
#include "../libslic3r.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
|
||||||
|
static const int guassian_window_size = 11;
|
||||||
|
static const int guassian_r = 2;
|
||||||
|
static const int guassian_stop_threshold = 5;
|
||||||
|
static const float guassian_layer_time_stop_threshold = 3.0;
|
||||||
|
// if the layer time longer than this threshold, ignore it
|
||||||
|
static const float layer_time_ignore_threshold = 30.0;
|
||||||
|
static const int max_steps_count = 1000;
|
||||||
|
|
||||||
|
struct CoolingNode
|
||||||
|
{
|
||||||
|
// extruder pos, line pos;
|
||||||
|
std::vector<std::pair<int, int>> outwall_line;
|
||||||
|
std::vector<std::pair<int, int>> innerwall_line;
|
||||||
|
float max_feedrate = 0;
|
||||||
|
float filter_feedrate = 0;
|
||||||
|
double rate = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OutwallCollection
|
||||||
|
{
|
||||||
|
int object_id;
|
||||||
|
std::map<int, CoolingNode> cooling_nodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SmoothCalculator
|
||||||
|
{
|
||||||
|
std::vector<double> guassian_filter;
|
||||||
|
double filter_sum = .0f;
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::vector<std::vector<OutwallCollection>> layers_wall_collection;
|
||||||
|
std::vector<float> layers_cooling_time;
|
||||||
|
std::vector<std::map<int, std::pair<int, int>>> objects_node_range;
|
||||||
|
|
||||||
|
SmoothCalculator(const int print_size, const int objects_size)
|
||||||
|
{
|
||||||
|
guassian_filter_generator();
|
||||||
|
objects_node_range.resize(objects_size);
|
||||||
|
layers_wall_collection.resize(print_size);
|
||||||
|
layers_cooling_time.resize(print_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// guassian filter
|
||||||
|
double guassian_function(double x, double r) {
|
||||||
|
return exp(-x * x / (2 * r * r)) / (r * sqrt(2 * PI));
|
||||||
|
}
|
||||||
|
|
||||||
|
void guassian_filter_generator() {
|
||||||
|
double r = guassian_r;
|
||||||
|
int half_win_size = guassian_window_size / 2;
|
||||||
|
for (int start = -half_win_size; start <= half_win_size; ++start) {
|
||||||
|
double y = guassian_function(start, r);
|
||||||
|
filter_sum += y;
|
||||||
|
guassian_filter.push_back(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_wall_collection(const int layer_id, const std::vector<int> &object_label)
|
||||||
|
{
|
||||||
|
for (size_t object_idx = 0; object_idx < object_label.size(); ++object_idx) {
|
||||||
|
OutwallCollection object_level;
|
||||||
|
object_level.object_id = object_label[object_idx];
|
||||||
|
layers_wall_collection[layer_id].push_back(object_level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_object_node_range() {
|
||||||
|
for (size_t object_id = 0; object_id < objects_node_range.size(); ++object_id) {
|
||||||
|
|
||||||
|
for (size_t layer_id = 1; layer_id < layers_wall_collection.size(); ++layer_id) {
|
||||||
|
const OutwallCollection &each_object = layers_wall_collection[layer_id][object_id];
|
||||||
|
auto it = each_object.cooling_nodes.begin();
|
||||||
|
while (it != each_object.cooling_nodes.end()) {
|
||||||
|
if (objects_node_range[object_id].count(it->first) == 0) {
|
||||||
|
objects_node_range[object_id].emplace(it->first, std::pair<int, int>(layer_id, layer_id));
|
||||||
|
} else {
|
||||||
|
objects_node_range[object_id][it->first].second = layer_id;
|
||||||
|
}
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter the data
|
||||||
|
void layer_speed_filter(const int object_id, const int node_id)
|
||||||
|
{
|
||||||
|
int start_pos = guassian_filter.size() / 2;
|
||||||
|
// first layer don't need to be smoothed
|
||||||
|
int layer_id = objects_node_range[object_id][node_id].first;
|
||||||
|
int layer_end = objects_node_range[object_id][node_id].second;
|
||||||
|
|
||||||
|
for (; layer_id <= layer_end; ++layer_id) {
|
||||||
|
if (layers_wall_collection[layer_id][object_id].cooling_nodes.count(node_id) == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
CoolingNode &node = layers_wall_collection[layer_id][object_id].cooling_nodes[node_id];
|
||||||
|
|
||||||
|
if (node.outwall_line.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
double conv_sum = 0;
|
||||||
|
for (int filter_pos_idx = 0; filter_pos_idx < guassian_filter.size(); ++filter_pos_idx) {
|
||||||
|
int remap_data_pos = layer_id - start_pos + filter_pos_idx;
|
||||||
|
|
||||||
|
if (remap_data_pos < 1)
|
||||||
|
remap_data_pos = 1;
|
||||||
|
else if (remap_data_pos > layers_wall_collection.size() - 1)
|
||||||
|
remap_data_pos = layers_wall_collection.size() - 1;
|
||||||
|
|
||||||
|
// some node may not start at layer 1
|
||||||
|
double remap_data = node.filter_feedrate;
|
||||||
|
if (!layers_wall_collection[remap_data_pos][object_id].cooling_nodes[node_id].outwall_line.empty())
|
||||||
|
remap_data = layers_wall_collection[remap_data_pos][object_id].cooling_nodes[node_id].filter_feedrate;
|
||||||
|
|
||||||
|
conv_sum += guassian_filter[filter_pos_idx] * remap_data;
|
||||||
|
}
|
||||||
|
double filter_res = conv_sum / filter_sum;
|
||||||
|
if (filter_res < node.filter_feedrate) node.filter_feedrate = filter_res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool speed_filter_continue(const int object_id, const int node_id)
|
||||||
|
{
|
||||||
|
int layer_id = objects_node_range[object_id][node_id].first;
|
||||||
|
int layer_end = objects_node_range[object_id][node_id].second;
|
||||||
|
|
||||||
|
for (; layer_id < layer_end; ++layer_id) {
|
||||||
|
if (std::abs(layers_wall_collection[layer_id][object_id].cooling_nodes[node_id].outwall_line.empty()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (std::abs(layers_wall_collection[layer_id][object_id].cooling_nodes[node_id].filter_feedrate -
|
||||||
|
layers_wall_collection[layer_id + 1][object_id].cooling_nodes[node_id].filter_feedrate) >
|
||||||
|
guassian_stop_threshold)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void smooth_layer_speed()
|
||||||
|
{
|
||||||
|
init_object_node_range();
|
||||||
|
|
||||||
|
for (size_t obj_id = 0; obj_id < objects_node_range.size(); ++obj_id) {
|
||||||
|
auto it = objects_node_range[obj_id].begin();
|
||||||
|
while (it != objects_node_range[obj_id].end()) {
|
||||||
|
int step_count = 0;
|
||||||
|
while (step_count < max_steps_count && speed_filter_continue(obj_id, it->first)) {
|
||||||
|
step_count++;
|
||||||
|
layer_speed_filter(obj_id, it->first);
|
||||||
|
}
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// filter the data
|
||||||
|
void filter_layer_time()
|
||||||
|
{
|
||||||
|
int start_pos = guassian_filter.size() / 2;
|
||||||
|
// first layer don't need to be smoothed
|
||||||
|
for (int layer_id = 1; layer_id < layers_cooling_time.size(); ++layer_id) {
|
||||||
|
if (layers_cooling_time[layer_id] > layer_time_ignore_threshold)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
double conv_sum = 0;
|
||||||
|
for (int filter_pos_idx = 0; filter_pos_idx < guassian_filter.size(); ++filter_pos_idx) {
|
||||||
|
int remap_data_pos = layer_id - start_pos + filter_pos_idx;
|
||||||
|
|
||||||
|
if (remap_data_pos < 1)
|
||||||
|
remap_data_pos = 1;
|
||||||
|
else if (remap_data_pos > layers_cooling_time.size() - 1)
|
||||||
|
remap_data_pos = layers_cooling_time.size() - 1;
|
||||||
|
|
||||||
|
// if the layer time big enough, surface defact will disappear
|
||||||
|
double data_temp = layers_cooling_time[remap_data_pos] > layer_time_ignore_threshold ? layer_time_ignore_threshold : layers_cooling_time[remap_data_pos];
|
||||||
|
|
||||||
|
conv_sum += guassian_filter[filter_pos_idx] * data_temp;
|
||||||
|
}
|
||||||
|
double filter_res = conv_sum / filter_sum;
|
||||||
|
filter_res = filter_res > layer_time_ignore_threshold ? layer_time_ignore_threshold : filter_res;
|
||||||
|
if (filter_res > layers_cooling_time[layer_id])
|
||||||
|
layers_cooling_time[layer_id] = filter_res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool layer_time_filter_continue()
|
||||||
|
{
|
||||||
|
for (int layer_id = 1; layer_id < layers_cooling_time.size() - 1; ++layer_id) {
|
||||||
|
double layer_time = layers_cooling_time[layer_id] > layer_time_ignore_threshold ? layer_time_ignore_threshold : layers_cooling_time[layer_id];
|
||||||
|
double layer_time_cmp = layers_cooling_time[layer_id + 1] > layer_time_ignore_threshold ? layer_time_ignore_threshold : layers_cooling_time[layer_id + 1];
|
||||||
|
|
||||||
|
if (std::abs(layer_time - layer_time_cmp) > guassian_layer_time_stop_threshold)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void smooth_layer_time()
|
||||||
|
{
|
||||||
|
int step_count = 0;
|
||||||
|
while (step_count < max_steps_count && layer_time_filter_continue()) {
|
||||||
|
step_count++;
|
||||||
|
filter_layer_time();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
#endif
|
@ -5,8 +5,9 @@
|
|||||||
#include "ShortestPath.hpp"
|
#include "ShortestPath.hpp"
|
||||||
#include "SVG.hpp"
|
#include "SVG.hpp"
|
||||||
#include "BoundingBox.hpp"
|
#include "BoundingBox.hpp"
|
||||||
|
#include "libslic3r/AABBTreeLines.hpp"
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
|
static const int Continuitious_length = scale_(0.01);
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -198,7 +199,7 @@ void Layer::make_perimeters()
|
|||||||
|
|
||||||
if (layerms.size() == 1) { // optimization
|
if (layerms.size() == 1) { // optimization
|
||||||
(*layerm)->fill_surfaces.surfaces.clear();
|
(*layerm)->fill_surfaces.surfaces.clear();
|
||||||
(*layerm)->make_perimeters((*layerm)->slices, &(*layerm)->fill_surfaces, &(*layerm)->fill_no_overlap_expolygons);
|
(*layerm)->make_perimeters((*layerm)->slices, &(*layerm)->fill_surfaces, &(*layerm)->fill_no_overlap_expolygons, region_id, this->loop_nodes);
|
||||||
(*layerm)->fill_expolygons = to_expolygons((*layerm)->fill_surfaces.surfaces);
|
(*layerm)->fill_expolygons = to_expolygons((*layerm)->fill_surfaces.surfaces);
|
||||||
} else {
|
} else {
|
||||||
SurfaceCollection new_slices;
|
SurfaceCollection new_slices;
|
||||||
@ -222,7 +223,7 @@ void Layer::make_perimeters()
|
|||||||
SurfaceCollection fill_surfaces;
|
SurfaceCollection fill_surfaces;
|
||||||
//BBS
|
//BBS
|
||||||
ExPolygons fill_no_overlap;
|
ExPolygons fill_no_overlap;
|
||||||
layerm_config->make_perimeters(new_slices, &fill_surfaces, &fill_no_overlap);
|
layerm_config->make_perimeters(new_slices, &fill_surfaces, &fill_no_overlap, region_id, this->loop_nodes);
|
||||||
|
|
||||||
// assign fill_surfaces to each layer
|
// assign fill_surfaces to each layer
|
||||||
if (!fill_surfaces.surfaces.empty()) {
|
if (!fill_surfaces.surfaces.empty()) {
|
||||||
@ -237,9 +238,127 @@ void Layer::make_perimeters()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << " - Done";
|
BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << " - Done";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//BBS: use aabbtree to get distance
|
||||||
|
class ContinuitiousDistancer
|
||||||
|
{
|
||||||
|
std::vector<Linef> lines;
|
||||||
|
AABBTreeIndirect::Tree<2, double> tree;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ContinuitiousDistancer(const std::pair<ExPolygon,bool> node_expolygon)
|
||||||
|
{
|
||||||
|
if (node_expolygon.second) {
|
||||||
|
for (const auto &line : node_expolygon.first.lines())
|
||||||
|
lines.emplace_back(line.a.cast<double>(), line.b.cast<double>());
|
||||||
|
} else {
|
||||||
|
Polyline pl;
|
||||||
|
pl.append(node_expolygon.first.contour.points);
|
||||||
|
for (const auto &line : pl.lines())
|
||||||
|
lines.emplace_back(line.a.cast<double>(), line.b.cast<double>());
|
||||||
|
}
|
||||||
|
tree = AABBTreeLines::build_aabb_tree_over_indexed_lines(lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
float distance_from_perimeter(const Vec2f &point) const
|
||||||
|
{
|
||||||
|
Vec2d p = point.cast<double>();
|
||||||
|
size_t hit_idx_out{};
|
||||||
|
Vec2d hit_point_out = Vec2d::Zero();
|
||||||
|
auto distance = AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, p, hit_idx_out, hit_point_out);
|
||||||
|
if (distance < 0) {
|
||||||
|
return std::numeric_limits<float>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
distance = sqrt(distance);
|
||||||
|
const Linef &line = lines[hit_idx_out];
|
||||||
|
Vec2d v1 = line.b - line.a;
|
||||||
|
Vec2d v2 = p - line.a;
|
||||||
|
if ((v1.x() * v2.y()) - (v1.y() * v2.x()) > 0.0) { distance *= -1; }
|
||||||
|
return distance;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void Layer::calculate_perimeter_continuity(std::vector<LoopNode> &prev_nodes) {
|
||||||
|
for (size_t node_pos = 0; node_pos < loop_nodes.size(); ++node_pos) {
|
||||||
|
LoopNode &node=loop_nodes[node_pos];
|
||||||
|
double width = scale_(m_regions[node.region_id]->region().config().outer_wall_line_width) * 1.2;
|
||||||
|
ContinuitiousDistancer node_distancer(node.exp);
|
||||||
|
for (size_t prev_pos = 0; prev_pos < prev_nodes.size(); ++prev_pos) {
|
||||||
|
LoopNode &prev_node = prev_nodes[prev_pos];
|
||||||
|
|
||||||
|
// no overlap or has diff speed
|
||||||
|
if (!node.bbox.overlap(prev_node.bbox))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
//calculate dist, checkout the continuity
|
||||||
|
Polyline continuitious_pl;
|
||||||
|
//check start pt
|
||||||
|
size_t start = 0;
|
||||||
|
bool conntiouitious_flag = false;
|
||||||
|
size_t end = prev_node.exp.first.contour.points.size() - 1;
|
||||||
|
|
||||||
|
//if the countor is loop
|
||||||
|
if (prev_node.exp.second) {
|
||||||
|
//check first point
|
||||||
|
Point pt = prev_node.exp.first.contour.points.front();
|
||||||
|
float dist = node_distancer.distance_from_perimeter(pt.cast<float>());
|
||||||
|
if (dist < width && dist > -width)
|
||||||
|
continuitious_pl.append_before(pt);
|
||||||
|
|
||||||
|
for (; end >= 0; --end) {
|
||||||
|
if (continuitious_pl.length() >= Continuitious_length) {
|
||||||
|
node.lower_node_id.push_back(prev_node.node_id);
|
||||||
|
prev_node.upper_node_id.push_back(node.node_id);
|
||||||
|
conntiouitious_flag = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point pt = prev_node.exp.first.contour.points[end];
|
||||||
|
float dist = node_distancer.distance_from_perimeter(pt.cast<float>());
|
||||||
|
|
||||||
|
if (dist < width && dist > -width)
|
||||||
|
continuitious_pl.append_before(pt);
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conntiouitious_flag) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; start < end; ++start) {
|
||||||
|
Point pt = prev_node.exp.first.contour.points[start];
|
||||||
|
float dist = node_distancer.distance_from_perimeter(pt.cast<float>());
|
||||||
|
|
||||||
|
if (dist < width && dist > -width) {
|
||||||
|
continuitious_pl.append(pt);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (continuitious_pl.empty() || continuitious_pl.length() < Continuitious_length) {
|
||||||
|
continuitious_pl.clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
node.lower_node_id.push_back(prev_node.node_id);
|
||||||
|
prev_node.upper_node_id.push_back(node.node_id);
|
||||||
|
continuitious_pl.clear();
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (continuitious_pl.length() >= Continuitious_length) {
|
||||||
|
node.lower_node_id.push_back(prev_node.node_id);
|
||||||
|
prev_node.upper_node_id.push_back(node.node_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
void Layer::export_region_slices_to_svg(const char *path) const
|
void Layer::export_region_slices_to_svg(const char *path) const
|
||||||
{
|
{
|
||||||
BoundingBox bbox;
|
BoundingBox bbox;
|
||||||
|
@ -80,7 +80,7 @@ public:
|
|||||||
void slices_to_fill_surfaces_clipped();
|
void slices_to_fill_surfaces_clipped();
|
||||||
void prepare_fill_surfaces();
|
void prepare_fill_surfaces();
|
||||||
//BBS
|
//BBS
|
||||||
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces, ExPolygons* fill_no_overlap);
|
void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces, ExPolygons* fill_no_overlap, int region_id, std::vector<LoopNode> &loop_nodes);
|
||||||
void process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered);
|
void process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered);
|
||||||
double infill_area_threshold() const;
|
double infill_area_threshold() const;
|
||||||
// Trim surfaces by trimming polygons. Used by the elephant foot compensation at the 1st layer.
|
// Trim surfaces by trimming polygons. Used by the elephant foot compensation at the 1st layer.
|
||||||
@ -155,6 +155,7 @@ public:
|
|||||||
// BBS
|
// BBS
|
||||||
ExPolygons loverhangs;
|
ExPolygons loverhangs;
|
||||||
BoundingBox loverhangs_bbox;
|
BoundingBox loverhangs_bbox;
|
||||||
|
std::vector<LoopNode> loop_nodes;
|
||||||
size_t region_count() const { return m_regions.size(); }
|
size_t region_count() const { return m_regions.size(); }
|
||||||
const LayerRegion* get_region(int idx) const { return m_regions[idx]; }
|
const LayerRegion* get_region(int idx) const { return m_regions[idx]; }
|
||||||
LayerRegion* get_region(int idx) { return m_regions[idx]; }
|
LayerRegion* get_region(int idx) { return m_regions[idx]; }
|
||||||
@ -180,6 +181,9 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
void make_perimeters();
|
void make_perimeters();
|
||||||
|
//BBS
|
||||||
|
void calculate_perimeter_continuity(std::vector<LoopNode> &prev_nodes);
|
||||||
|
|
||||||
// Phony version of make_fills() without parameters for Perl integration only.
|
// Phony version of make_fills() without parameters for Perl integration only.
|
||||||
void make_fills() { this->make_fills(nullptr, nullptr); }
|
void make_fills() { this->make_fills(nullptr, nullptr); }
|
||||||
void make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator = nullptr);
|
void make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive::Octree* support_fill_octree, FillLightning::Generator* lightning_generator = nullptr);
|
||||||
|
@ -64,7 +64,7 @@ void LayerRegion::slices_to_fill_surfaces_clipped()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces, ExPolygons* fill_no_overlap)
|
void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollection *fill_surfaces, ExPolygons *fill_no_overlap, int region_id, std::vector<LoopNode> &loop_nodes)
|
||||||
{
|
{
|
||||||
this->perimeters.clear();
|
this->perimeters.clear();
|
||||||
this->thin_fills.clear();
|
this->thin_fills.clear();
|
||||||
@ -93,7 +93,9 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec
|
|||||||
&this->thin_fills,
|
&this->thin_fills,
|
||||||
fill_surfaces,
|
fill_surfaces,
|
||||||
//BBS
|
//BBS
|
||||||
fill_no_overlap
|
fill_no_overlap,
|
||||||
|
region_id,
|
||||||
|
&loop_nodes
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this->layer()->lower_layer != nullptr)
|
if (this->layer()->lower_layer != nullptr)
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "Clipper2Utils.hpp"
|
#include "Clipper2Utils.hpp"
|
||||||
#include "Arachne/WallToolPaths.hpp"
|
#include "Arachne/WallToolPaths.hpp"
|
||||||
#include "Line.hpp"
|
#include "Line.hpp"
|
||||||
|
#include "Layer.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <random>
|
#include <random>
|
||||||
@ -1162,6 +1163,7 @@ void PerimeterGenerator::process_classic()
|
|||||||
ExPolygons gaps;
|
ExPolygons gaps;
|
||||||
ExPolygons top_fills;
|
ExPolygons top_fills;
|
||||||
ExPolygons fill_clip;
|
ExPolygons fill_clip;
|
||||||
|
std::vector<std::pair<ExPolygon, bool>> outwall;
|
||||||
if (loop_number >= 0) {
|
if (loop_number >= 0) {
|
||||||
// In case no perimeters are to be generated, loop_number will equal to -1.
|
// In case no perimeters are to be generated, loop_number will equal to -1.
|
||||||
std::vector<PerimeterGeneratorLoops> contours(loop_number+1); // depth => loops
|
std::vector<PerimeterGeneratorLoops> contours(loop_number+1); // depth => loops
|
||||||
@ -1287,6 +1289,23 @@ void PerimeterGenerator::process_classic()
|
|||||||
|
|
||||||
//BBS: save perimeter loop which use smaller width
|
//BBS: save perimeter loop which use smaller width
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
|
//store outer wall
|
||||||
|
|
||||||
|
//not loop
|
||||||
|
for (const ThickPolyline &polyline : thin_walls) {
|
||||||
|
ExPolygon contour_coll;
|
||||||
|
contour_coll.contour.append(polyline.points);
|
||||||
|
outwall.emplace_back(contour_coll, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
//loop
|
||||||
|
for (const ExPolygon &expolygon : offsets_with_smaller_width) {
|
||||||
|
outwall.emplace_back(expolygon, true);
|
||||||
|
}
|
||||||
|
for (const ExPolygon &expolygon : offsets) {
|
||||||
|
outwall.emplace_back(expolygon, true);
|
||||||
|
}
|
||||||
|
|
||||||
for (const ExPolygon& expolygon : offsets_with_smaller_width) {
|
for (const ExPolygon& expolygon : offsets_with_smaller_width) {
|
||||||
contours[i].emplace_back(PerimeterGeneratorLoop(expolygon.contour, i, true, fuzzify_contours, true));
|
contours[i].emplace_back(PerimeterGeneratorLoop(expolygon.contour, i, true, fuzzify_contours, true));
|
||||||
if (!expolygon.holes.empty()) {
|
if (!expolygon.holes.empty()) {
|
||||||
@ -1463,6 +1482,41 @@ void PerimeterGenerator::process_classic()
|
|||||||
}
|
}
|
||||||
entities.entities = std::move( entities_reorder);
|
entities.entities = std::move( entities_reorder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//BBS: add node for loops
|
||||||
|
if (!outwall.empty() && this->layer_id > 0) {
|
||||||
|
entities.loop_node_range.first = this->loop_nodes->size();
|
||||||
|
if (outwall.size() == 1) {
|
||||||
|
LoopNode node;
|
||||||
|
node.node_id = this->loop_nodes->size();
|
||||||
|
node.region_id = region_id;
|
||||||
|
node.perimeter_id = this->loops->entities.size();
|
||||||
|
node.exp = outwall.front();
|
||||||
|
node.bbox = get_extents(node.exp.first);
|
||||||
|
this->loop_nodes->push_back(node);
|
||||||
|
} else {
|
||||||
|
int entity_idx = 0;
|
||||||
|
for (std::pair<ExPolygon, bool> &exp : outwall) {
|
||||||
|
LoopNode node;
|
||||||
|
node.node_id = this->loop_nodes->size();
|
||||||
|
node.region_id = region_id;
|
||||||
|
node.perimeter_id = this->loops->entities.size();
|
||||||
|
|
||||||
|
for (; entity_idx < entities.entities.size(); ++entity_idx) {
|
||||||
|
if (exp.first.contains(entities.entities[entity_idx]->first_point())) {
|
||||||
|
node.loop_id=entity_idx;
|
||||||
|
node.exp=exp;
|
||||||
|
node.bbox = get_extents(node.exp.first);
|
||||||
|
this->loop_nodes->push_back(node);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entities.loop_node_range.second = this->loop_nodes->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// append perimeters for this slice as a collection
|
// append perimeters for this slice as a collection
|
||||||
if (! entities.empty())
|
if (! entities.empty())
|
||||||
this->loops->append(entities);
|
this->loops->append(entities);
|
||||||
|
@ -40,7 +40,8 @@ public:
|
|||||||
std::pair<double, double> m_lower_overhang_dist_boundary;
|
std::pair<double, double> m_lower_overhang_dist_boundary;
|
||||||
std::pair<double, double> m_external_overhang_dist_boundary;
|
std::pair<double, double> m_external_overhang_dist_boundary;
|
||||||
std::pair<double, double> m_smaller_external_overhang_dist_boundary;
|
std::pair<double, double> m_smaller_external_overhang_dist_boundary;
|
||||||
|
std::vector<LoopNode> *loop_nodes;
|
||||||
|
int region_id;
|
||||||
|
|
||||||
PerimeterGenerator(
|
PerimeterGenerator(
|
||||||
// Input:
|
// Input:
|
||||||
@ -59,14 +60,20 @@ public:
|
|||||||
// Infills without the gap fills
|
// Infills without the gap fills
|
||||||
SurfaceCollection* fill_surfaces,
|
SurfaceCollection* fill_surfaces,
|
||||||
//BBS
|
//BBS
|
||||||
ExPolygons* fill_no_overlap)
|
ExPolygons* fill_no_overlap,
|
||||||
|
int region_id,
|
||||||
|
std::vector<LoopNode> *loop_nodes)
|
||||||
: slices(slices), upper_slices(nullptr), lower_slices(nullptr), layer_height(layer_height),
|
: slices(slices), upper_slices(nullptr), lower_slices(nullptr), layer_height(layer_height),
|
||||||
layer_id(-1), perimeter_flow(flow), ext_perimeter_flow(flow),
|
layer_id(-1), perimeter_flow(flow), ext_perimeter_flow(flow),
|
||||||
overhang_flow(flow), solid_infill_flow(flow),
|
overhang_flow(flow), solid_infill_flow(flow),
|
||||||
config(config), object_config(object_config), print_config(print_config),
|
config(config), object_config(object_config), print_config(print_config),
|
||||||
m_spiral_vase(spiral_mode),
|
m_spiral_vase(spiral_mode),
|
||||||
m_scaled_resolution(scaled<double>(print_config->resolution.value > EPSILON ? print_config->resolution.value : EPSILON)),
|
m_scaled_resolution(scaled<double>(print_config->resolution.value > EPSILON ? print_config->resolution.value : EPSILON)), loops(loops),
|
||||||
loops(loops), gap_fill(gap_fill), fill_surfaces(fill_surfaces), fill_no_overlap(fill_no_overlap),
|
gap_fill(gap_fill),
|
||||||
|
fill_surfaces(fill_surfaces),
|
||||||
|
fill_no_overlap(fill_no_overlap),
|
||||||
|
region_id(region_id),
|
||||||
|
loop_nodes(loop_nodes),
|
||||||
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)
|
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)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -842,7 +842,8 @@ static std::vector<std::string> s_Preset_print_options {
|
|||||||
"seam_gap", "wipe_speed", "top_solid_infill_flow_ratio", "initial_layer_flow_ratio",
|
"seam_gap", "wipe_speed", "top_solid_infill_flow_ratio", "initial_layer_flow_ratio",
|
||||||
"default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk", "top_surface_jerk", "initial_layer_jerk", "travel_jerk",
|
"default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk", "top_surface_jerk", "initial_layer_jerk", "travel_jerk",
|
||||||
"filter_out_gap_fill", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth",
|
"filter_out_gap_fill", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth",
|
||||||
"small_perimeter_speed", "small_perimeter_threshold",
|
"small_perimeter_speed", "small_perimeter_threshold", "z_direction_outwall_speed_continuous",
|
||||||
|
"layer_time_smoothing",
|
||||||
// calib
|
// calib
|
||||||
"print_flow_ratio",
|
"print_flow_ratio",
|
||||||
//Orca
|
//Orca
|
||||||
|
@ -505,7 +505,7 @@ private:
|
|||||||
|
|
||||||
// BBS
|
// BBS
|
||||||
SupportNecessaryType is_support_necessary();
|
SupportNecessaryType is_support_necessary();
|
||||||
|
void merge_layer_node(const size_t layer_id, int &max_merged_id, std::map<int, std::vector<std::pair<int, int>>> &node_record);
|
||||||
// XYZ in scaled coordinates
|
// XYZ in scaled coordinates
|
||||||
Vec3crd m_size;
|
Vec3crd m_size;
|
||||||
double m_max_z;
|
double m_max_z;
|
||||||
|
@ -571,6 +571,20 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionBool(false));
|
def->set_default_value(new ConfigOptionBool(false));
|
||||||
|
|
||||||
|
def = this->add("z_direction_outwall_speed_continuous", coBool);
|
||||||
|
def->label = L("Z direction outwall speed continuous");
|
||||||
|
def->category = L("Quality");
|
||||||
|
def->tooltip = L("Smoothing outwall speed in z direction to get better surface quality. Print time will increases.");
|
||||||
|
def->mode = comAdvanced;
|
||||||
|
def->set_default_value(new ConfigOptionBool(true));
|
||||||
|
|
||||||
|
def = this->add("layer_time_smoothing", coBool);
|
||||||
|
def->label = L("Layer time smoothing");
|
||||||
|
def->category = L("Quality");
|
||||||
|
def->tooltip = L("Smoothing layer time in z direction to get better surface quality. Print time will increases.");
|
||||||
|
def->mode = comAdvanced;
|
||||||
|
def->set_default_value(new ConfigOptionBool(false));
|
||||||
|
|
||||||
def = this->add("max_travel_detour_distance", coFloatOrPercent);
|
def = this->add("max_travel_detour_distance", coFloatOrPercent);
|
||||||
def->label = L("Avoid crossing wall - Max detour length");
|
def->label = L("Avoid crossing wall - Max detour length");
|
||||||
def->category = L("Quality");
|
def->category = L("Quality");
|
||||||
|
@ -981,6 +981,8 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
|||||||
//BBS
|
//BBS
|
||||||
((ConfigOptionInts, additional_cooling_fan_speed))
|
((ConfigOptionInts, additional_cooling_fan_speed))
|
||||||
((ConfigOptionBool, reduce_crossing_wall))
|
((ConfigOptionBool, reduce_crossing_wall))
|
||||||
|
((ConfigOptionBool, z_direction_outwall_speed_continuous))
|
||||||
|
((ConfigOptionBool, layer_time_smoothing))
|
||||||
((ConfigOptionFloatOrPercent, max_travel_detour_distance))
|
((ConfigOptionFloatOrPercent, max_travel_detour_distance))
|
||||||
((ConfigOptionPoints, printable_area))
|
((ConfigOptionPoints, printable_area))
|
||||||
//BBS: add bed_exclude_area
|
//BBS: add bed_exclude_area
|
||||||
|
@ -129,6 +129,63 @@ std::vector<std::reference_wrapper<const PrintRegion>> PrintObject::all_regions(
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PrintObject::merge_layer_node(const size_t layer_id, int &max_merged_id, std::map<int, std::vector<std::pair<int, int>>> &node_record)
|
||||||
|
{
|
||||||
|
Layer *this_layer = m_layers[layer_id];
|
||||||
|
std::vector<LoopNode> &loop_nodes = this_layer->loop_nodes;
|
||||||
|
for (size_t idx = 0; idx < loop_nodes.size(); ++idx) {
|
||||||
|
//new cool node
|
||||||
|
if (loop_nodes[idx].lower_node_id.empty()) {
|
||||||
|
max_merged_id++;
|
||||||
|
loop_nodes[idx].merged_id = max_merged_id;
|
||||||
|
std::vector<std::pair<int, int>> node_pos;
|
||||||
|
node_pos.emplace_back(layer_id, idx);
|
||||||
|
node_record.emplace(max_merged_id, node_pos);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//it should finds key in map
|
||||||
|
if (loop_nodes[idx].lower_node_id.size() == 1) {
|
||||||
|
loop_nodes[idx].merged_id = m_layers[layer_id - 1]->loop_nodes[loop_nodes[idx].lower_node_id.front()].merged_id;
|
||||||
|
node_record[loop_nodes[idx].merged_id].emplace_back(layer_id, idx);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//min index
|
||||||
|
int min_merged_id = -1;
|
||||||
|
std::vector<int> appear_id;
|
||||||
|
for (size_t lower_idx = 0; lower_idx < loop_nodes[idx].lower_node_id.size(); ++lower_idx) {
|
||||||
|
int id = m_layers[layer_id - 1]->loop_nodes[loop_nodes[idx].lower_node_id[lower_idx]].merged_id;
|
||||||
|
if (min_merged_id == -1 || min_merged_id > id)
|
||||||
|
min_merged_id = id;
|
||||||
|
appear_id.push_back(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
loop_nodes[idx].merged_id = min_merged_id;
|
||||||
|
node_record[min_merged_id].emplace_back(layer_id, idx);
|
||||||
|
|
||||||
|
//update other node merged id
|
||||||
|
for (size_t appear_node_idx = 0; appear_node_idx < appear_id.size(); ++appear_node_idx) {
|
||||||
|
if (appear_id[appear_node_idx] == min_merged_id)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto it = node_record.find(appear_id[appear_node_idx]);
|
||||||
|
std::vector<std::pair<int, int>> &appear_node_pos = it->second;
|
||||||
|
|
||||||
|
for (size_t node_idx = 0; node_idx < appear_node_pos.size(); ++node_idx) {
|
||||||
|
int node_layer = appear_node_pos[node_idx].first;
|
||||||
|
int node_pos = appear_node_pos[node_idx].second;
|
||||||
|
|
||||||
|
LoopNode &node = m_layers[node_layer]->loop_nodes[node_pos];
|
||||||
|
|
||||||
|
node.merged_id = min_merged_id;
|
||||||
|
node_record[min_merged_id].emplace_back(node_layer, node_pos);
|
||||||
|
}
|
||||||
|
node_record.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 1) Merges typed region slices into stInternal type.
|
// 1) Merges typed region slices into stInternal type.
|
||||||
// 2) Increases an "extra perimeters" counter at region slices where needed.
|
// 2) Increases an "extra perimeters" counter at region slices where needed.
|
||||||
// 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal).
|
// 3) Generates perimeters, gap fills and fill regions (fill regions of type stInternal).
|
||||||
@ -236,6 +293,33 @@ void PrintObject::make_perimeters()
|
|||||||
m_print->throw_if_canceled();
|
m_print->throw_if_canceled();
|
||||||
BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end";
|
BOOST_LOG_TRIVIAL(debug) << "Generating perimeters in parallel - end";
|
||||||
|
|
||||||
|
// BBS: get continuity of nodes
|
||||||
|
if (this->config().wall_generator == PerimeterGeneratorType::Classic) {
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Calculating perimeters connection in parallel - start";
|
||||||
|
tbb::parallel_for(tbb::blocked_range<size_t>(0, m_layers.size()), [this](const tbb::blocked_range<size_t> &range) {
|
||||||
|
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
|
||||||
|
m_print->throw_if_canceled();
|
||||||
|
if (layer_idx > 1) {
|
||||||
|
Layer &prev_layer = *m_layers[layer_idx - 1];
|
||||||
|
m_layers[layer_idx]->calculate_perimeter_continuity(m_layers[layer_idx - 1]->loop_nodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
m_print->throw_if_canceled();
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Calculating perimeters connection in parallel - end";
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Calculating cooling nodes - start";
|
||||||
|
|
||||||
|
int max_merged_id = -1;
|
||||||
|
std::map<int,std::vector<std::pair<int, int>>> node_record;
|
||||||
|
for (size_t layer_idx = 1; layer_idx < m_layers.size(); ++layer_idx) {
|
||||||
|
m_print->throw_if_canceled();
|
||||||
|
merge_layer_node(layer_idx, max_merged_id, node_record);
|
||||||
|
}
|
||||||
|
m_print->throw_if_canceled();
|
||||||
|
BOOST_LOG_TRIVIAL(debug) << "Calculating cooling nodes - end";
|
||||||
|
}
|
||||||
this->set_done(posPerimeters);
|
this->set_done(posPerimeters);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -917,7 +1001,9 @@ bool PrintObject::invalidate_state_by_config_options(
|
|||||||
|| opt_key == "sparse_infill_speed"
|
|| opt_key == "sparse_infill_speed"
|
||||||
|| opt_key == "inner_wall_speed"
|
|| opt_key == "inner_wall_speed"
|
||||||
|| opt_key == "internal_solid_infill_speed"
|
|| opt_key == "internal_solid_infill_speed"
|
||||||
|| opt_key == "top_surface_speed") {
|
|| opt_key == "top_surface_speed"
|
||||||
|
|| opt_key == "z_direction_outwall_speed_continuous"
|
||||||
|
|| opt_key == "layer_time_smoothing") {
|
||||||
invalidated |= m_print->invalidate_step(psGCodeExport);
|
invalidated |= m_print->invalidate_step(psGCodeExport);
|
||||||
} else if (
|
} else if (
|
||||||
opt_key == "flush_into_infill"
|
opt_key == "flush_into_infill"
|
||||||
|
@ -720,10 +720,10 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
|||||||
toggle_line(el, has_fuzzy_skin);
|
toggle_line(el, has_fuzzy_skin);
|
||||||
|
|
||||||
bool have_arachne = config->opt_enum<PerimeterGeneratorType>("wall_generator") == PerimeterGeneratorType::Arachne;
|
bool have_arachne = config->opt_enum<PerimeterGeneratorType>("wall_generator") == PerimeterGeneratorType::Arachne;
|
||||||
for (auto el : { "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle",
|
for (auto el : { "wall_transition_length", "wall_transition_filter_deviation", "wall_transition_angle", "min_feature_size", "min_bead_width", "wall_distribution_count"})
|
||||||
"min_feature_size", "min_bead_width", "wall_distribution_count" })
|
|
||||||
toggle_line(el, have_arachne);
|
toggle_line(el, have_arachne);
|
||||||
toggle_field("detect_thin_wall", !have_arachne);
|
toggle_field("detect_thin_wall", !have_arachne);
|
||||||
|
toggle_field("z_direction_outwall_speed_continuous", !have_arachne);
|
||||||
|
|
||||||
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
|
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
|
||||||
// OrcaSlicer
|
// OrcaSlicer
|
||||||
|
@ -1956,6 +1956,8 @@ void TabPrint::build()
|
|||||||
optgroup->append_single_option_line("smooth_speed_discontinuity_area");
|
optgroup->append_single_option_line("smooth_speed_discontinuity_area");
|
||||||
optgroup->append_single_option_line("smooth_coefficient");
|
optgroup->append_single_option_line("smooth_coefficient");
|
||||||
optgroup->append_single_option_line("reduce_crossing_wall");
|
optgroup->append_single_option_line("reduce_crossing_wall");
|
||||||
|
optgroup->append_single_option_line("z_direction_outwall_speed_continuous");
|
||||||
|
optgroup->append_single_option_line("layer_time_smoothing");
|
||||||
optgroup->append_single_option_line("max_travel_detour_distance");
|
optgroup->append_single_option_line("max_travel_detour_distance");
|
||||||
|
|
||||||
page = add_options_page(L("Strength"), "empty");
|
page = add_options_page(L("Strength"), "empty");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user