mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-08 16:21:14 +00:00
Since the slicing frame of a belt object starts at the belt below its leading end, its first layers are empty. On a single part they carry the brim bands; with several parts along the belt the later parts' empty layers fall between the earlier parts' printing layers and were written to the G-code as layer changes with no moves at all. The preview numbers its layers (libvgcode::Layers) from the vertices it is given and expects consecutive ids, so at the first such gap it stopped creating layers and folded everything after it into the last one: the top slider layer held nearly the whole print, the slider jumped every other layer through the single-colour stretch before a second part on another filament, and with the belt purge tower the whole print greyed out while dragging. Drop the belt layers that print nothing (no object, support or brim content) in GCode::collect_layers_to_print, and renumber the layers consecutively over the moves that exist when converting a result for libvgcode, so a file with empty layers from any source still previews correctly. The layer slider labels a belt layer with its print Z (the slicer's layer Z, which increases along the belt) instead of libvgcode's toolpath height, which on a tilted layer is wherever its last extrusion ended; the slider assumes that list increases and showed "0 / max" on alternate layers. The processor reads that print Z from the ";Z:" tag non-BBL printers write (it only knew "; Z_HEIGHT:"), on belt printers only, so nothing changes elsewhere. Regression test: two cubes 60 mm apart along the belt produce no layer without an extrusion and the header's layer count matches. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
875 lines
46 KiB
C++
875 lines
46 KiB
C++
///|/ Copyright (c) Prusa Research 2020 - 2023 Enrico Turri @enricoturri1966
|
|
///|/
|
|
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
|
///|/
|
|
#include <Eigen/Geometry>
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <iterator>
|
|
#include <cassert>
|
|
#include <cinttypes>
|
|
#include "libslic3r/Point.hpp"
|
|
#include <string>
|
|
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
|
#include <vector>
|
|
|
|
#include "libslic3r/libslic3r.h"
|
|
#include "LibVGCodeWrapper.hpp"
|
|
|
|
#include <limits>
|
|
#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/Line.hpp"
|
|
#include "libslic3r/Polyline.hpp"
|
|
#include "libslic3r/PrintConfig.hpp"
|
|
#include "../../src/libvgcode/include/GCodeInputData.hpp"
|
|
#include "../../src/libvgcode/include/PathVertex.hpp"
|
|
#include "libvgcode/include/Types.hpp"
|
|
#include "libslic3r/Config.hpp"
|
|
#include "libslic3r/Model.hpp"
|
|
#include "libslic3r/Slicing.hpp"
|
|
#include "libslic3r/SurfaceCollection.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)
|
|
{
|
|
// ORCA: Fix dark color rendering. Ensure minimal brightness.
|
|
auto safe_val = [](float v) -> uint8_t {
|
|
return std::max((uint8_t)(v * 255.0f), (uint8_t)48);
|
|
};
|
|
return { safe_val(c.r()), safe_val(c.g()), safe_val(c.b()) };
|
|
}
|
|
|
|
Color convert(const std::string& color_str)
|
|
{
|
|
Slic3r::ColorRGBA color_rgba;
|
|
return decode_color(color_str, color_rgba) ? convert(color_rgba) : DUMMY_COLOR;
|
|
}
|
|
|
|
Slic3r::ExtrusionRole convert(EGCodeExtrusionRole role)
|
|
{
|
|
switch (role)
|
|
{
|
|
case EGCodeExtrusionRole::None: { return Slic3r::ExtrusionRole::erNone; }
|
|
case EGCodeExtrusionRole::Perimeter: { return Slic3r::ExtrusionRole::erPerimeter; }
|
|
case EGCodeExtrusionRole::ExternalPerimeter: { return Slic3r::ExtrusionRole::erExternalPerimeter; }
|
|
case EGCodeExtrusionRole::OverhangPerimeter: { return Slic3r::ExtrusionRole::erOverhangPerimeter; }
|
|
case EGCodeExtrusionRole::InternalInfill: { return Slic3r::ExtrusionRole::erInternalInfill; }
|
|
case EGCodeExtrusionRole::SolidInfill: { return Slic3r::ExtrusionRole::erSolidInfill; }
|
|
case EGCodeExtrusionRole::TopSolidInfill: { return Slic3r::ExtrusionRole::erTopSolidInfill; }
|
|
case EGCodeExtrusionRole::Ironing: { return Slic3r::ExtrusionRole::erIroning; }
|
|
case EGCodeExtrusionRole::BridgeInfill: { return Slic3r::ExtrusionRole::erBridgeInfill; }
|
|
case EGCodeExtrusionRole::GapFill: { return Slic3r::ExtrusionRole::erGapFill; }
|
|
case EGCodeExtrusionRole::Skirt: { return Slic3r::ExtrusionRole::erSkirt; }
|
|
case EGCodeExtrusionRole::SupportMaterial: { return Slic3r::ExtrusionRole::erSupportMaterial; }
|
|
case EGCodeExtrusionRole::SupportMaterialInterface: { return Slic3r::ExtrusionRole::erSupportMaterialInterface; }
|
|
case EGCodeExtrusionRole::WipeTower: { return Slic3r::ExtrusionRole::erWipeTower; }
|
|
case EGCodeExtrusionRole::Custom: { return Slic3r::ExtrusionRole::erCustom; }
|
|
// ORCA
|
|
case EGCodeExtrusionRole::BottomSurface: { return Slic3r::ExtrusionRole::erBottomSurface; }
|
|
case EGCodeExtrusionRole::InternalBridgeInfill: { return Slic3r::ExtrusionRole::erInternalBridgeInfill; }
|
|
case EGCodeExtrusionRole::Brim: { return Slic3r::ExtrusionRole::erBrim; }
|
|
case EGCodeExtrusionRole::SupportTransition: { return Slic3r::ExtrusionRole::erSupportTransition; }
|
|
case EGCodeExtrusionRole::Mixed: { return Slic3r::ExtrusionRole::erMixed; }
|
|
default: { return Slic3r::ExtrusionRole::erNone; }
|
|
}
|
|
}
|
|
|
|
EGCodeExtrusionRole convert(Slic3r::ExtrusionRole role)
|
|
{
|
|
switch (role)
|
|
{
|
|
case Slic3r::ExtrusionRole::erNone: { return EGCodeExtrusionRole::None; }
|
|
case Slic3r::ExtrusionRole::erPerimeter: { return EGCodeExtrusionRole::Perimeter; }
|
|
case Slic3r::ExtrusionRole::erExternalPerimeter: { return EGCodeExtrusionRole::ExternalPerimeter; }
|
|
case Slic3r::ExtrusionRole::erOverhangPerimeter: { return EGCodeExtrusionRole::OverhangPerimeter; }
|
|
case Slic3r::ExtrusionRole::erInternalInfill: { return EGCodeExtrusionRole::InternalInfill; }
|
|
case Slic3r::ExtrusionRole::erSolidInfill: { return EGCodeExtrusionRole::SolidInfill; }
|
|
case Slic3r::ExtrusionRole::erTopSolidInfill: { return EGCodeExtrusionRole::TopSolidInfill; }
|
|
case Slic3r::ExtrusionRole::erIroning: { return EGCodeExtrusionRole::Ironing; }
|
|
case Slic3r::ExtrusionRole::erBridgeInfill: { return EGCodeExtrusionRole::BridgeInfill; }
|
|
case Slic3r::ExtrusionRole::erGapFill: { return EGCodeExtrusionRole::GapFill; }
|
|
case Slic3r::ExtrusionRole::erSkirt: { return EGCodeExtrusionRole::Skirt; }
|
|
case Slic3r::ExtrusionRole::erSupportMaterial: { return EGCodeExtrusionRole::SupportMaterial; }
|
|
case Slic3r::ExtrusionRole::erSupportMaterialInterface: { return EGCodeExtrusionRole::SupportMaterialInterface; }
|
|
case Slic3r::ExtrusionRole::erWipeTower: { return EGCodeExtrusionRole::WipeTower; }
|
|
case Slic3r::ExtrusionRole::erCustom: { return EGCodeExtrusionRole::Custom; }
|
|
// ORCA
|
|
case Slic3r::ExtrusionRole::erBottomSurface: { return EGCodeExtrusionRole::BottomSurface; }
|
|
case Slic3r::ExtrusionRole::erInternalBridgeInfill: { return EGCodeExtrusionRole::InternalBridgeInfill; }
|
|
case Slic3r::ExtrusionRole::erBrim: { return EGCodeExtrusionRole::Brim; }
|
|
case Slic3r::ExtrusionRole::erSupportTransition: { return EGCodeExtrusionRole::SupportTransition; }
|
|
case Slic3r::ExtrusionRole::erMixed: { return EGCodeExtrusionRole::Mixed; }
|
|
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,
|
|
const Slic3r::Transform3d* belt_xform)
|
|
{
|
|
GCodeInputData ret;
|
|
|
|
// Belt printers: optionally map each vertex DISPLAY position from machine
|
|
// (G-code) space back to model/Cartesian space using the general belt
|
|
// back-transform (handles any mesh rotation + shear + axis remap, not just
|
|
// 45 deg). Only the rendered position is transformed; layer_id, times and
|
|
// the volumetric/flow math below keep the original machine-space values.
|
|
auto xform_pos = [belt_xform](const Slic3r::Vec3f& v) -> Vec3 {
|
|
if (belt_xform != nullptr)
|
|
return convert(Slic3r::Vec3f((*belt_xform * v.cast<double>()).cast<float>()));
|
|
return convert(v);
|
|
};
|
|
|
|
// 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());
|
|
// libvgcode numbers its layers from the vertices it is given and expects them to
|
|
// arrive one layer after the other with consecutive ids: a layer without any move
|
|
// (a belt file can carry layer changes that print nothing) would leave a gap, and
|
|
// every later vertex would then be folded into the last layer created. Renumber
|
|
// the ids consecutively over the moves that exist.
|
|
uint32_t src_layer_id = std::numeric_limits<uint32_t>::max();
|
|
uint32_t layer_id = 0;
|
|
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);
|
|
if (curr.layer_id != src_layer_id) {
|
|
if (src_layer_id != std::numeric_limits<uint32_t>::max())
|
|
++ layer_id;
|
|
src_layer_id = curr.layer_id;
|
|
}
|
|
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
|
|
// ORCA: Split the path when a preview value changes.
|
|
|| prev.mm3_per_mm != curr.mm3_per_mm || prev.acceleration != curr.acceleration || prev.jerk != curr.jerk) {
|
|
// 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 = { xform_pos(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), layer_id,
|
|
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), { 0.0f, 0.0f },
|
|
/* ORCA: Add Pressure Advance visualization support */ 0.0f, curr.pressure_advance,
|
|
/* ORCA: Add Acceleration visualization support */ curr.acceleration,
|
|
/* ORCA: Add Jerk visualization support */ curr.jerk };
|
|
#else
|
|
const libvgcode::PathVertex vertex = { xform_pos(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), layer_id,
|
|
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), { 0.0f, 0.0f },
|
|
/* ORCA: Add Pressure Advance visualization support */ 0.0f, curr.pressure_advance,
|
|
/* ORCA: Add Acceleration visualization support */ curr.acceleration,
|
|
/* ORCA: Add Jerk visualization support */ curr.jerk };
|
|
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
ret.vertices.emplace_back(vertex);
|
|
}
|
|
}
|
|
|
|
#if VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
const libvgcode::PathVertex vertex = { xform_pos(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), layer_id,
|
|
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), curr.time,
|
|
/* ORCA: Add Pressure Advance visualization support */ 0.0f, curr.pressure_advance,
|
|
/* ORCA: Add Acceleration visualization support */ curr.acceleration,
|
|
/* ORCA: Add Jerk visualization support */ curr.jerk };
|
|
#else
|
|
const libvgcode::PathVertex vertex = { xform_pos(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), layer_id,
|
|
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), curr.time,
|
|
/* ORCA: Add Pressure Advance visualization support */ 0.0f, curr.pressure_advance,
|
|
/* ORCA: Add Acceleration visualization support */ curr.acceleration,
|
|
/* ORCA: Add Jerk visualization support */ curr.jerk };
|
|
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
|
|
ret.vertices.emplace_back(vertex);
|
|
}
|
|
ret.vertices.shrink_to_fit();
|
|
|
|
// Note: the belt designed-view anchoring (recovering the per-object placement/
|
|
// lift translation the linear back-transform cannot) is folded into belt_xform
|
|
// by the caller (GCodeViewer::load_as_gcode), which anchors onto the upright
|
|
// model bounding box. Nothing extra to do here.
|
|
|
|
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.to_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.to_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.to_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) {
|
|
// TODO - brim map?
|
|
// 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 = print.model().wipe_tower.rotation / 180.0f * PI;
|
|
// ORCA/BBS: plate index
|
|
m_position = print.model().wipe_tower.positions[print.get_plate_index()].cast<float>();
|
|
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(float print_z, size_t extruder_id) const {
|
|
if (!m_color_print_values.empty())
|
|
return color_print_color_id(double(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));
|
|
}
|
|
|
|
Slic3r::sort_remove_duplicates(data.layers_zs);
|
|
|
|
for (const Slic3r::Layer* layer : layers) {
|
|
const size_t old_vertices_count = data.vertices.size();
|
|
const float layer_z = static_cast<float>(layer->print_z);
|
|
const auto it = std::find(data.layers_zs.begin(), data.layers_zs.end(), layer_z);
|
|
assert(it != data.layers_zs.end());
|
|
const size_t layer_id = (it != data.layers_zs.end()) ? std::distance(data.layers_zs.begin(), it) : 0;
|
|
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.outer_wall_filament_id.value - 1, 0));
|
|
convert_to_vertices(layerm->perimeters, layer_z, layer_id, extruder_id,
|
|
object_helper.color_id(layer_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 Slic3r::ExtrusionRole role = fill.entities.front()->role();
|
|
const bool is_solid_infill = Slic3r::is_solid_infill(role);
|
|
const size_t extruder_id = is_solid_infill ?
|
|
static_cast<size_t>(std::max((role == Slic3r::erTopSolidInfill || role == Slic3r::erIroning ? cfg.top_surface_filament_id.value :
|
|
role == Slic3r::erBottomSurface ? cfg.bottom_surface_filament_id.value :
|
|
cfg.internal_solid_filament_id.value) - 1, 0)) :
|
|
static_cast<size_t>(std::max(cfg.sparse_infill_filament_id.value - 1, 0));
|
|
convert_to_vertices(fill, layer_z, layer_id, extruder_id,
|
|
object_helper.color_id(layer_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::erSupportMaterial;
|
|
const size_t extruder_id = is_support_material ?
|
|
static_cast<size_t>(std::max(cfg.support_filament.value - 1, 0)) :
|
|
static_cast<size_t>(std::max(cfg.support_interface_filament.value - 1, 0));
|
|
convert_to_vertices(*extrusion_entity, layer_z, layer_id,
|
|
extruder_id, object_helper.color_id(layer_z, extruder_id),
|
|
is_support_material ? EGCodeExtrusionRole::SupportMaterial : EGCodeExtrusionRole::SupportMaterialInterface,
|
|
copy, data.vertices);
|
|
}
|
|
}
|
|
}
|
|
// filter out empty layers
|
|
const size_t new_vertices_count = data.vertices.size();
|
|
if (new_vertices_count == old_vertices_count)
|
|
data.layers_zs.erase(data.layers_zs.begin() + layer_id);
|
|
}
|
|
}
|
|
|
|
static void convert_objects_to_vertices(const Slic3r::ConstPrintObjectPtrsAdaptor& 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
|
|
|