mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-08 16:21:14 +00:00
Belt cooling: keep the fan off in the band inside the layer cooling pass
The band was a second pass over the finished layer that fought the fan commands the layer pass had already written (overhang, bridge and resume requests). The generator now marks where each segment enters and leaves the band and the layer pass treats the band as the strongest fan request, so there is one place that decides the fan.
This commit is contained in:
+10
-15
@@ -8507,25 +8507,20 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
// the speed fade tracks perpendicular distance from the plane on
|
||||
// belt printers; otherwise this falls back to the slicing layer id.
|
||||
const int _layer = this->effective_layer_index_for_point(path_point_mm);
|
||||
// Belt printers: tell the cooling buffer which band above the belt the next extrusion is
|
||||
// in (CoolingBuffer::apply_belt_band_fan consumes the tag). A tilted layer runs from the
|
||||
// belt to the top of the part, so this is decided per segment, not per path; a tag is
|
||||
// written once per layer and at every change, capped where the fan stops depending on it.
|
||||
const bool belt_band_tags = m_enable_cooling_markers && m_config.belt_printer.value;
|
||||
const int belt_band_cap = belt_band_tags
|
||||
? std::max(m_config.close_fan_the_first_x_layers.get_at(m_writer->filament()->id()),
|
||||
m_config.full_fan_speed_layer.get_at(m_writer->filament()->id())) + 1 : 0;
|
||||
auto tag_belt_band = [this, &gcode, belt_band_tags, belt_band_cap, z = path_point_mm.z()](coord_t x, coord_t y) {
|
||||
// Belt printers: a tilted layer runs from the belt to the top of the part, so the
|
||||
// "first layers" the fan stays off for are a band along the belt. Mark where the
|
||||
// extrusion enters and leaves it, per segment, for the cooling buffer.
|
||||
const bool belt_band_tags = m_enable_cooling_markers && m_config.belt_printer.value;
|
||||
const int belt_band_layers = belt_band_tags ? m_config.close_fan_the_first_x_layers.get_at(m_writer->filament()->id()) : 0;
|
||||
auto tag_belt_band = [this, &gcode, belt_band_tags, belt_band_layers, z = path_point_mm.z()](coord_t x, coord_t y) {
|
||||
if (! belt_band_tags)
|
||||
return;
|
||||
const int band = std::min(this->effective_layer_index_for_point(Vec3d(unscale<double>(x), unscale<double>(y), z)), belt_band_cap);
|
||||
if (m_belt_band_tag_layer != m_layer_index || m_belt_band_tag != band) {
|
||||
gcode += ";_BELT_BAND:" + std::to_string(band) + "\n";
|
||||
m_belt_band_tag = band;
|
||||
m_belt_band_tag_layer = m_layer_index;
|
||||
const bool in_band = this->effective_layer_index_for_point(Vec3d(unscale<double>(x), unscale<double>(y), z)) < belt_band_layers;
|
||||
if (in_band != m_belt_in_band) {
|
||||
gcode += in_band ? ";_BELT_BAND_START\n" : ";_BELT_BAND_END\n";
|
||||
m_belt_in_band = in_band;
|
||||
}
|
||||
};
|
||||
tag_belt_band(path.first_point().x(), path.first_point().y());
|
||||
if (path_on_first_layer || object_layer_over_raft()) {
|
||||
//BBS: for solid infill of first layer, speed can be higher as long as
|
||||
//wall lines have be attached
|
||||
|
||||
@@ -856,9 +856,8 @@ protected:
|
||||
~BeltFloorObjectGuard() { slot = nullptr; }
|
||||
};
|
||||
|
||||
// Last ";_BELT_BAND" tag written and the layer it was written on (see _extrude()).
|
||||
int m_belt_band_tag{std::numeric_limits<int>::min()};
|
||||
int m_belt_band_tag_layer{std::numeric_limits<int>::min()};
|
||||
// The last extrusion segment was inside the belt's first-layer fan band (see _extrude()).
|
||||
bool m_belt_in_band{false};
|
||||
|
||||
std::set<unsigned int> m_initial_layer_extruders;
|
||||
std::vector<std::vector<unsigned int>> m_sorted_layer_filaments;
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
#include "../GCode.hpp"
|
||||
#include "CoolingBuffer.hpp"
|
||||
#include "FanMover.hpp"
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/replace.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <float.h>
|
||||
#include <string_view>
|
||||
@@ -39,8 +36,7 @@ CoolingBuffer::CoolingBuffer(GCode &gcodegen) : m_config(gcodegen.config()), m_t
|
||||
|
||||
void CoolingBuffer::reset(const Vec3d &position)
|
||||
{
|
||||
m_belt_band_fan = -1;
|
||||
m_belt_band_layer_fan = -1;
|
||||
m_belt_band_active = false;
|
||||
// BBS: add I and J axis to store center of arc
|
||||
m_current_pos.assign(7, 0.f);
|
||||
m_current_pos[0] = float(position.x());
|
||||
@@ -80,6 +76,9 @@ struct CoolingLine
|
||||
// ORCA: Add support for ironing fan speed control
|
||||
TYPE_IRONING_FAN_START = 1 << 19,
|
||||
TYPE_IRONING_FAN_END = 1 << 20,
|
||||
// Belt printers: extrusions within the first-layer band above the belt.
|
||||
TYPE_BELT_BAND_START = 1 << 21,
|
||||
TYPE_BELT_BAND_END = 1 << 22,
|
||||
};
|
||||
|
||||
CoolingLine(unsigned int type, size_t line_start, size_t line_end) :
|
||||
@@ -334,13 +333,9 @@ std::string CoolingBuffer::process_layer(std::string &&gcode, size_t layer_id, b
|
||||
if (flush) {
|
||||
// This is either an object layer or the very last print layer. Calculate cool down over the collected support layers
|
||||
// and one object layer.
|
||||
const unsigned int extruder_at_start = m_current_extruder;
|
||||
std::vector<PerExtruderAdjustments> per_extruder_adjustments = this->parse_layer_gcode(m_gcode, m_current_pos);
|
||||
float layer_time_stretched = this->calculate_layer_slowdown(per_extruder_adjustments);
|
||||
out = this->apply_layer_cooldown(m_gcode, layer_id, layer_time_stretched, per_extruder_adjustments);
|
||||
// Belt printers: the fan follows each path's height above the belt (see apply_belt_band_fan).
|
||||
if (out.find(";_BELT_BAND:") != std::string::npos)
|
||||
out = this->apply_belt_band_fan(std::move(out), layer_time_stretched, extruder_at_start);
|
||||
m_gcode.clear();
|
||||
}
|
||||
return out;
|
||||
@@ -544,6 +539,10 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
|
||||
line.type = CoolingLine::TYPE_IRONING_FAN_START;
|
||||
} else if (boost::starts_with(sline, ";_IRONING_FAN_END")) { // ORCA: Add support for ironing fan speed control
|
||||
line.type = CoolingLine::TYPE_IRONING_FAN_END;
|
||||
} else if (boost::starts_with(sline, ";_BELT_BAND_START")) {
|
||||
line.type = CoolingLine::TYPE_BELT_BAND_START;
|
||||
} else if (boost::starts_with(sline, ";_BELT_BAND_END")) {
|
||||
line.type = CoolingLine::TYPE_BELT_BAND_END;
|
||||
} else if (boost::starts_with(sline, "G4 ")) {
|
||||
// Parse the wait time.
|
||||
line.type = CoolingLine::TYPE_G4;
|
||||
@@ -885,7 +884,9 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
||||
{CoolingLine::TYPE_SUPPORT_INTERFACE_FAN_START, false},
|
||||
{CoolingLine::TYPE_IRONING_FAN_START, false}, // ORCA: Add support for ironing fan speed control
|
||||
{CoolingLine::TYPE_FORCE_RESUME_FAN, false}};
|
||||
bool need_set_fan = false;
|
||||
// Belt printers: a band still open from the previous layer has to take the fan back from
|
||||
// the layer-level speed issued just above.
|
||||
bool need_set_fan = m_belt_band_active;
|
||||
|
||||
for (const CoolingLine *line : lines) {
|
||||
const char *line_start = gcode.c_str() + line->line_start;
|
||||
@@ -899,6 +900,8 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
||||
if (new_extruder != m_current_extruder) {
|
||||
m_current_extruder = new_extruder;
|
||||
change_extruder_set_fan(true);
|
||||
if (m_belt_band_active)
|
||||
need_set_fan = true;
|
||||
}
|
||||
}
|
||||
new_gcode.append(line_start, line_end - line_start);
|
||||
@@ -951,6 +954,13 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
||||
if (m_additional_fan_speed != -1 && m_config.auxiliary_fan.value)
|
||||
new_gcode += GCodeWriter::set_additional_fan(m_additional_fan_speed);
|
||||
}
|
||||
else if (line->type & CoolingLine::TYPE_BELT_BAND_START) {
|
||||
m_belt_band_active = true;
|
||||
need_set_fan = true;
|
||||
} else if (line->type & CoolingLine::TYPE_BELT_BAND_END) {
|
||||
m_belt_band_active = false;
|
||||
need_set_fan = true;
|
||||
}
|
||||
else if (line->type & CoolingLine::TYPE_EXTRUDE_END) {
|
||||
// Just remove this comment.
|
||||
} else if (line->type & (CoolingLine::TYPE_ADJUSTABLE | CoolingLine::TYPE_EXTERNAL_PERIMETER | CoolingLine::TYPE_WIPE | CoolingLine::TYPE_HAS_F)) {
|
||||
@@ -1043,7 +1053,15 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
||||
m_current_fan_speed = speed;
|
||||
}
|
||||
};
|
||||
if (fan_speed_change_requests[CoolingLine::TYPE_OVERHANG_FAN_START]){
|
||||
if (m_belt_band_active) {
|
||||
// Belt printers: a tilted layer runs from the belt to the top of the part, so
|
||||
// "the first layers" are a band along the belt rather than the first slicing
|
||||
// layers. Extrusions GCode::_extrude() marks as inside that band print with the
|
||||
// fan off, whatever overhang, bridge or resume request is pending, as the first
|
||||
// layers of a flat bed do. Leaving the band falls through to the branches below.
|
||||
set_fan(0);
|
||||
fan_speed_change_requests[CoolingLine::TYPE_FORCE_RESUME_FAN] = false;
|
||||
} else if (fan_speed_change_requests[CoolingLine::TYPE_OVERHANG_FAN_START]){
|
||||
set_fan(overhang_fan_speed);
|
||||
} else if (fan_speed_change_requests[CoolingLine::TYPE_INTERNAL_BRIDGE_FAN_START]){ // ORCA: Add support for separate internal bridge fan speed control
|
||||
set_fan(internal_bridge_fan_speed);
|
||||
@@ -1072,115 +1090,4 @@ std::string CoolingBuffer::apply_layer_cooldown(
|
||||
return new_gcode;
|
||||
}
|
||||
|
||||
// Pure helper: compute the main fan speed for a given effective layer index.
|
||||
// Mirrors the inline logic in change_extruder_set_fan but is callable from
|
||||
// per-tag code in apply_belt_band_fan.
|
||||
int CoolingBuffer::compute_main_fan_speed(int effective_layer_id, float layer_time,
|
||||
unsigned int extruder_id) const
|
||||
{
|
||||
#define EXTRUDER_CFG(opt) m_config.opt.get_at(extruder_id)
|
||||
float fan_min_speed = EXTRUDER_CFG(fan_min_speed);
|
||||
float fan_max_speed = EXTRUDER_CFG(fan_max_speed);
|
||||
bool reduce_fan_stop_start_freq = EXTRUDER_CFG(reduce_fan_stop_start_freq);
|
||||
int close_fan_the_first_x_layers = EXTRUDER_CFG(close_fan_the_first_x_layers);
|
||||
int full_fan_speed_layer = EXTRUDER_CFG(full_fan_speed_layer);
|
||||
float slow_down_layer_time = float(EXTRUDER_CFG(slow_down_layer_time));
|
||||
float fan_cooling_layer_time = float(EXTRUDER_CFG(fan_cooling_layer_time));
|
||||
#undef EXTRUDER_CFG
|
||||
|
||||
if (close_fan_the_first_x_layers <= 0 && full_fan_speed_layer > 0)
|
||||
close_fan_the_first_x_layers = 1;
|
||||
|
||||
float fan_speed_new = reduce_fan_stop_start_freq ? fan_min_speed : 0.f;
|
||||
if (effective_layer_id >= close_fan_the_first_x_layers) {
|
||||
if (layer_time < slow_down_layer_time) {
|
||||
fan_speed_new = fan_max_speed;
|
||||
} else if (layer_time < fan_cooling_layer_time) {
|
||||
double t = (layer_time - slow_down_layer_time) /
|
||||
(fan_cooling_layer_time - slow_down_layer_time);
|
||||
fan_speed_new = float(int(floor(t * fan_min_speed +
|
||||
(1. - t) * fan_max_speed) + 0.5));
|
||||
}
|
||||
if (effective_layer_id + 1 < full_fan_speed_layer) {
|
||||
float factor = float(effective_layer_id + 1 - close_fan_the_first_x_layers)
|
||||
/ float(full_fan_speed_layer - close_fan_the_first_x_layers);
|
||||
fan_speed_new = float(std::clamp(int(fan_speed_new * factor + 0.5f), 0, 255));
|
||||
}
|
||||
} else {
|
||||
fan_speed_new = 0.f;
|
||||
}
|
||||
return int(fan_speed_new);
|
||||
}
|
||||
|
||||
// Belt printers: a layer is a tilted slab, so "the first layer" is not a slicing layer but
|
||||
// whatever lies within a layer height of the belt. GCode::_extrude() knows each path's
|
||||
// height above the belt in the slicing frame and tags every change of it with
|
||||
// ";_BELT_BAND:<effective layer index>". This pass turns the tags into part-fan changes
|
||||
// and strips them. (The G-code itself is in machine coordinates, which is why the band
|
||||
// is not worked out from the moves here.)
|
||||
//
|
||||
// Only the main part-cooling fan is touched; overhang and bridge fans keep the values
|
||||
// apply_layer_cooldown() gave them.
|
||||
std::string CoolingBuffer::apply_belt_band_fan(std::string &&gcode_in, float layer_time, unsigned int extruder_at_start)
|
||||
{
|
||||
static constexpr std::string_view band_tag = ";_BELT_BAND:";
|
||||
const std::string &gcode = gcode_in;
|
||||
std::string out;
|
||||
out.reserve(gcode.size());
|
||||
|
||||
// Match the PWM floor applied at every other set_fan call in this file.
|
||||
const unsigned int part_cooling_fan_min_pwm = static_cast<unsigned int>(std::max(0, m_config.part_cooling_fan_min_pwm.value));
|
||||
unsigned int active_extruder = extruder_at_start;
|
||||
|
||||
const char *p = gcode.data();
|
||||
const char *end = p + gcode.size();
|
||||
while (p < end) {
|
||||
const char *line_end = static_cast<const char*>(std::memchr(p, '\n', end - p));
|
||||
if (line_end == nullptr)
|
||||
line_end = end;
|
||||
const char *next_line = line_end < end ? line_end + 1 : end;
|
||||
const std::string_view line(p, line_end - p);
|
||||
|
||||
if (line.size() > band_tag.size() && line.compare(0, band_tag.size(), band_tag) == 0) {
|
||||
const int eff_idx = std::atoi(std::string(line.substr(band_tag.size())).c_str());
|
||||
const bool in_band = eff_idx < std::max(m_config.close_fan_the_first_x_layers.get_at(active_extruder), 1);
|
||||
int target = this->compute_main_fan_speed(eff_idx, layer_time, active_extruder);
|
||||
// Clear of the belt and nothing to add: fall back to what the layer asked for.
|
||||
if (! in_band && target == 0 && m_belt_band_layer_fan >= 0)
|
||||
target = m_belt_band_layer_fan;
|
||||
if (target != m_belt_band_fan) {
|
||||
out += GCodeWriter::set_fan(m_config.gcode_flavor, target, part_cooling_fan_min_pwm);
|
||||
m_belt_band_fan = target;
|
||||
}
|
||||
// The tag itself is dropped.
|
||||
p = next_line;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! line.empty() && line.front() == 'M') {
|
||||
// Follow the fan commands of the layer-level cooling. FanMover's parser ignores
|
||||
// auxiliary and chamber fans (M106 P2, P3...), which must not be taken for the part fan.
|
||||
const int16_t raw = get_fan_speed(std::string(line), m_config.gcode_flavor);
|
||||
if (raw >= 0) {
|
||||
m_belt_band_fan = int(std::lround(double(std::min<int16_t>(raw, 255)) * 100. / 255.));
|
||||
m_belt_band_layer_fan = m_belt_band_fan;
|
||||
}
|
||||
} else if (line.size() > m_toolchange_prefix.size() && line.compare(0, m_toolchange_prefix.size(), m_toolchange_prefix) == 0) {
|
||||
char *num_end = nullptr;
|
||||
const std::string num(line.substr(m_toolchange_prefix.size()));
|
||||
const long tool = std::strtol(num.c_str(), &num_end, 10);
|
||||
if (num_end != num.c_str() && tool >= 0)
|
||||
active_extruder = unsigned(tool);
|
||||
}
|
||||
|
||||
out.append(p, next_line - p);
|
||||
p = next_line;
|
||||
}
|
||||
|
||||
// The fan's real state, so the next layer's cooling re-issues its own speed if it differs.
|
||||
if (m_belt_band_fan >= 0)
|
||||
m_current_fan_speed = m_belt_band_fan;
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -36,18 +36,6 @@ private:
|
||||
// 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);
|
||||
|
||||
// Belt printers: turn the ";_BELT_BAND:<n>" tags GCode::_extrude() leaves in the
|
||||
// layer's G-code into part-fan changes, so the fan follows a path's height above the
|
||||
// belt rather than the slicing layer index, and strip the tags.
|
||||
std::string apply_belt_band_fan(std::string &&gcode_in, float layer_time, unsigned int extruder_at_start);
|
||||
|
||||
// Pure helper: compute the main fan speed for a given effective layer
|
||||
// index (layer-id units, mapped through the plane evaluator) and the
|
||||
// current extruder. Mirrors the inline logic in the change_extruder_set_fan
|
||||
// lambda but is callable from per-line code.
|
||||
int compute_main_fan_speed(int effective_layer_id, float layer_time,
|
||||
unsigned int extruder_id) const;
|
||||
|
||||
// G-code snippet cached for the support layers preceding an object layer.
|
||||
std::string m_gcode;
|
||||
// Internal data.
|
||||
@@ -70,10 +58,9 @@ private:
|
||||
unsigned int m_current_nozzle;
|
||||
//BBS: current fan speed
|
||||
int m_current_fan_speed;
|
||||
// Belt band pass state, kept across layers. The part fan as this pass last saw or set
|
||||
// it (percent, -1 unknown), and the last value the layer-level cooling asked for.
|
||||
int m_belt_band_fan = -1;
|
||||
int m_belt_band_layer_fan = -1;
|
||||
// Belt printers: the extrusion being processed lies in the first-layer band above the
|
||||
// belt (between a ";_BELT_BAND_START" and a ";_BELT_BAND_END"). Kept across layers.
|
||||
bool m_belt_band_active = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -27,11 +27,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// The part-cooling fan value a G-code line sets: the raw S (0..255) of an M106 that
|
||||
// addresses the part fan, 0 for a fan-off command, -1 for any other line (auxiliary
|
||||
// and chamber fans included).
|
||||
int16_t get_fan_speed(const std::string &line, GCodeFlavor flavor);
|
||||
|
||||
class FanMover
|
||||
{
|
||||
private:
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
#include "test_utils.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <cstdlib>
|
||||
#include <sstream>
|
||||
#include <limits>
|
||||
#include <fstream>
|
||||
#include <iterator>
|
||||
@@ -628,13 +631,11 @@ TEST_CASE("Belt printers refuse an object taller than the gantry clearance", "[P
|
||||
}
|
||||
}
|
||||
|
||||
// On a belt every tilted layer starts on the belt, so "first layer" cooling is a band along
|
||||
// the belt, not the first slicing layers: the part fan goes off for the paths that start
|
||||
// within a layer height of the belt and back on above it, on every layer. The G-code is in
|
||||
// machine coordinates, so the generator tags the band changes and the cooling buffer
|
||||
// applies them; before that the buffer compared machine-frame moves with a slicing-frame
|
||||
// plane and never switched the fan at all.
|
||||
TEST_CASE("Belt printers switch the part fan by height above the belt", "[Print][belt][Cooling]")
|
||||
// On a belt every tilted layer starts on the belt, so "the first layers" the fan stays off
|
||||
// for are a band along the belt, not the first slicing layers. The generator marks where
|
||||
// each extrusion segment enters and leaves that band and the cooling buffer keeps the fan
|
||||
// off inside it, on every layer.
|
||||
TEST_CASE("Belt printers keep the part fan off within the band above the belt", "[Print][belt][Cooling]")
|
||||
{
|
||||
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
|
||||
config.set_deserialize_strict({
|
||||
@@ -649,8 +650,8 @@ TEST_CASE("Belt printers switch the part fan by height above the belt", "[Print]
|
||||
{ "initial_layer_print_height", 0.2 },
|
||||
{ "skirt_loops", 0 },
|
||||
{ "z_hop", 0 },
|
||||
// Three layers: the lowest wall of each tilted layer is centred about 0.3 mm above
|
||||
// the belt (half a line width in from the contact edge), outside a one-layer band.
|
||||
// Three layers, 0.6 mm: the lowest wall of each tilted layer is centred about 0.3 mm
|
||||
// above the belt (half a line width in from the contact edge).
|
||||
{ "close_fan_the_first_x_layers", 3 },
|
||||
{ "full_fan_speed_layer", 0 },
|
||||
{ "fan_min_speed", 100 },
|
||||
@@ -664,24 +665,50 @@ TEST_CASE("Belt printers switch the part fan by height above the belt", "[Print]
|
||||
const std::string gcode = slice({ cube(20) }, config);
|
||||
REQUIRE(! gcode.empty());
|
||||
|
||||
// The tags are consumed by the cooling buffer and never reach the file.
|
||||
// The markers are consumed by the cooling buffer and never reach the file.
|
||||
CHECK(gcode.find(";_BELT_BAND") == std::string::npos);
|
||||
|
||||
// The fan commands in order: '0' off, '1' on.
|
||||
std::string fan_sequence;
|
||||
GCodeReader parser;
|
||||
parser.parse_buffer(gcode, [&](GCodeReader &, const GCodeReader::GCodeLine &line) {
|
||||
if (line.cmd_is("M107"))
|
||||
fan_sequence += '0';
|
||||
else if (line.cmd_is("M106")) {
|
||||
float s = 0.f;
|
||||
fan_sequence += (line.has_value('S', s) && s <= 0.f) ? '0' : '1';
|
||||
// With this axis mapping machine Y is the height above the belt along the gantry. Walk
|
||||
// the moves with the fan state: extrusions that stay within 0.45 mm of the belt are well
|
||||
// inside the band and must print with the fan off; extrusions that stay 5 mm clear of it
|
||||
// must print with it on. The first three slicing layers have the fan off altogether.
|
||||
size_t in_band = 0, in_band_fan_on = 0, clear = 0, clear_fan_off = 0;
|
||||
int layer = -1;
|
||||
bool fan_on = false;
|
||||
double y = 0.;
|
||||
std::istringstream lines(gcode);
|
||||
for (std::string line; std::getline(lines, line); ) {
|
||||
if (boost::starts_with(line, ";LAYER_CHANGE")) {
|
||||
++ layer;
|
||||
} else if (boost::starts_with(line, "M107")) {
|
||||
fan_on = false;
|
||||
} else if (boost::starts_with(line, "M106")) {
|
||||
const size_t s = line.find('S');
|
||||
fan_on = s != std::string::npos && std::atof(line.c_str() + s + 1) > 0.;
|
||||
} else if (boost::starts_with(line, "G1 ")) {
|
||||
const size_t comment = line.find(';');
|
||||
const std::string cmd = line.substr(0, comment);
|
||||
const size_t ypos = cmd.find(" Y"), epos = cmd.find(" E");
|
||||
if (ypos == std::string::npos)
|
||||
continue;
|
||||
const double y_new = std::atof(cmd.c_str() + ypos + 2);
|
||||
const bool extruding = epos != std::string::npos && std::atof(cmd.c_str() + epos + 2) > 0.;
|
||||
if (extruding && layer >= 3) {
|
||||
if (std::max(y, y_new) < 0.45) {
|
||||
++ in_band;
|
||||
in_band_fan_on += fan_on;
|
||||
} else if (std::min(y, y_new) > 5.) {
|
||||
++ clear;
|
||||
clear_fan_off += ! fan_on;
|
||||
}
|
||||
}
|
||||
y = y_new;
|
||||
}
|
||||
});
|
||||
// A flat-bed print turns the fan on once and leaves it on. Here it goes off again for
|
||||
// paths that start back down at the belt, and on again above it.
|
||||
INFO("fan sequence: " << fan_sequence);
|
||||
CHECK(fan_sequence.find("101") != std::string::npos);
|
||||
}
|
||||
CHECK(in_band > 20);
|
||||
CHECK(in_band_fan_on == 0);
|
||||
CHECK(clear > 20);
|
||||
CHECK(clear_fan_off == 0);
|
||||
}
|
||||
|
||||
// Organic supports under an overhang on a belt printer reach below the object's first layer,
|
||||
|
||||
Reference in New Issue
Block a user