mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-14 11:21:18 +00:00
Merge branch 'main' into perf/gcode-result-move
This commit is contained in:
@@ -1,14 +1,30 @@
|
||||
#include "libslic3r/ArcFitter.hpp"
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/CustomGCode.hpp"
|
||||
#include "libslic3r/Exception.hpp"
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/CommonDefs.hpp"
|
||||
#include "ExtrusionEntity.hpp"
|
||||
#include "libslic3r/GCodeReader.hpp"
|
||||
#include "GCodeWriter.hpp"
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/PrintBase.hpp"
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "libslic3r/TriangleSelector.hpp"
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
#include "libslic3r/format.hpp"
|
||||
#include "libslic3r_version.h"
|
||||
#include "GCodeProcessor.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <boost/algorithm/string/classification.hpp>
|
||||
#include <boost/algorithm/string/constants.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
@@ -16,15 +32,38 @@
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cmath>
|
||||
#include <exception>
|
||||
#include <deque>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <cstdlib>
|
||||
#include <climits>
|
||||
#include <fast_float/fast_float.h>
|
||||
|
||||
#include <float.h>
|
||||
#include <assert.h>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <iterator>
|
||||
#include <functional>
|
||||
#include <math.h>
|
||||
#include <map>
|
||||
#include <limits>
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <charconv>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <system_error>
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#if __has_include(<charconv>)
|
||||
#include <charconv>
|
||||
@@ -34,6 +73,7 @@
|
||||
#include <chrono>
|
||||
|
||||
#include "Geometry/ArcWelder.hpp"
|
||||
#include "libslic3r/MultiNozzleUtils.hpp"
|
||||
|
||||
static const float DEFAULT_TOOLPATH_WIDTH = 0.4f;
|
||||
static const float DEFAULT_TOOLPATH_HEIGHT = 0.2f;
|
||||
@@ -49,7 +89,6 @@ static const float DEFAULT_TRAVEL_ACCELERATION = 1250.0f;
|
||||
static const size_t MIN_EXTRUDERS_COUNT = 5;
|
||||
static const float DEFAULT_FILAMENT_DIAMETER = 1.75f;
|
||||
static const int DEFAULT_FILAMENT_HRC = 0;
|
||||
static const float DEFAULT_FILAMENT_DENSITY = 1.245f;
|
||||
static const float DEFAULT_FILAMENT_COST = 29.99f;
|
||||
static const int DEFAULT_FILAMENT_VITRIFICATION_TEMPERATURE = 0;
|
||||
static const Slic3r::Vec3f DEFAULT_EXTRUDER_OFFSET = Slic3r::Vec3f::Zero();
|
||||
@@ -711,6 +750,50 @@ template<typename T>
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
// Writes G-code to a file in blocks and records in lines_ends the file offset after every '\n'
|
||||
class GCodeFileWriter
|
||||
{
|
||||
public:
|
||||
GCodeFileWriter(FilePtr& out, const std::string& out_path, std::vector<size_t>& lines_ends, const char* error_message)
|
||||
: m_out(out), m_out_path(out_path), m_lines_ends(lines_ends), m_error_message(error_message)
|
||||
{}
|
||||
~GCodeFileWriter() { assert(m_buffer.empty() || std::uncaught_exceptions() > 0); }
|
||||
|
||||
void append(std::string_view text)
|
||||
{
|
||||
const size_t text_pos = m_file_pos + m_buffer.size();
|
||||
for (size_t i = text.find('\n'); i != std::string_view::npos; i = text.find('\n', i + 1))
|
||||
m_lines_ends.emplace_back(text_pos + i + 1);
|
||||
m_buffer += text;
|
||||
if (m_buffer.size() >= GCodeProcessor::Output_Block_Size)
|
||||
flush();
|
||||
}
|
||||
|
||||
void flush()
|
||||
{
|
||||
if (m_buffer.empty())
|
||||
return;
|
||||
fwrite(m_buffer.data(), 1, m_buffer.size(), m_out.f);
|
||||
if (ferror(m_out.f)) {
|
||||
m_out.close();
|
||||
boost::nowide::remove(m_out_path.c_str());
|
||||
throw Slic3r::RuntimeError(m_error_message);
|
||||
}
|
||||
m_file_pos += m_buffer.size();
|
||||
m_buffer.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
FilePtr& m_out;
|
||||
const std::string& m_out_path;
|
||||
std::vector<size_t>& m_lines_ends;
|
||||
const char* m_error_message;
|
||||
std::string m_buffer;
|
||||
size_t m_file_pos{0};
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// Helper class to modify and export gcode to file
|
||||
class ExportLines
|
||||
{
|
||||
@@ -725,16 +808,6 @@ public:
|
||||
enum class EWriteType { BySize, ByTime };
|
||||
|
||||
private:
|
||||
static void update_lines_ends_and_out_file_pos(const std::string& out_string, std::vector<size_t>& lines_ends, size_t* out_file_pos)
|
||||
{
|
||||
for (size_t i = 0; i < out_string.size(); ++i) {
|
||||
if (out_string[i] == '\n')
|
||||
lines_ends.emplace_back((out_file_pos != nullptr) ? *out_file_pos + i + 1 : i + 1);
|
||||
}
|
||||
if (out_file_pos != nullptr)
|
||||
*out_file_pos += out_string.size();
|
||||
}
|
||||
|
||||
struct LineData
|
||||
{
|
||||
std::string line;
|
||||
@@ -774,12 +847,14 @@ private:
|
||||
EWriteType m_write_type{EWriteType::BySize};
|
||||
// Time machines containing g1 times cache
|
||||
const std::array<GCodeProcessor::TimeMachine, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)>& m_machines;
|
||||
// Output file writer
|
||||
GCodeFileWriter& m_writer;
|
||||
// Current time
|
||||
std::array<float, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> m_times{0.0f, 0.0f};
|
||||
// Current size in bytes
|
||||
// Current size of the cache in bytes
|
||||
size_t m_size{0};
|
||||
|
||||
// gcode lines cache
|
||||
// gcode lines cache, used only when writing by time
|
||||
std::deque<LineData> m_lines;
|
||||
size_t m_added_lines_counter{0};
|
||||
// map of gcode line ids from original to final
|
||||
@@ -787,16 +862,16 @@ private:
|
||||
std::vector<std::pair<size_t, size_t>> m_gcode_lines_map;
|
||||
|
||||
size_t m_times_cache_id{0};
|
||||
size_t m_out_file_pos{0};
|
||||
|
||||
public:
|
||||
ExportLines(EWriteType type, const std::array<GCodeProcessor::TimeMachine, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)>& machines)
|
||||
ExportLines(EWriteType type, const std::array<GCodeProcessor::TimeMachine, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)>& machines, GCodeFileWriter& writer)
|
||||
#ifndef NDEBUG
|
||||
: m_statistics(*this)
|
||||
, m_write_type(type)
|
||||
, m_machines(machines){}
|
||||
, m_machines(machines)
|
||||
, m_writer(writer){}
|
||||
#else
|
||||
: m_write_type(type), m_machines(machines)
|
||||
: m_write_type(type), m_machines(machines), m_writer(writer)
|
||||
{}
|
||||
#endif // NDEBUG
|
||||
|
||||
@@ -850,11 +925,14 @@ public:
|
||||
if (line.empty())
|
||||
return;
|
||||
|
||||
m_lines.push_back({line, m_times});
|
||||
if (m_write_type == EWriteType::ByTime) {
|
||||
m_lines.push_back({line, m_times});
|
||||
#ifndef NDEBUG
|
||||
m_statistics.add_line(line.length());
|
||||
m_statistics.add_line(line.length());
|
||||
#endif // NDEBUG
|
||||
m_size += line.length();
|
||||
m_size += line.length();
|
||||
} else
|
||||
m_writer.append(line);
|
||||
++m_added_lines_counter;
|
||||
if (!ignore_from_move) {
|
||||
assert(!m_gcode_lines_map.empty());
|
||||
@@ -870,8 +948,8 @@ public:
|
||||
{
|
||||
// Orca: find start pos by seaching G28/G29/PRINT_START/START_PRINT commands
|
||||
auto is_start_pos = [](const std::string& curr_cmd) {
|
||||
return boost::iequals(curr_cmd, "G28") || boost::iequals(curr_cmd, "G29") || boost::iequals(curr_cmd, "PRINT_START") ||
|
||||
boost::iequals(curr_cmd, "START_PRINT");
|
||||
return ascii_iequals(curr_cmd, "G28") || ascii_iequals(curr_cmd, "G29") || ascii_iequals(curr_cmd, "PRINT_START") ||
|
||||
ascii_iequals(curr_cmd, "START_PRINT");
|
||||
};
|
||||
assert(!m_lines.empty());
|
||||
const float time_step = backtrace.time_step();
|
||||
@@ -920,65 +998,35 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// write to file:
|
||||
// m_write_type == EWriteType::ByTime - all lines older than m_time - backtrace_time
|
||||
// m_write_type == EWriteType::BySize - all lines if current size is greater than 65535 bytes
|
||||
void write(FilePtr& out, float backtrace_time, GCodeProcessorResult& result, const std::string& out_path)
|
||||
// when writing by time, pass the cached lines older than m_times[Normal] - backtrace_time to the writer
|
||||
void write(float backtrace_time)
|
||||
{
|
||||
if (m_lines.empty())
|
||||
if (m_write_type != EWriteType::ByTime)
|
||||
return;
|
||||
|
||||
// collect lines to write into a single string
|
||||
std::string out_string;
|
||||
if (!m_lines.empty()) {
|
||||
if (m_write_type == EWriteType::ByTime) {
|
||||
while (m_lines.front().times[Normal] < m_times[Normal] - backtrace_time) {
|
||||
const LineData& data = m_lines.front();
|
||||
out_string += data.line;
|
||||
m_size -= data.line.length();
|
||||
m_lines.pop_front();
|
||||
while (!m_lines.empty() && m_lines.front().times[Normal] < m_times[Normal] - backtrace_time) {
|
||||
const LineData& data = m_lines.front();
|
||||
m_writer.append(data.line);
|
||||
m_size -= data.line.length();
|
||||
m_lines.pop_front();
|
||||
#ifndef NDEBUG
|
||||
m_statistics.remove_line();
|
||||
m_statistics.remove_line();
|
||||
#endif // NDEBUG
|
||||
}
|
||||
} else {
|
||||
if (m_size > 65535) {
|
||||
while (!m_lines.empty()) {
|
||||
out_string += m_lines.front().line;
|
||||
m_lines.pop_front();
|
||||
}
|
||||
m_size = 0;
|
||||
#ifndef NDEBUG
|
||||
m_statistics.remove_all_lines();
|
||||
#endif // NDEBUG
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
write_to_file(out, out_string, result, out_path);
|
||||
update_lines_ends_and_out_file_pos(out_string, result.lines_ends, &m_out_file_pos);
|
||||
}
|
||||
}
|
||||
|
||||
// flush the current content of the cache to file
|
||||
void flush(FilePtr& out, GCodeProcessorResult& result, const std::string& out_path)
|
||||
// flush the current content of the cache and the writer to file
|
||||
void flush()
|
||||
{
|
||||
// collect lines to flush into a single string
|
||||
std::string out_string;
|
||||
while (!m_lines.empty()) {
|
||||
out_string += m_lines.front().line;
|
||||
m_writer.append(m_lines.front().line);
|
||||
m_lines.pop_front();
|
||||
}
|
||||
m_size = 0;
|
||||
#ifndef NDEBUG
|
||||
m_statistics.remove_all_lines();
|
||||
#endif // NDEBUG
|
||||
|
||||
{
|
||||
write_to_file(out, out_string, result, out_path);
|
||||
update_lines_ends_and_out_file_pos(out_string, result.lines_ends, &m_out_file_pos);
|
||||
}
|
||||
m_writer.flush();
|
||||
}
|
||||
|
||||
void synchronize_moves(GCodeProcessorResult& result) const
|
||||
@@ -1011,20 +1059,7 @@ public:
|
||||
|
||||
size_t get_size() const { return m_size; }
|
||||
|
||||
private:
|
||||
void write_to_file(FilePtr& out, const std::string& out_string, GCodeProcessorResult& result, const std::string& out_path)
|
||||
{
|
||||
if (!out_string.empty()) {
|
||||
if (true) {
|
||||
fwrite((const void*) out_string.c_str(), 1, out_string.length(), out.f);
|
||||
if (ferror(out.f)) {
|
||||
out.close();
|
||||
boost::nowide::remove(out_path.c_str());
|
||||
throw Slic3r::RuntimeError("GCode processor post process export failed.\nIs the disk full?");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void reserve(size_t lines_count) { m_gcode_lines_map.reserve(lines_count); }
|
||||
};
|
||||
|
||||
void GCodeProcessor::run_post_process()
|
||||
@@ -1120,8 +1155,13 @@ void GCodeProcessor::run_post_process()
|
||||
last_exported_stop[i] = time_in_minutes(m_time_processor.machines[i].time);
|
||||
}
|
||||
|
||||
m_result.lines_ends.clear();
|
||||
// m_result.lines_ends.emplace_back(std::vector<size_t>());
|
||||
GCodeFileWriter writer(out, out_path, m_result.lines_ends, "GCode processor post process export failed.\nIs the disk full?");
|
||||
ExportLines export_line(m_result.backtrace_enabled ? ExportLines::EWriteType::ByTime : ExportLines::EWriteType::BySize,
|
||||
m_time_processor.machines);
|
||||
m_time_processor.machines, writer);
|
||||
// The line map holds an entry for each line of the file, and the first pass counted them
|
||||
export_line.reserve(m_line_id);
|
||||
|
||||
// replace placeholder lines with the proper final value
|
||||
// gcode_line is in/out parameter, to reduce expensive memory allocation
|
||||
@@ -1226,6 +1266,9 @@ void GCodeProcessor::run_post_process()
|
||||
// Process inline placeholders (print_time_total_sec, print_time_day, print_time_hour, print_time_minute, print_time_sec and used_filament_length)
|
||||
auto process_inline_placeholders = [&](std::string& gcode_line) {
|
||||
bool processed = false;
|
||||
// Every inline placeholder contains '@', so a line without one has nothing to replace.
|
||||
if (gcode_line.find('@') == std::string::npos)
|
||||
return processed;
|
||||
|
||||
const std::string& print_time_total_placeholder = reserved_tag(ETags::Print_Time_Total_Sec_Placeholder);
|
||||
const std::string& print_time_day_placeholder = reserved_tag(ETags::Print_Time_Day_Placeholder);
|
||||
@@ -1492,9 +1535,6 @@ void GCodeProcessor::run_post_process()
|
||||
}
|
||||
};
|
||||
|
||||
m_result.lines_ends.clear();
|
||||
// m_result.lines_ends.emplace_back(std::vector<size_t>());
|
||||
|
||||
// Orca: freshly collect SKIPPABLE ranges each post-process pass. The ranges are stored on the
|
||||
// member (rather than a local) so the injection pass can consume them, hence the clear here to
|
||||
// avoid stale ranges on re-invocation.
|
||||
@@ -1759,7 +1799,7 @@ void GCodeProcessor::run_post_process()
|
||||
|
||||
if (!gcode_line.empty())
|
||||
export_line.append_line(gcode_line);
|
||||
export_line.write(out, 1.1f * max_backtrace_time, m_result, out_path);
|
||||
export_line.write(1.1f * max_backtrace_time);
|
||||
gcode_line.clear();
|
||||
}
|
||||
}
|
||||
@@ -1801,7 +1841,7 @@ void GCodeProcessor::run_post_process()
|
||||
}
|
||||
}
|
||||
|
||||
export_line.flush(out, m_result, out_path);
|
||||
export_line.flush();
|
||||
|
||||
out.close();
|
||||
in.close();
|
||||
@@ -1949,31 +1989,13 @@ void GCodeProcessor::run_second_pass_injection()
|
||||
// The rewrite may shift byte positions (once the injector inserts lines), so rebuild lines_ends from scratch.
|
||||
// With an empty map the scanned '\n' offsets reproduce the current lines_ends exactly.
|
||||
m_result.lines_ends.clear();
|
||||
size_t out_file_pos = 0;
|
||||
|
||||
auto write_out = [&out, &out_path, this, &out_file_pos](std::string& str) {
|
||||
if (str.empty())
|
||||
return;
|
||||
fwrite((const void*) str.c_str(), 1, str.length(), out.f);
|
||||
if (ferror(out.f)) {
|
||||
out.close();
|
||||
boost::nowide::remove(out_path.c_str());
|
||||
throw Slic3r::RuntimeError(std::string("GCode processor pre-heat injection pass failed.\nIs the disk full?\n"));
|
||||
}
|
||||
for (size_t i = 0; i < str.size(); ++i) {
|
||||
if (str[i] == '\n')
|
||||
m_result.lines_ends.emplace_back(out_file_pos + i + 1);
|
||||
}
|
||||
out_file_pos += str.size();
|
||||
str.clear();
|
||||
};
|
||||
GCodeFileWriter writer(out, out_path, m_result.lines_ends, "GCode processor pre-heat injection pass failed.\nIs the disk full?\n");
|
||||
|
||||
// Orca: read/split lines with EOL-preserving semantics (keep the original \r and \n bytes, and
|
||||
// synthesize no trailing newline). This is required for the empty-map identity: normalizing every
|
||||
// line ending to "\n" would not be byte-identical if the finished file used \r\n or lacked a
|
||||
// final newline.
|
||||
std::string gcode_line;
|
||||
std::string export_buffer;
|
||||
unsigned int line_id = 0;
|
||||
auto op_it = inserted_operation_lines.begin();
|
||||
std::vector<char> buffer(65536 * 10, 0);
|
||||
@@ -2015,16 +2037,14 @@ void GCodeProcessor::run_second_pass_injection()
|
||||
}
|
||||
++op_it;
|
||||
}
|
||||
export_buffer += gcode_line;
|
||||
writer.append(gcode_line);
|
||||
gcode_line.clear();
|
||||
if (export_buffer.length() >= 65536)
|
||||
write_out(export_buffer);
|
||||
}
|
||||
}
|
||||
if (eof)
|
||||
break;
|
||||
}
|
||||
write_out(export_buffer);
|
||||
writer.flush();
|
||||
|
||||
out.close();
|
||||
in.close();
|
||||
@@ -2587,6 +2607,10 @@ void GCodeProcessorResult::reset() {
|
||||
// (see BackgroundSlicingProcess::apply()). The capacity would not be reused anyway: the
|
||||
// result is refilled by move-assigning the processor's own result.
|
||||
moves = std::vector<MoveVertex>();
|
||||
plate_mass = {};
|
||||
object_masses.clear();
|
||||
body_masses.clear();
|
||||
support_masses.clear();
|
||||
lines_ends = std::vector<size_t>();
|
||||
printable_area = Pointfs();
|
||||
//BBS: add bed exclude area
|
||||
@@ -2599,6 +2623,9 @@ void GCodeProcessorResult::reset() {
|
||||
long_retraction_when_cut = false;
|
||||
timelapse_warning_code = 0;
|
||||
printable_height = 0.0f;
|
||||
machine_frame_transform_active = false;
|
||||
belt_tilt_angle = 0.f;
|
||||
belt_z_origin = 0.f;
|
||||
settings_ids.reset();
|
||||
filaments_count = 0;
|
||||
backtrace_enabled = false;
|
||||
@@ -2835,6 +2862,32 @@ bool GCodeProcessor::check_multi_extruder_gcode_valid(const int
|
||||
return ps;
|
||||
};
|
||||
|
||||
// Belt-printer post-gcode shear/scale/post_remap is applied as the final
|
||||
// step of BeltKinematics::to_machine, so MoveVertex.position is
|
||||
// in the printer's machine frame. Undo it here so the XY area check
|
||||
// operates in the build-volume frame that printable_area is defined in
|
||||
// (the height checks below are skipped on belt printers). For non-belt printers
|
||||
// (is_active() == false) apply_inverse is identity and behaviour is
|
||||
// unchanged from before.
|
||||
const bool machine_frame_active = m_machine_frame_transform.is_active();
|
||||
auto compare_pos = [&](const GCodeProcessorResult::MoveVertex &move) -> Vec3d {
|
||||
Vec3d pos = move.position.cast<double>();
|
||||
if (!machine_frame_active)
|
||||
return pos;
|
||||
Vec3d extruder_off = Vec3d::Zero();
|
||||
if (size_t(move.extruder_id) < m_extruder_offsets.size())
|
||||
extruder_off = m_extruder_offsets[move.extruder_id].cast<double>();
|
||||
// Strip plate + extruder offsets to recover the raw machine-frame
|
||||
// coordinate that was emitted into the G-code (see store_move_vertex).
|
||||
Vec3d machine(pos.x() - m_x_offset - extruder_off.x(),
|
||||
pos.y() - m_y_offset - extruder_off.y(),
|
||||
pos.z() - extruder_off.z() + m_z_offset);
|
||||
Vec3d build = m_machine_frame_transform.apply_inverse(machine);
|
||||
// Re-apply plate offset so the result matches plate_printable_poly,
|
||||
// which is translated by plate_offset below.
|
||||
return Vec3d(build.x() + m_x_offset, build.y() + m_y_offset, build.z());
|
||||
};
|
||||
|
||||
struct GCodePosInfo
|
||||
{
|
||||
Points pos;
|
||||
@@ -2846,26 +2899,20 @@ bool GCodeProcessor::check_multi_extruder_gcode_valid(const int
|
||||
for (const GCodeProcessorResult::MoveVertex &move : m_result.moves) {
|
||||
// sometimes, the start line extrude was outside the edge of plate a little, this is allowed, so do not include into the gcode_path_pos
|
||||
if (move.type == EMoveType::Extrude /* && move.extrusion_role != ExtrusionRole::erFlush || move.type == EMoveType::Travel*/) {
|
||||
const Vec3d cp = compare_pos(move);
|
||||
// For belt printers we read Z from the inverse-transformed position
|
||||
// (post-origin-snap, pre-machine-frame). Otherwise keep the
|
||||
// original print_z source (the slicer's layer-Z comment) so
|
||||
// non-belt behaviour is bit-for-bit unchanged.
|
||||
const float z_for_height = machine_frame_active ? float(cp.z()) : move.print_z;
|
||||
if (move.extrusion_role == ExtrusionRole::erCustom) {
|
||||
/*if (move.is_arc_move_with_interpolation_points()) {
|
||||
for (int i = 0; i < move.interpolation_points.size(); i++) {
|
||||
gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos_custom.emplace_back(to_2d(move.interpolation_points[i].cast<double>()));
|
||||
}
|
||||
} else {*/
|
||||
gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos_custom.emplace_back(to_2d(move.position.cast<double>()));
|
||||
//}
|
||||
gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos_custom.emplace_back(to_2d(cp));
|
||||
gcode_path_pos[move.object_label_id][int(move.extruder_id)].max_print_z_custom =
|
||||
std::max(gcode_path_pos[move.object_label_id][int(move.extruder_id)].max_print_z_custom, move.print_z);
|
||||
std::max(gcode_path_pos[move.object_label_id][int(move.extruder_id)].max_print_z_custom, z_for_height);
|
||||
} else {
|
||||
/*if (move.is_arc_move_with_interpolation_points()) {
|
||||
for (int i = 0; i < move.interpolation_points.size(); i++) {
|
||||
gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos.emplace_back(to_2d(move.interpolation_points[i].cast<double>()));
|
||||
}
|
||||
} else {*/
|
||||
gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos.emplace_back(to_2d(move.position.cast<double>()));
|
||||
//}
|
||||
gcode_path_pos[move.object_label_id][int(move.extruder_id)].pos.emplace_back(to_2d(cp));
|
||||
gcode_path_pos[move.object_label_id][int(move.extruder_id)].max_print_z = std::max(gcode_path_pos[move.object_label_id][int(move.extruder_id)].max_print_z,
|
||||
move.print_z);
|
||||
z_for_height);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2900,7 +2947,12 @@ bool GCodeProcessor::check_multi_extruder_gcode_valid(const int
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
if ( iter->second.max_print_z > plate_printable_height ) { //over height
|
||||
// Belt printers: the Z recorded here grows with belt travel (machine Z with the
|
||||
// frame transform, the slicing-frame Z without it), while printable_height is the
|
||||
// clearance above the belt; the two are not comparable, so the over-height check
|
||||
// is skipped, as the preview's ToolHeightOutside warning already is.
|
||||
// Print::validate() checks the object's height against the clearance.
|
||||
if ( !m_belt_printer && iter->second.max_print_z > plate_printable_height ) { //over height
|
||||
m_result.gcode_check_result.error_code |= (1 << 3);
|
||||
std::pair<int, int> filament_to_object_id;
|
||||
filament_to_object_id.first = iter->first;
|
||||
@@ -2941,7 +2993,7 @@ bool GCodeProcessor::check_multi_extruder_gcode_valid(const int
|
||||
}
|
||||
|
||||
// check printable height
|
||||
if ((extruder_id < printable_heights.size()) && (iter->second.max_print_z > printable_heights[extruder_id])) {
|
||||
if (!m_belt_printer && (extruder_id < printable_heights.size()) && (iter->second.max_print_z > printable_heights[extruder_id])) {
|
||||
m_result.gcode_check_result.error_code |= (1 << 1);
|
||||
std::pair<int, int> filament_to_object_id;
|
||||
filament_to_object_id.first = iter->first;
|
||||
@@ -3107,6 +3159,13 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
|
||||
|
||||
m_result.printable_height = config.printable_height;
|
||||
|
||||
// Belt printer: cache the post-gcode machine-frame transform so the
|
||||
// multi-extruder validator can undo it and compare against build-volume
|
||||
// bounds rather than machine-frame positions.
|
||||
m_machine_frame_transform.init_from_config(config);
|
||||
m_result.machine_frame_transform_active = m_machine_frame_transform.is_active();
|
||||
m_belt_printer = config.belt_printer.value;
|
||||
|
||||
auto filament_maps = config.option<ConfigOptionInts>("filament_map");
|
||||
if (filament_maps != nullptr) {
|
||||
m_filament_maps = filament_maps->values;
|
||||
@@ -3137,6 +3196,32 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
||||
{
|
||||
m_parser.apply_config(config);
|
||||
|
||||
// Belt printer: remember the file's belt keys for export_config_for_render(). The
|
||||
// config block lists belt_printer for every printer, so a non-belt file loaded while
|
||||
// a belt printer is selected switches the preview's belt view off, and a belt file
|
||||
// loaded on another printer brings its own tilt, remaps and bed along.
|
||||
m_belt_render_config.clear();
|
||||
{
|
||||
const auto *belt = config.option<ConfigOptionBool>("belt_printer");
|
||||
if (belt != nullptr) {
|
||||
static const char *belt_keys[] = {
|
||||
"belt_printer", "belt_slice_rotation", "belt_slice_rotation_angle",
|
||||
"gcode_remap_x", "gcode_remap_y", "gcode_remap_z",
|
||||
"belt_frame_tilt_decouple", "belt_frame_tilt_angle",
|
||||
};
|
||||
for (const char *key : belt_keys)
|
||||
if (const ConfigOption *opt = config.option(key); opt != nullptr)
|
||||
m_belt_render_config.set_key_value(key, opt->clone());
|
||||
// The Rev remaps mirror inside the build volume, so the designed view needs
|
||||
// the bed the file was sliced for. Only a belt file may override it.
|
||||
static const char *bed_keys[] = { "printable_area", "printable_height" };
|
||||
if (belt->value)
|
||||
for (const char *key : bed_keys)
|
||||
if (const ConfigOption *opt = config.option(key); opt != nullptr)
|
||||
m_belt_render_config.set_key_value(key, opt->clone());
|
||||
}
|
||||
}
|
||||
|
||||
//BBS
|
||||
const ConfigOptionFloatsNullable* nozzle_volume = config.option<ConfigOptionFloatsNullable>("nozzle_volume");
|
||||
if (nozzle_volume != nullptr) {
|
||||
@@ -3517,6 +3602,10 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
||||
if (has_scarf_joint_seam != nullptr)
|
||||
m_detect_layer_based_on_tag = m_detect_layer_based_on_tag || has_scarf_joint_seam->value;
|
||||
|
||||
const ConfigOptionBool* manual_filament_change = config.option<ConfigOptionBool>("manual_filament_change");
|
||||
if (manual_filament_change != nullptr)
|
||||
m_manual_filament_change = manual_filament_change->value;
|
||||
|
||||
const ConfigOptionEnumGeneric *bed_type = config.option<ConfigOptionEnumGeneric>("curr_bed_type");
|
||||
if (bed_type != nullptr)
|
||||
m_result.bed_type = (BedType)bed_type->value;
|
||||
@@ -3620,9 +3709,11 @@ void GCodeProcessor::reset()
|
||||
m_zero_layer_height = 0.0f;
|
||||
m_first_layer_height = 0.0f;
|
||||
m_processing_start_custom_gcode = false;
|
||||
m_in_config_block = false;
|
||||
m_g1_line_id = 0;
|
||||
m_layer_id = 0;
|
||||
m_cp_color.reset();
|
||||
m_mass_locator = nullptr;
|
||||
|
||||
m_producer = EProducer::Unknown;
|
||||
|
||||
@@ -3660,6 +3751,7 @@ DynamicConfig GCodeProcessor::export_config_for_render() const
|
||||
config.set_key_value("filament_is_support", new ConfigOptionBools(m_parser.get_config().filament_is_support.values));
|
||||
config.set_key_value("filament_type", new ConfigOptionStrings(m_parser.get_config().filament_type.values));
|
||||
config.set_key_value("filament_map", new ConfigOptionInts(m_parser.get_config().filament_map.values));
|
||||
config.apply(m_belt_render_config);
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -3761,6 +3853,7 @@ void GCodeProcessor::process_buffer(const std::string &buffer)
|
||||
void GCodeProcessor::finalize(bool post_process)
|
||||
{
|
||||
m_result.z_offset = m_z_offset;
|
||||
finalize_object_masses();
|
||||
|
||||
// update width/height of wipe moves
|
||||
for (GCodeProcessorResult::MoveVertex& move : m_result.moves) {
|
||||
@@ -3972,13 +4065,13 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line, bool
|
||||
const std::string_view cmd = line.cmd();
|
||||
if (m_flavor == gcfKlipper)
|
||||
{
|
||||
if (boost::iequals(cmd, "SET_VELOCITY_LIMIT"))
|
||||
if (ascii_iequals(cmd, "SET_VELOCITY_LIMIT"))
|
||||
{
|
||||
process_SET_VELOCITY_LIMIT(line);
|
||||
return;
|
||||
}
|
||||
// ORCA: Add Pressure Advance visualization support
|
||||
if (boost::iequals(cmd, "SET_PRESSURE_ADVANCE"))
|
||||
if (ascii_iequals(cmd, "SET_PRESSURE_ADVANCE"))
|
||||
{
|
||||
process_SET_PRESSURE_ADVANCE(line);
|
||||
return;
|
||||
@@ -4225,6 +4318,34 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
|
||||
return;
|
||||
}
|
||||
|
||||
// ;Z: -- the layer Z tag non-BBL printers write. Only read on a belt printer,
|
||||
// where the preview labels its layers with it (GCodeViewer::load_as_gcode);
|
||||
// elsewhere print_z stays unset, as it always was, so nothing downstream of
|
||||
// it changes for other printers.
|
||||
if (m_belt_printer && boost::starts_with(comment, "Z:")) {
|
||||
m_print_z = get_z_height(comment);
|
||||
return;
|
||||
}
|
||||
|
||||
if (boost::starts_with(comment, " CONFIG_BLOCK_START")) {
|
||||
m_in_config_block = true;
|
||||
return;
|
||||
}
|
||||
if (boost::starts_with(comment, " CONFIG_BLOCK_END")) {
|
||||
m_in_config_block = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Belt printer: derive the physical tilt magnitude from the slicing-rotation
|
||||
// angle header comment (used to enable the preview's belt view). Only the belt
|
||||
// header carries it outside the config block; the config block lists the key
|
||||
// for every printer, belt or not.
|
||||
if (!m_in_config_block && boost::starts_with(comment, " belt_slice_rotation_angle = ")) {
|
||||
try {
|
||||
m_result.belt_tilt_angle = std::abs(std::stof(std::string(comment.substr(29))));
|
||||
} catch (...) {}
|
||||
return;
|
||||
}
|
||||
// wipe start tag
|
||||
if (boost::starts_with(comment, reserved_tag(ETags::Wipe_Start))) {
|
||||
m_wiping = true;
|
||||
@@ -5361,6 +5482,9 @@ void GCodeProcessor::process_G1(const std::array<std::optional<double>, 4>& axes
|
||||
m_seams_detector.set_first_vertex(m_result.moves.back().position - m_extruder_offsets[filament_id] - plate_offset);
|
||||
}
|
||||
|
||||
if (type == EMoveType::Extrude)
|
||||
add_object_mass(filament_id, area_filament_cross_section * delta_pos[E]);
|
||||
|
||||
// store move
|
||||
store_move_vertex(type);
|
||||
}
|
||||
@@ -6121,6 +6245,13 @@ void GCodeProcessor::process_G92(const GCodeReader::GCodeLine& line)
|
||||
if (line.has_z()) {
|
||||
m_origin[Z] = m_end_position[Z] - line.z() * lengths_scale_factor;
|
||||
any_found = true;
|
||||
// Belt only: the start G-code's purge-blob advance + G92 Z0 resets leave a constant
|
||||
// machine-Z origin offset here; the designed-view back-transform subtracts it so
|
||||
// toolpaths map to the model's belt coordinate (gcode Z). Gated on belt_tilt_angle
|
||||
// (set from the belt header, parsed before the body) so non-belt G-code processing
|
||||
// is byte-identical — no unconditional work on the shared path.
|
||||
if (m_result.belt_tilt_angle != 0.f)
|
||||
m_result.belt_z_origin = m_origin[Z];
|
||||
}
|
||||
|
||||
if (line.has_e()) {
|
||||
@@ -7099,6 +7230,22 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type,
|
||||
m_result.print_statistics.total_travel_distance += m_travel_dist;
|
||||
}
|
||||
|
||||
// During the start G-code "prepare" stage the toolhead Z is not yet a real
|
||||
// print height on a normal printer, so it is pinned to the first-layer height
|
||||
// to keep the preview tidy. Belt printers are the exception: there the Z is
|
||||
// written explicitly by the belt kinematics and the designed-view back-transform
|
||||
// couples machine Z into the rendered model Y (the belt tilt mixes the height
|
||||
// and belt-feed axes). Overriding Z therefore back-transforms the last
|
||||
// prepare-stage move (the unretract before the first extrusion) to model
|
||||
// Y ~= 0, and the libvgcode path builder then draws a phantom extrusion
|
||||
// segment from Y ~= 0 to the first real toolpath. Keep the real Z for belt
|
||||
// printers so prepare-stage moves map correctly. Gated on belt_tilt_angle (set
|
||||
// from the G-code header before the body is processed) so non-belt processing
|
||||
// is byte-identical.
|
||||
const float store_z = (m_processing_start_custom_gcode && m_result.belt_tilt_angle == 0.f)
|
||||
? m_first_layer_height
|
||||
: m_end_position[Z] - m_z_offset;
|
||||
|
||||
m_result.moves.push_back({
|
||||
m_last_line_id,
|
||||
type,
|
||||
@@ -7106,7 +7253,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type,
|
||||
static_cast<unsigned char>(filament_id),
|
||||
m_cp_color.current,
|
||||
//BBS: add plate's offset to the rendering vertices
|
||||
Vec3f(m_end_position[X] + m_x_offset, m_end_position[Y] + m_y_offset, m_processing_start_custom_gcode ? m_first_layer_height : m_end_position[Z]- m_z_offset) + m_extruder_offsets[filament_id],
|
||||
Vec3f(m_end_position[X] + m_x_offset, m_end_position[Y] + m_y_offset, store_z) + m_extruder_offsets[filament_id],
|
||||
static_cast<float>(m_end_position[E] - m_start_position[E]),
|
||||
m_feedrate,
|
||||
0.0f, // actual feedrate
|
||||
@@ -7146,6 +7293,73 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type,
|
||||
}
|
||||
}
|
||||
|
||||
void GCodeProcessorResult::ObjectMass::add(const Sum &sum, const BoundingBoxf3 &extent, size_t layer)
|
||||
{
|
||||
box.merge(extent);
|
||||
if (printed_up_to_layer.size() <= layer)
|
||||
printed_up_to_layer.resize(layer + 1);
|
||||
printed_up_to_layer[layer].add(sum);
|
||||
}
|
||||
|
||||
void GCodeProcessor::add_object_mass(int filament_id, float volume)
|
||||
{
|
||||
// Skirt, prime tower and custom G-code belong to no object.
|
||||
const ExtrusionRole role = m_extrusion_role;
|
||||
if (volume <= 0.f || role == erNone || role == erSkirt || role == erWipeTower || role == erCustom || role == erMixed)
|
||||
return;
|
||||
|
||||
const bool has_density = size_t(filament_id) < m_result.filament_densities.size() && m_result.filament_densities[filament_id] > 0.f;
|
||||
const double mass = double(volume) * (has_density ? m_result.filament_densities[filament_id] : DEFAULT_FILAMENT_DENSITY);
|
||||
// In the frame of the stored moves, the bead's center half its height below the nozzle, from the move's start to its end.
|
||||
const Vec3d half_height = 0.5 * double(m_height) * Vec3d::UnitZ();
|
||||
const Vec3d offset = Vec3d(m_x_offset, m_y_offset, -m_z_offset) - half_height + m_extruder_offsets[filament_id].cast<double>();
|
||||
const Vec3d start = Vec3d(m_start_position[X], m_start_position[Y], m_start_position[Z]) + offset;
|
||||
const Vec3d end = Vec3d(m_end_position[X], m_end_position[Y], m_end_position[Z]) + offset;
|
||||
// The second moments of a uniform segment.
|
||||
const GCodeProcessorResult::ObjectMass::Sum sum{ mass, double(volume), 0.5 * mass * (start + end),
|
||||
mass / 3. * (start.cwiseProduct(start) + start.cwiseProduct(end) + end.cwiseProduct(end)) };
|
||||
// Of the bead's center line and its height, as its width is only estimated. Merged, as a wall along an axis is flat.
|
||||
BoundingBoxf3 extent;
|
||||
extent.merge(start.cwiseMin(end) - half_height);
|
||||
extent.merge(start.cwiseMax(end) + half_height);
|
||||
const bool part = role != erBrim && !is_support(role);
|
||||
const size_t layer = std::max<unsigned int>(1, m_layer_id) - 1;
|
||||
|
||||
m_result.plate_mass.add(sum, extent, layer);
|
||||
// The brim belongs to the plate alone.
|
||||
if (role == erBrim || !m_mass_locator)
|
||||
return;
|
||||
const auto add = [&sum, &extent, layer](std::vector<GCodeProcessorResult::ObjectMass> &masses, int index) {
|
||||
if (index < 0)
|
||||
return;
|
||||
if (masses.size() <= size_t(index))
|
||||
masses.resize(index + 1);
|
||||
masses[index].add(sum, extent, layer);
|
||||
};
|
||||
// At the nozzle's height, which the layers print at.
|
||||
const MassLocation location = m_mass_locator(0.5 * (start + end) + half_height, !part);
|
||||
if (part) {
|
||||
add(m_result.object_masses, location.object);
|
||||
add(m_result.body_masses, location.body);
|
||||
} else
|
||||
add(m_result.support_masses, location.object);
|
||||
}
|
||||
|
||||
void GCodeProcessor::finalize_object_masses()
|
||||
{
|
||||
const auto accumulate = [](GCodeProcessorResult::ObjectMass &object) {
|
||||
for (size_t i = 1; i < object.printed_up_to_layer.size(); ++i)
|
||||
object.printed_up_to_layer[i].add(object.printed_up_to_layer[i - 1]);
|
||||
};
|
||||
accumulate(m_result.plate_mass);
|
||||
for (GCodeProcessorResult::ObjectMass &object : m_result.object_masses)
|
||||
accumulate(object);
|
||||
for (GCodeProcessorResult::ObjectMass &body : m_result.body_masses)
|
||||
accumulate(body);
|
||||
for (GCodeProcessorResult::ObjectMass &support : m_result.support_masses)
|
||||
accumulate(support);
|
||||
}
|
||||
|
||||
void GCodeProcessor::set_extrusion_role(ExtrusionRole role)
|
||||
{
|
||||
m_used_filaments.process_role_cache(this);
|
||||
@@ -7516,51 +7730,72 @@ void GCodeProcessor::calculate_time(GCodeProcessorResult& result, size_t keep_la
|
||||
actual_speed_moves = std::move(machine.actual_speed_moves);
|
||||
}
|
||||
|
||||
// insert actual speed moves into the move list
|
||||
unsigned int inserted_actual_speed_moves_count = 0;
|
||||
std::vector<GCodeProcessorResult::MoveVertex> new_moves;
|
||||
std::map<unsigned int, unsigned int> id_map;
|
||||
for (auto it = actual_speed_moves.begin(); it != actual_speed_moves.end(); ++it) {
|
||||
const unsigned int base_id = it->move_id + inserted_actual_speed_moves_count;
|
||||
if (it->position.has_value()) {
|
||||
// insert actual speed move into the move list
|
||||
// clone from existing move
|
||||
GCodeProcessorResult::MoveVertex new_move = result.moves[base_id];
|
||||
// override modified parameters
|
||||
new_move.time = { 0.0f, 0.0f };
|
||||
new_move.position = *it->position;
|
||||
new_move.actual_feedrate = it->actual_feedrate;
|
||||
new_move.delta_extruder = *it->delta_extruder;
|
||||
new_move.feedrate = *it->feedrate;
|
||||
new_move.width = *it->width;
|
||||
new_move.height = *it->height;
|
||||
new_move.mm3_per_mm = *it->mm3_per_mm;
|
||||
new_move.fan_speed = *it->fan_speed;
|
||||
new_move.temperature = *it->temperature;
|
||||
new_move.internal_only = true;
|
||||
new_moves.push_back(new_move);
|
||||
// actual_speed_moves holds, per block in move order, the moves to insert before the block's move and then an
|
||||
// entry without a position for that move; positioned entries after the last such entry are dropped.
|
||||
std::vector<GCodeProcessorResult::MoveVertex>& moves = result.moves;
|
||||
size_t inserted_actual_speed_moves_count = 0;
|
||||
size_t kept = 0;
|
||||
size_t group_start = 0;
|
||||
for (size_t i = 0; i < actual_speed_moves.size(); ++i) {
|
||||
if (actual_speed_moves[i].position.has_value())
|
||||
continue;
|
||||
const unsigned int move_id = actual_speed_moves[i].move_id;
|
||||
// A VG1 block has no move of its own, so its id can fall behind the previous block's or point past the list.
|
||||
if (move_id < moves.size() && (kept == 0 || move_id > actual_speed_moves[kept - 1].move_id)) {
|
||||
inserted_actual_speed_moves_count += i - group_start;
|
||||
moves[move_id].actual_feedrate = actual_speed_moves[i].actual_feedrate;
|
||||
// A seam vertex right after a block's move shares its actual speed.
|
||||
if (move_id + 1 < moves.size() && moves[move_id + 1].type == EMoveType::Seam)
|
||||
moves[move_id + 1].actual_feedrate = actual_speed_moves[i].actual_feedrate;
|
||||
for (size_t j = group_start; j <= i; ++j, ++kept)
|
||||
if (kept != j)
|
||||
actual_speed_moves[kept] = std::move(actual_speed_moves[j]);
|
||||
}
|
||||
else {
|
||||
result.moves.insert(result.moves.begin() + base_id, new_moves.begin(), new_moves.end());
|
||||
id_map[it->move_id] = base_id + new_moves.size();
|
||||
// update move actual speed
|
||||
result.moves[base_id + new_moves.size()].actual_feedrate = it->actual_feedrate;
|
||||
inserted_actual_speed_moves_count += new_moves.size();
|
||||
// synchronize seams actual speed
|
||||
if (base_id + new_moves.size() + 1 < result.moves.size()) {
|
||||
GCodeProcessorResult::MoveVertex& move = result.moves[base_id + new_moves.size() + 1];
|
||||
if (move.type == EMoveType::Seam)
|
||||
move.actual_feedrate = it->actual_feedrate;
|
||||
}
|
||||
new_moves.clear();
|
||||
group_start = i + 1;
|
||||
}
|
||||
actual_speed_moves.erase(actual_speed_moves.begin() + kept, actual_speed_moves.end());
|
||||
|
||||
// Walks the blocks back to front, so each shifted move is moved once, into its final slot.
|
||||
size_t read = moves.size(); // one past the last move not yet placed
|
||||
moves.resize(moves.size() + inserted_actual_speed_moves_count);
|
||||
size_t write = moves.size(); // one past the last free slot
|
||||
m_actual_speed_id_map.clear();
|
||||
size_t entry = actual_speed_moves.size();
|
||||
while (entry > 0) {
|
||||
const unsigned int block_id = actual_speed_moves[--entry].move_id;
|
||||
assert(block_id < read);
|
||||
while (read > block_id + 1)
|
||||
moves[--write] = moves[--read];
|
||||
const GCodeProcessorResult::MoveVertex block_move = moves[--read];
|
||||
moves[--write] = block_move;
|
||||
m_actual_speed_id_map.emplace_back(block_id, (unsigned int)write);
|
||||
for (; entry > 0 && actual_speed_moves[entry - 1].position.has_value(); --entry) {
|
||||
const TimeMachine::ActualSpeedMove& it = actual_speed_moves[entry - 1];
|
||||
GCodeProcessorResult::MoveVertex new_move = block_move;
|
||||
new_move.time = { 0.0f, 0.0f };
|
||||
new_move.position = *it.position;
|
||||
new_move.actual_feedrate = it.actual_feedrate;
|
||||
new_move.delta_extruder = *it.delta_extruder;
|
||||
new_move.feedrate = *it.feedrate;
|
||||
new_move.width = *it.width;
|
||||
new_move.height = *it.height;
|
||||
new_move.mm3_per_mm = *it.mm3_per_mm;
|
||||
new_move.fan_speed = *it.fan_speed;
|
||||
new_move.temperature = *it.temperature;
|
||||
new_move.internal_only = true;
|
||||
moves[--write] = new_move;
|
||||
}
|
||||
}
|
||||
assert(read == write);
|
||||
|
||||
// synchronize blocks' move_ids with after moves for actual speed insertion
|
||||
std::reverse(m_actual_speed_id_map.begin(), m_actual_speed_id_map.end());
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count); ++i) {
|
||||
for (GCodeProcessor::TimeBlock& block : m_time_processor.machines[i].blocks) {
|
||||
auto it = id_map.find(block.move_id);
|
||||
block.move_id = (it != id_map.end()) ? it->second : block.move_id + inserted_actual_speed_moves_count;
|
||||
auto it = std::lower_bound(m_actual_speed_id_map.begin(), m_actual_speed_id_map.end(), block.move_id,
|
||||
[](const std::pair<unsigned int, unsigned int>& entry, unsigned int id) { return entry.first < id; });
|
||||
block.move_id = (it != m_actual_speed_id_map.end() && it->first == block.move_id) ?
|
||||
it->second : block.move_id + (unsigned int)inserted_actual_speed_moves_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user