mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-29 18:02:01 +08:00
789 lines
40 KiB
C++
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
|
|
|