PrusaSlicer/src/slic3r/GUI/LibVGCode/LibVGCodeWrapper.cpp
2024-07-08 16:57:26 +02:00

789 lines
40 KiB
C++

///|/ Copyright (c) Prusa Research 2020 - 2023 Enrico Turri @enricoturri1966
///|/
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
///|/
#include <assert.h>
#include <stdint.h>
#include <Eigen/Geometry>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iterator>
#include "libslic3r/libslic3r.h"
#include "LibVGCodeWrapper.hpp"
#include "libslic3r/Print.hpp"
#include "libslic3r/Color.hpp"
#include "libslic3r/CustomGCode.hpp"
#include "libslic3r/Exception.hpp"
#include "libslic3r/ExtrusionEntity.hpp"
#include "libslic3r/ExtrusionEntityCollection.hpp"
#include "libslic3r/GCode/WipeTower.hpp"
#include "libslic3r/Layer.hpp"
#include "libslic3r/LayerRegion.hpp"
#include "libslic3r/Line.hpp"
#include "libslic3r/Polyline.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "../../src/libvgcode/include/GCodeInputData.hpp"
#include "../../src/libvgcode/include/PathVertex.hpp"
namespace libvgcode {
class Viewer;
Vec3 convert(const Slic3r::Vec3f& v)
{
return { v.x(), v.y(), v.z() };
}
Slic3r::Vec3f convert(const Vec3& v)
{
return { v[0], v[1], v[2] };
}
Mat4x4 convert(const Slic3r::Matrix4f& m)
{
Mat4x4 ret;
std::memcpy(ret.data(), m.data(), 16 * sizeof(float));
return ret;
}
Slic3r::ColorRGBA convert(const Color& c)
{
static const float inv_255 = 1.0f / 255.0f;
return { c[0] * inv_255, c[1] * inv_255, c[2] * inv_255, 1.0f };
}
Color convert(const Slic3r::ColorRGBA& c)
{
return { static_cast<uint8_t>(c.r() * 255.0f), static_cast<uint8_t>(c.g() * 255.0f), static_cast<uint8_t>(c.b() * 255.0f) };
}
Color convert(const std::string& color_str)
{
Slic3r::ColorRGBA color_rgba;
return decode_color(color_str, color_rgba) ? convert(color_rgba) : DUMMY_COLOR;
}
Slic3r::GCodeExtrusionRole convert(EGCodeExtrusionRole role)
{
switch (role)
{
case EGCodeExtrusionRole::None: { return Slic3r::GCodeExtrusionRole::None; }
case EGCodeExtrusionRole::Perimeter: { return Slic3r::GCodeExtrusionRole::Perimeter; }
case EGCodeExtrusionRole::ExternalPerimeter: { return Slic3r::GCodeExtrusionRole::ExternalPerimeter; }
case EGCodeExtrusionRole::OverhangPerimeter: { return Slic3r::GCodeExtrusionRole::OverhangPerimeter; }
case EGCodeExtrusionRole::InternalInfill: { return Slic3r::GCodeExtrusionRole::InternalInfill; }
case EGCodeExtrusionRole::SolidInfill: { return Slic3r::GCodeExtrusionRole::SolidInfill; }
case EGCodeExtrusionRole::TopSolidInfill: { return Slic3r::GCodeExtrusionRole::TopSolidInfill; }
case EGCodeExtrusionRole::Ironing: { return Slic3r::GCodeExtrusionRole::Ironing; }
case EGCodeExtrusionRole::BridgeInfill: { return Slic3r::GCodeExtrusionRole::BridgeInfill; }
case EGCodeExtrusionRole::GapFill: { return Slic3r::GCodeExtrusionRole::GapFill; }
case EGCodeExtrusionRole::Skirt: { return Slic3r::GCodeExtrusionRole::Skirt; }
case EGCodeExtrusionRole::SupportMaterial: { return Slic3r::GCodeExtrusionRole::SupportMaterial; }
case EGCodeExtrusionRole::SupportMaterialInterface: { return Slic3r::GCodeExtrusionRole::SupportMaterialInterface; }
case EGCodeExtrusionRole::WipeTower: { return Slic3r::GCodeExtrusionRole::WipeTower; }
case EGCodeExtrusionRole::Custom: { return Slic3r::GCodeExtrusionRole::Custom; }
default: { return Slic3r::GCodeExtrusionRole::None; }
}
}
EGCodeExtrusionRole convert(Slic3r::GCodeExtrusionRole role)
{
switch (role)
{
case Slic3r::GCodeExtrusionRole::None: { return EGCodeExtrusionRole::None; }
case Slic3r::GCodeExtrusionRole::Perimeter: { return EGCodeExtrusionRole::Perimeter; }
case Slic3r::GCodeExtrusionRole::ExternalPerimeter: { return EGCodeExtrusionRole::ExternalPerimeter; }
case Slic3r::GCodeExtrusionRole::OverhangPerimeter: { return EGCodeExtrusionRole::OverhangPerimeter; }
case Slic3r::GCodeExtrusionRole::InternalInfill: { return EGCodeExtrusionRole::InternalInfill; }
case Slic3r::GCodeExtrusionRole::SolidInfill: { return EGCodeExtrusionRole::SolidInfill; }
case Slic3r::GCodeExtrusionRole::TopSolidInfill: { return EGCodeExtrusionRole::TopSolidInfill; }
case Slic3r::GCodeExtrusionRole::Ironing: { return EGCodeExtrusionRole::Ironing; }
case Slic3r::GCodeExtrusionRole::BridgeInfill: { return EGCodeExtrusionRole::BridgeInfill; }
case Slic3r::GCodeExtrusionRole::GapFill: { return EGCodeExtrusionRole::GapFill; }
case Slic3r::GCodeExtrusionRole::Skirt: { return EGCodeExtrusionRole::Skirt; }
case Slic3r::GCodeExtrusionRole::SupportMaterial: { return EGCodeExtrusionRole::SupportMaterial; }
case Slic3r::GCodeExtrusionRole::SupportMaterialInterface: { return EGCodeExtrusionRole::SupportMaterialInterface; }
case Slic3r::GCodeExtrusionRole::WipeTower: { return EGCodeExtrusionRole::WipeTower; }
case Slic3r::GCodeExtrusionRole::Custom: { return EGCodeExtrusionRole::Custom; }
default: { return EGCodeExtrusionRole::None; }
}
}
EMoveType convert(Slic3r::EMoveType type)
{
switch (type)
{
case Slic3r::EMoveType::Noop: { return EMoveType::Noop; }
case Slic3r::EMoveType::Retract: { return EMoveType::Retract; }
case Slic3r::EMoveType::Unretract: { return EMoveType::Unretract; }
case Slic3r::EMoveType::Seam: { return EMoveType::Seam; }
case Slic3r::EMoveType::Tool_change: { return EMoveType::ToolChange; }
case Slic3r::EMoveType::Color_change: { return EMoveType::ColorChange; }
case Slic3r::EMoveType::Pause_Print: { return EMoveType::PausePrint; }
case Slic3r::EMoveType::Custom_GCode: { return EMoveType::CustomGCode; }
case Slic3r::EMoveType::Travel: { return EMoveType::Travel; }
case Slic3r::EMoveType::Wipe: { return EMoveType::Wipe; }
case Slic3r::EMoveType::Extrude: { return EMoveType::Extrude; }
default: { return EMoveType::COUNT; }
}
}
EOptionType convert(const Slic3r::GUI::Preview::OptionType& type)
{
switch (type)
{
case Slic3r::GUI::Preview::OptionType::Travel: { return EOptionType::Travels; }
case Slic3r::GUI::Preview::OptionType::Wipe: { return EOptionType::Wipes; }
case Slic3r::GUI::Preview::OptionType::Retractions: { return EOptionType::Retractions; }
case Slic3r::GUI::Preview::OptionType::Unretractions: { return EOptionType::Unretractions; }
case Slic3r::GUI::Preview::OptionType::Seams: { return EOptionType::Seams; }
case Slic3r::GUI::Preview::OptionType::ToolChanges: { return EOptionType::ToolChanges; }
case Slic3r::GUI::Preview::OptionType::ColorChanges: { return EOptionType::ColorChanges; }
case Slic3r::GUI::Preview::OptionType::PausePrints: { return EOptionType::PausePrints; }
case Slic3r::GUI::Preview::OptionType::CustomGCodes: { return EOptionType::CustomGCodes; }
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
case Slic3r::GUI::Preview::OptionType::CenterOfGravity: { return EOptionType::CenterOfGravity; }
case Slic3r::GUI::Preview::OptionType::ToolMarker: { return EOptionType::ToolMarker; }
#else
case Slic3r::GUI::Preview::OptionType::CenterOfGravity: { return EOptionType::COUNT; }
case Slic3r::GUI::Preview::OptionType::ToolMarker: { return EOptionType::COUNT; }
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
default: { return EOptionType::COUNT; }
}
}
ETimeMode convert(const Slic3r::PrintEstimatedStatistics::ETimeMode& mode)
{
switch (mode)
{
case Slic3r::PrintEstimatedStatistics::ETimeMode::Normal: { return ETimeMode::Normal; }
case Slic3r::PrintEstimatedStatistics::ETimeMode::Stealth: { return ETimeMode::Stealth; }
default: { return ETimeMode::COUNT; }
}
}
Slic3r::PrintEstimatedStatistics::ETimeMode convert(const ETimeMode& mode)
{
switch (mode)
{
case ETimeMode::Normal: { return Slic3r::PrintEstimatedStatistics::ETimeMode::Normal; }
case ETimeMode::Stealth: { return Slic3r::PrintEstimatedStatistics::ETimeMode::Stealth; }
default: { return Slic3r::PrintEstimatedStatistics::ETimeMode::Count; }
}
}
GCodeInputData convert(const Slic3r::GCodeProcessorResult& result, const std::vector<std::string>& str_tool_colors,
const std::vector<std::string>& str_color_print_colors, const Viewer& viewer)
{
GCodeInputData ret;
// collect tool colors
ret.tools_colors.reserve(str_tool_colors.size());
for (const std::string& color : str_tool_colors) {
ret.tools_colors.emplace_back(convert(color));
}
// collect color print colors
const std::vector<std::string>& str_colors = str_color_print_colors.empty() ? str_tool_colors : str_color_print_colors;
ret.color_print_colors.reserve(str_colors.size());
for (const std::string& color : str_colors) {
ret.color_print_colors.emplace_back(convert(color));
}
const std::vector<Slic3r::GCodeProcessorResult::MoveVertex>& moves = result.moves;
ret.vertices.reserve(2 * moves.size());
for (size_t i = 1; i < moves.size(); ++i) {
const Slic3r::GCodeProcessorResult::MoveVertex& curr = moves[i];
const Slic3r::GCodeProcessorResult::MoveVertex& prev = moves[i - 1];
const EMoveType curr_type = convert(curr.type);
const EOptionType option_type = move_type_to_option(curr_type);
if (option_type == EOptionType::COUNT || option_type == EOptionType::Travels || option_type == EOptionType::Wipes) {
if (ret.vertices.empty() || prev.type != curr.type || prev.extrusion_role != curr.extrusion_role) {
// to allow libvgcode to properly detect the start/end of a path we need to add a 'phantom' vertex
// equal to the current one with the exception of the position, which should match the previous move position,
// and the times, which are set to zero
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
const libvgcode::PathVertex vertex = { convert(prev.position), curr.height, curr.width, curr.feedrate, prev.actual_feedrate,
curr.mm3_per_mm, curr.fan_speed, curr.temperature, 0.0f, convert(curr.extrusion_role), curr_type,
static_cast<uint32_t>(curr.gcode_id), static_cast<uint32_t>(curr.layer_id),
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), { 0.0f, 0.0f } };
#else
const libvgcode::PathVertex vertex = { convert(prev.position), curr.height, curr.width, curr.feedrate, prev.actual_feedrate,
curr.mm3_per_mm, curr.fan_speed, curr.temperature, convert(curr.extrusion_role), curr_type,
static_cast<uint32_t>(curr.gcode_id), static_cast<uint32_t>(curr.layer_id),
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), { 0.0f, 0.0f } };
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
ret.vertices.emplace_back(vertex);
}
}
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
const libvgcode::PathVertex vertex = { convert(curr.position), curr.height, curr.width, curr.feedrate, curr.actual_feedrate,
curr.mm3_per_mm, curr.fan_speed, curr.temperature,
result.filament_densities[curr.extruder_id] * curr.mm3_per_mm * (curr.position - prev.position).norm(),
convert(curr.extrusion_role), curr_type, static_cast<uint32_t>(curr.gcode_id), static_cast<uint32_t>(curr.layer_id),
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), curr.time };
#else
const libvgcode::PathVertex vertex = { convert(curr.position), curr.height, curr.width, curr.feedrate, curr.actual_feedrate,
curr.mm3_per_mm, curr.fan_speed, curr.temperature, convert(curr.extrusion_role), curr_type,
static_cast<uint32_t>(curr.gcode_id), static_cast<uint32_t>(curr.layer_id),
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), curr.time };
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
ret.vertices.emplace_back(vertex);
}
ret.vertices.shrink_to_fit();
ret.spiral_vase_mode = result.spiral_vase_mode;
return ret;
}
static void convert_lines_to_vertices(const Slic3r::Lines& lines, const std::vector<float>& widths, const std::vector<float>& heights,
float top_z, size_t layer_id, size_t extruder_id, size_t color_id, EGCodeExtrusionRole extrusion_role, bool closed, std::vector<PathVertex>& vertices)
{
if (lines.empty())
return;
// loop once more in case of closed loops
const size_t lines_end = closed ? (lines.size() + 1) : lines.size();
for (size_t ii = 0; ii < lines_end; ++ii) {
const size_t i = (ii == lines.size()) ? 0 : ii;
const Slic3r::Line& line = lines[i];
// first segment of the polyline
if (ii == 0) {
// add a dummy vertex at the start, to separate the current line from the others
const Slic3r::Vec2f a = unscale(line.a).cast<float>();
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
libvgcode::PathVertex vertex = { convert(Slic3r::Vec3f(a.x(), a.y(), top_z)), heights[i], widths[i], 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, extrusion_role, EMoveType::Noop, 0, static_cast<uint32_t>(layer_id),
static_cast<uint8_t>(extruder_id), static_cast<uint8_t>(color_id), { 0.0f, 0.0f } };
#else
libvgcode::PathVertex vertex = { convert(Slic3r::Vec3f(a.x(), a.y(), top_z)), heights[i], widths[i], 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, extrusion_role, EMoveType::Noop, 0, static_cast<uint32_t>(layer_id),
static_cast<uint8_t>(extruder_id), static_cast<uint8_t>(color_id), { 0.0f, 0.0f } };
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
vertices.emplace_back(vertex);
// add the starting vertex of the segment
vertex.type = EMoveType::Extrude;
vertices.emplace_back(vertex);
}
// add the ending vertex of the segment
const Slic3r::Vec2f b = unscale(line.b).cast<float>();
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
const libvgcode::PathVertex vertex = { convert(Slic3r::Vec3f(b.x(), b.y(), top_z)), heights[i], widths[i], 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f, extrusion_role, EMoveType::Extrude, 0, static_cast<uint32_t>(layer_id),
static_cast<uint8_t>(extruder_id), static_cast<uint8_t>(color_id), { 0.0f, 0.0f } };
#else
const libvgcode::PathVertex vertex = { convert(Slic3r::Vec3f(b.x(), b.y(), top_z)), heights[i], widths[i], 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, extrusion_role, EMoveType::Extrude, 0, static_cast<uint32_t>(layer_id),
static_cast<uint8_t>(extruder_id), static_cast<uint8_t>(color_id), { 0.0f, 0.0f } };
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
vertices.emplace_back(vertex);
}
}
static void convert_to_vertices(const Slic3r::ExtrusionPath& extrusion_path, float print_z, size_t layer_id, size_t extruder_id, size_t color_id,
EGCodeExtrusionRole extrusion_role, const Slic3r::Point& shift, std::vector<PathVertex>& vertices)
{
Slic3r::Polyline polyline = extrusion_path.polyline;
polyline.remove_duplicate_points();
polyline.translate(shift);
const Slic3r::Lines lines = polyline.lines();
std::vector<float> widths(lines.size(), extrusion_path.width());
std::vector<float> heights(lines.size(), extrusion_path.height());
convert_lines_to_vertices(lines, widths, heights, print_z, layer_id, extruder_id, color_id, extrusion_role, false, vertices);
}
static void convert_to_vertices(const Slic3r::ExtrusionMultiPath& extrusion_multi_path, float print_z, size_t layer_id, size_t extruder_id,
size_t color_id, EGCodeExtrusionRole extrusion_role, const Slic3r::Point& shift, std::vector<PathVertex>& vertices)
{
Slic3r::Lines lines;
std::vector<float> widths;
std::vector<float> heights;
for (const Slic3r::ExtrusionPath& extrusion_path : extrusion_multi_path.paths) {
Slic3r::Polyline polyline = extrusion_path.polyline;
polyline.remove_duplicate_points();
polyline.translate(shift);
const Slic3r::Lines lines_this = polyline.lines();
append(lines, lines_this);
widths.insert(widths.end(), lines_this.size(), extrusion_path.width());
heights.insert(heights.end(), lines_this.size(), extrusion_path.height());
}
convert_lines_to_vertices(lines, widths, heights, print_z, layer_id, extruder_id, color_id, extrusion_role, false, vertices);
}
static void convert_to_vertices(const Slic3r::ExtrusionLoop& extrusion_loop, float print_z, size_t layer_id, size_t extruder_id, size_t color_id,
EGCodeExtrusionRole extrusion_role, const Slic3r::Point& shift, std::vector<PathVertex>& vertices)
{
Slic3r::Lines lines;
std::vector<float> widths;
std::vector<float> heights;
for (const Slic3r::ExtrusionPath& extrusion_path : extrusion_loop.paths) {
Slic3r::Polyline polyline = extrusion_path.polyline;
polyline.remove_duplicate_points();
polyline.translate(shift);
const Slic3r::Lines lines_this = polyline.lines();
append(lines, lines_this);
widths.insert(widths.end(), lines_this.size(), extrusion_path.width());
heights.insert(heights.end(), lines_this.size(), extrusion_path.height());
}
convert_lines_to_vertices(lines, widths, heights, print_z, layer_id, extruder_id, color_id, extrusion_role, true, vertices);
}
// forward declaration
static void convert_to_vertices(const Slic3r::ExtrusionEntityCollection& extrusion_entity_collection, float print_z, size_t layer_id,
size_t extruder_id, size_t color_id, EGCodeExtrusionRole extrusion_role, const Slic3r::Point& shift, std::vector<PathVertex>& vertices);
static void convert_to_vertices(const Slic3r::ExtrusionEntity& extrusion_entity, float print_z, size_t layer_id, size_t extruder_id, size_t color_id,
EGCodeExtrusionRole extrusion_role, const Slic3r::Point& shift, std::vector<PathVertex>& vertices)
{
auto* extrusion_path = dynamic_cast<const Slic3r::ExtrusionPath*>(&extrusion_entity);
if (extrusion_path != nullptr)
convert_to_vertices(*extrusion_path, print_z, layer_id, extruder_id, color_id, extrusion_role, shift, vertices);
else {
auto* extrusion_loop = dynamic_cast<const Slic3r::ExtrusionLoop*>(&extrusion_entity);
if (extrusion_loop != nullptr)
convert_to_vertices(*extrusion_loop, print_z, layer_id, extruder_id, color_id, extrusion_role, shift, vertices);
else {
auto* extrusion_multi_path = dynamic_cast<const Slic3r::ExtrusionMultiPath*>(&extrusion_entity);
if (extrusion_multi_path != nullptr)
convert_to_vertices(*extrusion_multi_path, print_z, layer_id, extruder_id, color_id, extrusion_role, shift, vertices);
else {
auto* extrusion_entity_collection = dynamic_cast<const Slic3r::ExtrusionEntityCollection*>(&extrusion_entity);
if (extrusion_entity_collection != nullptr)
convert_to_vertices(*extrusion_entity_collection, print_z, layer_id, extruder_id, color_id, extrusion_role, shift, vertices);
else
throw Slic3r::RuntimeError("Found unexpected extrusion_entity type");
}
}
}
}
static void convert_to_vertices(const Slic3r::ExtrusionEntityCollection& extrusion_entity_collection, float print_z, size_t layer_id,
size_t extruder_id, size_t color_id, EGCodeExtrusionRole extrusion_role, const Slic3r::Point& shift, std::vector<PathVertex>& vertices)
{
for (const Slic3r::ExtrusionEntity* extrusion_entity : extrusion_entity_collection.entities) {
if (extrusion_entity != nullptr)
convert_to_vertices(*extrusion_entity, print_z, layer_id, extruder_id, color_id, extrusion_role, shift, vertices);
}
}
struct VerticesData
{
std::vector<PathVertex> vertices;
std::vector<float> layers_zs;
};
static void convert_brim_skirt_to_vertices(const Slic3r::Print& print, std::vector<VerticesData>& vertices_data)
{
vertices_data.emplace_back(VerticesData());
VerticesData& data = vertices_data.back();
// number of skirt layers
size_t total_layer_count = 0;
for (const Slic3r::PrintObject* print_object : print.objects()) {
total_layer_count = std::max(total_layer_count, print_object->total_layer_count());
}
size_t skirt_height = print.has_infinite_skirt() ? total_layer_count : std::min<size_t>(print.config().skirt_height.value, total_layer_count);
if (skirt_height == 0 && print.has_brim())
skirt_height = 1;
// Get first skirt_height layers.
//FIXME This code is fishy. It may not work for multiple objects with different layering due to variable layer height feature.
// This is not critical as this is just an initial preview.
const Slic3r::PrintObject* highest_object = *std::max_element(print.objects().begin(), print.objects().end(),
[](auto l, auto r) { return l->layers().size() < r->layers().size(); });
data.layers_zs.reserve(skirt_height * 2);
for (size_t i = 0; i < std::min(skirt_height, highest_object->layers().size()); ++i) {
data.layers_zs.emplace_back(float(highest_object->layers()[i]->print_z));
}
// Only add skirt for the raft layers.
for (size_t i = 0; i < std::min(skirt_height, std::min(highest_object->slicing_parameters().raft_layers(), highest_object->support_layers().size())); ++i) {
data.layers_zs.emplace_back(float(highest_object->support_layers()[i]->print_z));
}
Slic3r::sort_remove_duplicates(data.layers_zs);
skirt_height = std::min(skirt_height, data.layers_zs.size());
data.layers_zs.erase(data.layers_zs.begin() + skirt_height, data.layers_zs.end());
for (size_t i = 0; i < skirt_height; ++i) {
if (i == 0)
convert_to_vertices(print.brim(), data.layers_zs[i], i, 0, 0, EGCodeExtrusionRole::Skirt, Slic3r::Point(0, 0), data.vertices);
convert_to_vertices(print.skirt(), data.layers_zs[i], i, 0, 0, EGCodeExtrusionRole::Skirt, Slic3r::Point(0, 0), data.vertices);
}
}
class WipeTowerHelper
{
public:
WipeTowerHelper(const Slic3r::Print& print) : m_print(print) {
const Slic3r::PrintConfig& config = m_print.config();
const Slic3r::WipeTowerData& wipe_tower_data = m_print.wipe_tower_data();
if (wipe_tower_data.priming && config.single_extruder_multi_material_priming) {
for (size_t i = 0; i < wipe_tower_data.priming.get()->size(); ++i) {
m_priming.emplace_back(wipe_tower_data.priming.get()->at(i));
}
}
if (wipe_tower_data.final_purge)
m_final.emplace_back(*wipe_tower_data.final_purge.get());
m_angle = config.wipe_tower_rotation_angle.value / 180.0f * PI;
m_position = Slic3r::Vec2f(config.wipe_tower_x.value, config.wipe_tower_y.value);
m_layers_count = wipe_tower_data.tool_changes.size() + (m_priming.empty() ? 0 : 1);
}
const std::vector<Slic3r::WipeTower::ToolChangeResult>& tool_change(size_t idx) {
const auto& tool_changes = m_print.wipe_tower_data().tool_changes;
return m_priming.empty() ?
((idx == tool_changes.size()) ? m_final : tool_changes[idx]) :
((idx == 0) ? m_priming : (idx == tool_changes.size() + 1) ? m_final : tool_changes[idx - 1]);
}
float get_angle() const { return m_angle; }
const Slic3r::Vec2f& get_position() const { return m_position; }
size_t get_layers_count() { return m_layers_count; }
private:
const Slic3r::Print& m_print;
std::vector<Slic3r::WipeTower::ToolChangeResult> m_priming;
std::vector<Slic3r::WipeTower::ToolChangeResult> m_final;
Slic3r::Vec2f m_position{ Slic3r::Vec2f::Zero() };
float m_angle{ 0.0f };
size_t m_layers_count{ 0 };
};
static void convert_wipe_tower_to_vertices(const Slic3r::Print& print, const std::vector<std::string>& str_tool_colors,
std::vector<VerticesData>& vertices_data)
{
vertices_data.emplace_back(VerticesData());
VerticesData& data = vertices_data.back();
WipeTowerHelper wipe_tower_helper(print);
const float angle = wipe_tower_helper.get_angle();
const Slic3r::Vec2f& position = wipe_tower_helper.get_position();
for (size_t item = 0; item < wipe_tower_helper.get_layers_count(); ++item) {
const std::vector<Slic3r::WipeTower::ToolChangeResult>& layer = wipe_tower_helper.tool_change(item);
for (const Slic3r::WipeTower::ToolChangeResult& extrusions : layer) {
data.layers_zs.emplace_back(extrusions.print_z);
for (size_t i = 1; i < extrusions.extrusions.size(); /*no increment*/) {
const Slic3r::WipeTower::Extrusion& e = extrusions.extrusions[i];
if (e.width == 0.0f) {
++i;
continue;
}
size_t j = i + 1;
if (str_tool_colors.empty())
for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].width > 0.0f; ++j);
else
for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].tool == e.tool && extrusions.extrusions[j].width > 0.0f; ++j);
const size_t n_lines = j - i;
Slic3r::Lines lines;
std::vector<float> widths;
std::vector<float> heights;
lines.reserve(n_lines);
widths.reserve(n_lines);
heights.assign(n_lines, extrusions.layer_height);
Slic3r::WipeTower::Extrusion e_prev = extrusions.extrusions[i - 1];
if (!extrusions.priming) { // wipe tower extrusions describe the wipe tower at the origin with no rotation
e_prev.pos = Eigen::Rotation2Df(angle) * e_prev.pos;
e_prev.pos += position;
}
for (; i < j; ++i) {
Slic3r::WipeTower::Extrusion ee = extrusions.extrusions[i];
assert(ee.width > 0.0f);
if (!extrusions.priming) {
ee.pos = Eigen::Rotation2Df(angle) * ee.pos;
ee.pos += position;
}
lines.emplace_back(Slic3r::Point::new_scale(e_prev.pos.x(), e_prev.pos.y()), Slic3r::Point::new_scale(ee.pos.x(), ee.pos.y()));
widths.emplace_back(ee.width);
e_prev = ee;
}
convert_lines_to_vertices(lines, widths, heights, extrusions.print_z, item, static_cast<size_t>(e.tool), 0,
EGCodeExtrusionRole::WipeTower, lines.front().a == lines.back().b, data.vertices);
}
}
}
Slic3r::sort_remove_duplicates(data.layers_zs);
}
class ObjectHelper
{
public:
ObjectHelper(const std::vector<Slic3r::CustomGCode::Item>& color_print_values, size_t tool_colors_count, size_t color_print_colors_count, size_t extruders_count)
: m_color_print_values(color_print_values)
, m_tool_colors_count(tool_colors_count)
, m_color_print_colors_count(color_print_colors_count)
, m_extruders_count(extruders_count) {
}
uint8_t color_id(double print_z, size_t extruder_id) const {
if (!m_color_print_values.empty())
return color_print_color_id(print_z, extruder_id);
else {
if (m_tool_colors_count > 0)
return std::min<uint8_t>(m_tool_colors_count - 1, static_cast<uint8_t>(extruder_id));
else
return 0;
}
}
private:
const std::vector<Slic3r::CustomGCode::Item>& m_color_print_values;
size_t m_tool_colors_count{ 0 };
size_t m_color_print_colors_count{ 0 };
size_t m_extruders_count{ 0 };
uint8_t color_print_color_id(double print_z, size_t extruder_id) const {
auto it = std::find_if(m_color_print_values.begin(), m_color_print_values.end(),
[print_z](const Slic3r::CustomGCode::Item& code) {
return std::fabs(code.print_z - print_z) < EPSILON;
});
if (it != m_color_print_values.end()) {
Slic3r::CustomGCode::Type type = it->type;
// pause print or custom Gcode
if (type == Slic3r::CustomGCode::PausePrint || (type != Slic3r::CustomGCode::ColorChange && type != Slic3r::CustomGCode::Template))
return static_cast<uint8_t>(m_color_print_colors_count - 1); // last color item is a gray color for pause print or custom G-code
switch (it->type) {
// change color for current extruder
case Slic3r::CustomGCode::ColorChange: {
const int c = color_change_color_id(it, extruder_id);
if (c >= 0)
return static_cast<uint8_t>(c);
break;
}
// change tool (extruder)
case Slic3r::CustomGCode::ToolChange: { return tool_change_color_id(it, extruder_id); }
default: { break; }
}
}
const Slic3r::CustomGCode::Item value{ print_z + EPSILON, Slic3r::CustomGCode::Custom, 0, "" };
it = std::lower_bound(m_color_print_values.begin(), m_color_print_values.end(), value);
while (it != m_color_print_values.begin()) {
--it;
switch (it->type) {
// change color for current extruder
case Slic3r::CustomGCode::ColorChange: {
const int c = color_change_color_id(it, extruder_id);
if (c >= 0)
return static_cast<uint8_t>(c);
break;
}
// change tool (extruder)
case Slic3r::CustomGCode::ToolChange: { return tool_change_color_id(it, extruder_id); }
default: { break; }
}
}
return std::min<uint8_t>(m_extruders_count - 1, static_cast<uint8_t>(extruder_id));
}
int color_change_color_id(std::vector<Slic3r::CustomGCode::Item>::const_iterator it, size_t extruder_id) const {
if (m_extruders_count == 1)
return m600_color_id(it);
auto it_n = it;
bool is_tool_change = false;
while (it_n != m_color_print_values.begin()) {
--it_n;
if (it_n->type == Slic3r::CustomGCode::ToolChange) {
is_tool_change = true;
if (it_n->extruder == it->extruder || (it_n->extruder == 0 && it->extruder == static_cast<int>(extruder_id + 1)))
return m600_color_id(it);
break;
}
}
if (!is_tool_change && it->extruder == static_cast<int>(extruder_id + 1))
return m600_color_id(it);
return -1;
}
uint8_t tool_change_color_id(std::vector<Slic3r::CustomGCode::Item>::const_iterator it, size_t extruder_id) const {
const int current_extruder = it->extruder == 0 ? static_cast<int>(extruder_id + 1) : it->extruder;
if (m_tool_colors_count == m_extruders_count + 1) // there is no one "M600"
return std::min<uint8_t>(m_extruders_count - 1, std::max<uint8_t>(current_extruder - 1, 0));
auto it_n = it;
while (it_n != m_color_print_values.begin()) {
--it_n;
if (it_n->type == Slic3r::CustomGCode::ColorChange && it_n->extruder == current_extruder)
return m600_color_id(it_n);
}
return std::min<uint8_t>(m_extruders_count - 1, std::max<uint8_t>(current_extruder - 1, 0));
}
int m600_color_id(std::vector<Slic3r::CustomGCode::Item>::const_iterator it) const {
int shift = 0;
while (it != m_color_print_values.begin()) {
--it;
if (it->type == Slic3r::CustomGCode::ColorChange)
++shift;
}
return static_cast<int>(m_extruders_count) + shift;
}
};
static void convert_object_to_vertices(const Slic3r::PrintObject& object, const std::vector<std::string>& str_tool_colors,
const std::vector<std::string>& str_color_print_colors, const std::vector<Slic3r::CustomGCode::Item>& color_print_values,
size_t extruders_count, VerticesData& data)
{
const bool has_perimeters = object.is_step_done(Slic3r::posPerimeters);
const bool has_infill = object.is_step_done(Slic3r::posInfill);
const bool has_support = object.is_step_done(Slic3r::posSupportMaterial);
// order layers by print_z
std::vector<const Slic3r::Layer*> layers;
if (has_perimeters || has_infill) {
layers.reserve(layers.size() + object.layers().size());
std::copy(object.layers().begin(), object.layers().end(), std::back_inserter(layers));
}
if (has_support) {
layers.reserve(layers.size() + object.support_layers().size());
std::copy(object.support_layers().begin(), object.support_layers().end(), std::back_inserter(layers));
}
std::sort(layers.begin(), layers.end(), [](const Slic3r::Layer* l1, const Slic3r::Layer* l2) { return l1->print_z < l2->print_z; });
ObjectHelper object_helper(color_print_values, str_tool_colors.size(), str_color_print_colors.size(), extruders_count);
data.layers_zs.reserve(layers.size());
for (const Slic3r::Layer* layer : layers) {
data.layers_zs.emplace_back(static_cast<float>(layer->print_z));
}
for (size_t i = 0; i < layers.size(); ++i) {
const Slic3r::Layer* layer = layers[i];
for (const Slic3r::PrintInstance& instance : object.instances()) {
const Slic3r::Point& copy = instance.shift;
for (const Slic3r::LayerRegion* layerm : layer->regions()) {
if (layerm->slices().empty())
continue;
const Slic3r::PrintRegionConfig& cfg = layerm->region().config();
if (has_perimeters) {
const size_t extruder_id = static_cast<size_t>(std::max(cfg.perimeter_extruder.value - 1, 0));
convert_to_vertices(layerm->perimeters(), static_cast<float>(layer->print_z), i, extruder_id, object_helper.color_id(layer->print_z, extruder_id),
EGCodeExtrusionRole::ExternalPerimeter, copy, data.vertices);
}
if (has_infill) {
for (const Slic3r::ExtrusionEntity* ee : layerm->fills()) {
// fill represents infill extrusions of a single island.
const auto& fill = *dynamic_cast<const Slic3r::ExtrusionEntityCollection*>(ee);
if (!fill.entities.empty()) {
const bool is_solid_infill = fill.entities.front()->role().is_solid_infill();
const size_t extruder_id = is_solid_infill ?
static_cast<size_t>(std::max(cfg.solid_infill_extruder.value - 1, 0)) :
static_cast<size_t>(std::max(cfg.infill_extruder.value - 1, 0));
convert_to_vertices(fill, static_cast<float>(layer->print_z), i, extruder_id, object_helper.color_id(layer->print_z, extruder_id),
is_solid_infill ? EGCodeExtrusionRole::SolidInfill : EGCodeExtrusionRole::InternalInfill,
copy, data.vertices);
}
}
}
}
if (has_support) {
const Slic3r::SupportLayer* support_layer = dynamic_cast<const Slic3r::SupportLayer*>(layer);
if (support_layer == nullptr)
continue;
const Slic3r::PrintObjectConfig& cfg = support_layer->object()->config();
for (const Slic3r::ExtrusionEntity* extrusion_entity : support_layer->support_fills.entities) {
const bool is_support_material = extrusion_entity->role() == Slic3r::ExtrusionRole::SupportMaterial;
const size_t extruder_id = is_support_material ?
static_cast<size_t>(std::max(cfg.support_material_extruder.value - 1, 0)) :
static_cast<size_t>(std::max(cfg.support_material_interface_extruder.value - 1, 0));
convert_to_vertices(*extrusion_entity, static_cast<float>(layer->print_z), i,
extruder_id, object_helper.color_id(layer->print_z, extruder_id),
is_support_material ? EGCodeExtrusionRole::SupportMaterial : EGCodeExtrusionRole::SupportMaterialInterface,
copy, data.vertices);
}
}
}
}
}
static void convert_objects_to_vertices(const Slic3r::SpanOfConstPtrs<Slic3r::PrintObject>& objects, const std::vector<std::string>& str_tool_colors,
const std::vector<std::string>& str_color_print_colors, const std::vector<Slic3r::CustomGCode::Item>& color_print_values, size_t extruders_count,
std::vector<VerticesData>& data)
{
// extract vertices and layers zs object by object
data.reserve(data.size() + objects.size());
for (size_t i = 0; i < objects.size(); ++i) {
data.emplace_back(VerticesData());
convert_object_to_vertices(*objects[i], str_tool_colors, str_color_print_colors, color_print_values, extruders_count, data.back());
}
}
// mapping from Slic3r::Print to libvgcode::GCodeInputData
GCodeInputData convert(const Slic3r::Print& print, const std::vector<std::string>& str_tool_colors,
const std::vector<std::string>& str_color_print_colors, const std::vector<Slic3r::CustomGCode::Item>& color_print_values,
size_t extruders_count)
{
GCodeInputData ret;
std::vector<VerticesData> data;
if (print.is_step_done(Slic3r::psSkirtBrim) && (print.has_skirt() || print.has_brim()))
// extract vertices and layers zs from skirt/brim
convert_brim_skirt_to_vertices(print, data);
if (!print.wipe_tower_data().tool_changes.empty() && print.is_step_done(Slic3r::psWipeTower))
// extract vertices and layers zs from wipe tower
convert_wipe_tower_to_vertices(print, str_tool_colors, data);
// extract vertices and layers zs from objects
convert_objects_to_vertices(print.objects(), str_tool_colors, str_color_print_colors, color_print_values, extruders_count, data);
// collect layers zs
std::vector<float> layers;
for (const VerticesData& d : data) {
layers.reserve(layers.size() + d.layers_zs.size());
std::copy(d.layers_zs.begin(), d.layers_zs.end(), std::back_inserter(layers));
}
Slic3r::sort_remove_duplicates(layers);
// Now we need to copy the vertices into ret.vertices to be consumed by the preliminary G-code preview.
// We need to collect vertices in the first layer for all objects, push them into the output vector
// and then do the same for all the layers. The algorithm relies on the fact that the vertices from
// lower layers are always placed after vertices from the higher layer.
std::vector<size_t> vert_indices(data.size(), 0);
for (size_t layer_id = 0; layer_id < layers.size(); ++layer_id) {
const float layer_z = layers[layer_id];
for (size_t obj_idx = 0; obj_idx < data.size(); ++obj_idx) {
// d contains PathVertices for one object. Let's stuff everything below this layer_z into ret.vertices.
const size_t start_idx = vert_indices[obj_idx];
size_t idx = start_idx;
while (idx < data[obj_idx].vertices.size() && data[obj_idx].vertices[idx].position[2] <= layer_z)
++idx;
// We have found a vertex above current layer_z. Let's copy the vertices into the output
// and remember where to start when we process another layer.
ret.vertices.insert(ret.vertices.end(),
data[obj_idx].vertices.begin() + start_idx,
data[obj_idx].vertices.begin() + idx);
vert_indices[obj_idx] = idx;
}
}
// collect tool colors
ret.tools_colors.reserve(str_tool_colors.size());
for (const std::string& color : str_tool_colors) {
ret.tools_colors.emplace_back(convert(color));
}
// collect color print colors
const std::vector<std::string>& str_colors = str_color_print_colors.empty() ? str_tool_colors : str_color_print_colors;
ret.color_print_colors.reserve(str_colors.size());
for (const std::string& color : str_colors) {
ret.color_print_colors.emplace_back(convert(color));
}
return ret;
}
} // namespace libvgcode