mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-10-23 16:51:21 -06:00
Merge remote-tracking branch 'origin/master' into lh_avoid_crossing_perimeters
# Conflicts: # src/libslic3r/MotionPlanner.cpp # src/libslic3r/libslic3r.h
This commit is contained in:
commit
87879034f6
175 changed files with 34821 additions and 26174 deletions
|
|
@ -25,6 +25,10 @@ static const float DEFAULT_ACCELERATION = 1500.0f; // Prusa Firmware 1_75mm_MK2
|
|||
namespace Slic3r {
|
||||
|
||||
const std::string GCodeProcessor::Extrusion_Role_Tag = "TYPE:";
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
const std::string GCodeProcessor::Wipe_Start_Tag = "WIPE_START";
|
||||
const std::string GCodeProcessor::Wipe_End_Tag = "WIPE_END";
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
const std::string GCodeProcessor::Height_Tag = "HEIGHT:";
|
||||
const std::string GCodeProcessor::Layer_Change_Tag = "LAYER_CHANGE";
|
||||
const std::string GCodeProcessor::Color_Change_Tag = "COLOR_CHANGE";
|
||||
|
|
@ -35,6 +39,11 @@ const std::string GCodeProcessor::First_Line_M73_Placeholder_Tag = "; _
|
|||
const std::string GCodeProcessor::Last_Line_M73_Placeholder_Tag = "; _GP_LAST_LINE_M73_PLACEHOLDER";
|
||||
const std::string GCodeProcessor::Estimated_Printing_Time_Placeholder_Tag = "; _GP_ESTIMATED_PRINTING_TIME_PLACEHOLDER";
|
||||
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
const float GCodeProcessor::Wipe_Width = 0.05f;
|
||||
const float GCodeProcessor::Wipe_Height = 0.05f;
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
const std::string GCodeProcessor::Width_Tag = "WIDTH:";
|
||||
const std::string GCodeProcessor::Mm3_Per_Mm_Tag = "MM3_PER_MM:";
|
||||
|
|
@ -390,13 +399,17 @@ void GCodeProcessor::TimeProcessor::post_process(const std::string& filename)
|
|||
};
|
||||
|
||||
// check for temporary lines
|
||||
auto is_temporary_decoration = [](const std::string& gcode_line) {
|
||||
auto is_temporary_decoration = [](const std::string_view gcode_line) {
|
||||
// remove trailing '\n'
|
||||
std::string line = gcode_line.substr(0, gcode_line.length() - 1);
|
||||
if (line == "; " + Layer_Change_Tag)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
assert(! gcode_line.empty());
|
||||
assert(gcode_line.back() == '\n');
|
||||
|
||||
// return true for decorations which are used in processing the gcode but that should not be exported into the final gcode
|
||||
// i.e.:
|
||||
// bool ret = gcode_line.substr(0, gcode_line.length() - 1) == ";" + Layer_Change_Tag;
|
||||
// ...
|
||||
// return ret;
|
||||
return false;
|
||||
};
|
||||
|
||||
// Iterators for the normal and silent cached time estimate entry recently processed, used by process_line_G1.
|
||||
|
|
@ -488,7 +501,8 @@ const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> GCodeProces
|
|||
{ EProducer::Cura, "Cura_SteamEngine" },
|
||||
{ EProducer::Simplify3D, "Simplify3D" },
|
||||
{ EProducer::CraftWare, "CraftWare" },
|
||||
{ EProducer::ideaMaker, "ideaMaker" }
|
||||
{ EProducer::ideaMaker, "ideaMaker" },
|
||||
{ EProducer::KissSlicer, "KISSlicer" }
|
||||
};
|
||||
|
||||
unsigned int GCodeProcessor::s_result_id = 0;
|
||||
|
|
@ -591,9 +605,6 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
|||
}
|
||||
}
|
||||
|
||||
// ensure at least one (default) color is defined
|
||||
std::string default_color = "#FF8000";
|
||||
m_result.extruder_colors = std::vector<std::string>(1, default_color);
|
||||
const ConfigOptionStrings* extruder_colour = config.option<ConfigOptionStrings>("extruder_colour");
|
||||
if (extruder_colour != nullptr) {
|
||||
// takes colors from config
|
||||
|
|
@ -608,7 +619,9 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// replace missing values with default
|
||||
std::string default_color = "#FF8000";
|
||||
for (size_t i = 0; i < m_result.extruder_colors.size(); ++i) {
|
||||
if (m_result.extruder_colors[i].empty())
|
||||
m_result.extruder_colors[i] = default_color;
|
||||
|
|
@ -725,6 +738,9 @@ void GCodeProcessor::reset()
|
|||
m_end_position = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
m_origin = { 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
m_cached_position.reset();
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
m_wiping = false;
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
m_feedrate = 0.0f;
|
||||
m_width = 0.0f;
|
||||
|
|
@ -806,6 +822,16 @@ void GCodeProcessor::process_file(const std::string& filename, bool apply_postpr
|
|||
process_gcode_line(line);
|
||||
});
|
||||
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
// update width/height of wipe moves
|
||||
for (MoveVertex& move : m_result.moves) {
|
||||
if (move.type == EMoveType::Wipe) {
|
||||
move.width = Wipe_Width;
|
||||
move.height = Wipe_Height;
|
||||
}
|
||||
}
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
// process the time blocks
|
||||
for (size_t i = 0; i < static_cast<size_t>(PrintEstimatedTimeStatistics::ETimeMode::Count); ++i) {
|
||||
TimeMachine& machine = m_time_processor.machines[i];
|
||||
|
|
@ -1027,6 +1053,20 @@ void GCodeProcessor::process_tags(const std::string_view comment)
|
|||
return;
|
||||
}
|
||||
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
// wipe start tag
|
||||
if (starts_with(comment, Wipe_Start_Tag)) {
|
||||
m_wiping = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// wipe end tag
|
||||
if (starts_with(comment, Wipe_End_Tag)) {
|
||||
m_wiping = false;
|
||||
return;
|
||||
}
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
if ((!m_producers_enabled || m_producer == EProducer::PrusaSlicer) &&
|
||||
starts_with(comment, Height_Tag)) {
|
||||
// height tag
|
||||
|
|
@ -1104,11 +1144,14 @@ bool GCodeProcessor::process_producers_tags(const std::string_view comment)
|
|||
{
|
||||
switch (m_producer)
|
||||
{
|
||||
case EProducer::Slic3rPE:
|
||||
case EProducer::Slic3r:
|
||||
case EProducer::PrusaSlicer: { return process_prusaslicer_tags(comment); }
|
||||
case EProducer::Cura: { return process_cura_tags(comment); }
|
||||
case EProducer::Simplify3D: { return process_simplify3d_tags(comment); }
|
||||
case EProducer::CraftWare: { return process_craftware_tags(comment); }
|
||||
case EProducer::ideaMaker: { return process_ideamaker_tags(comment); }
|
||||
case EProducer::KissSlicer: { return process_kissslicer_tags(comment); }
|
||||
default: { return false; }
|
||||
}
|
||||
}
|
||||
|
|
@ -1178,6 +1221,14 @@ bool GCodeProcessor::process_cura_tags(const std::string_view comment)
|
|||
return true;
|
||||
}
|
||||
|
||||
// layer
|
||||
tag = "LAYER:";
|
||||
pos = comment.find(tag);
|
||||
if (pos != comment.npos) {
|
||||
++m_layer_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1262,9 +1313,8 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string_view comment)
|
|||
return true;
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
// geometry
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
// ; tool
|
||||
std::string tag = " tool";
|
||||
pos = comment.find(tag);
|
||||
|
|
@ -1289,6 +1339,19 @@ bool GCodeProcessor::process_simplify3d_tags(const std::string_view comment)
|
|||
}
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
// ; layer
|
||||
std::string tag = " layer";
|
||||
pos = comment.find(tag);
|
||||
if (pos == 0) {
|
||||
// skip lines "; layer end"
|
||||
const std::string_view data = comment.substr(pos + tag.length());
|
||||
size_t end_start = data.find("end");
|
||||
if (end_start == data.npos)
|
||||
++m_layer_id;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1329,6 +1392,13 @@ bool GCodeProcessor::process_craftware_tags(const std::string_view comment)
|
|||
return true;
|
||||
}
|
||||
|
||||
// layer
|
||||
pos = comment.find(" Layer #");
|
||||
if (pos == 0) {
|
||||
++m_layer_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1360,9 +1430,8 @@ bool GCodeProcessor::process_ideamaker_tags(const std::string_view comment)
|
|||
return true;
|
||||
}
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
// geometry
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
// width
|
||||
tag = "WIDTH:";
|
||||
pos = comment.find(tag);
|
||||
|
|
@ -1382,6 +1451,120 @@ bool GCodeProcessor::process_ideamaker_tags(const std::string_view comment)
|
|||
}
|
||||
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
|
||||
// layer
|
||||
pos = comment.find("LAYER:");
|
||||
if (pos == 0) {
|
||||
++m_layer_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GCodeProcessor::process_kissslicer_tags(const std::string_view comment)
|
||||
{
|
||||
// extrusion roles
|
||||
|
||||
// ; 'Raft Path'
|
||||
size_t pos = comment.find(" 'Raft Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erSkirt;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Support Interface Path'
|
||||
pos = comment.find(" 'Support Interface Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erSupportMaterialInterface;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Travel/Ironing Path'
|
||||
pos = comment.find(" 'Travel/Ironing Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erIroning;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Support (may Stack) Path'
|
||||
pos = comment.find(" 'Support (may Stack) Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erSupportMaterial;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Perimeter Path'
|
||||
pos = comment.find(" 'Perimeter Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erExternalPerimeter;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Pillar Path'
|
||||
pos = comment.find(" 'Pillar Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Destring/Wipe/Jump Path'
|
||||
pos = comment.find(" 'Destring/Wipe/Jump Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Prime Pillar Path'
|
||||
pos = comment.find(" 'Prime Pillar Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Loop Path'
|
||||
pos = comment.find(" 'Loop Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Crown Path'
|
||||
pos = comment.find(" 'Crown Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erNone; // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Solid Path'
|
||||
pos = comment.find(" 'Solid Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erNone;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Stacked Sparse Infill Path'
|
||||
pos = comment.find(" 'Stacked Sparse Infill Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erInternalInfill;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ; 'Sparse Infill Path'
|
||||
pos = comment.find(" 'Sparse Infill Path'");
|
||||
if (pos == 0) {
|
||||
m_extrusion_role = erSolidInfill;
|
||||
return true;
|
||||
}
|
||||
|
||||
// geometry
|
||||
|
||||
// layer
|
||||
pos = comment.find(" BEGIN_LAYER_");
|
||||
if (pos == 0) {
|
||||
++m_layer_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1423,7 +1606,13 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
|
|||
auto move_type = [this](const AxisCoords& delta_pos) {
|
||||
EMoveType type = EMoveType::Noop;
|
||||
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
if (m_wiping)
|
||||
type = EMoveType::Wipe;
|
||||
else if (delta_pos[E] < 0.0f)
|
||||
#else
|
||||
if (delta_pos[E] < 0.0f)
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
type = (delta_pos[X] != 0.0f || delta_pos[Y] != 0.0f || delta_pos[Z] != 0.0f) ? EMoveType::Travel : EMoveType::Retract;
|
||||
else if (delta_pos[E] > 0.0f) {
|
||||
if (delta_pos[X] == 0.0f && delta_pos[Y] == 0.0f)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,9 @@ namespace Slic3r {
|
|||
Pause_Print,
|
||||
Custom_GCode,
|
||||
Travel,
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
Wipe,
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
Extrude,
|
||||
Count
|
||||
};
|
||||
|
|
@ -69,6 +72,10 @@ namespace Slic3r {
|
|||
{
|
||||
public:
|
||||
static const std::string Extrusion_Role_Tag;
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
static const std::string Wipe_Start_Tag;
|
||||
static const std::string Wipe_End_Tag;
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
static const std::string Height_Tag;
|
||||
static const std::string Layer_Change_Tag;
|
||||
static const std::string Color_Change_Tag;
|
||||
|
|
@ -78,6 +85,11 @@ namespace Slic3r {
|
|||
static const std::string Last_Line_M73_Placeholder_Tag;
|
||||
static const std::string Estimated_Printing_Time_Placeholder_Tag;
|
||||
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
static const float Wipe_Width;
|
||||
static const float Wipe_Height;
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
|
||||
static const std::string Width_Tag;
|
||||
static const std::string Mm3_Per_Mm_Tag;
|
||||
|
|
@ -390,6 +402,9 @@ namespace Slic3r {
|
|||
AxisCoords m_end_position; // mm
|
||||
AxisCoords m_origin; // mm
|
||||
CachedPosition m_cached_position;
|
||||
#if ENABLE_SHOW_WIPE_MOVES
|
||||
bool m_wiping;
|
||||
#endif // ENABLE_SHOW_WIPE_MOVES
|
||||
|
||||
float m_feedrate; // mm/s
|
||||
float m_width; // mm
|
||||
|
|
@ -414,7 +429,8 @@ namespace Slic3r {
|
|||
Cura,
|
||||
Simplify3D,
|
||||
CraftWare,
|
||||
ideaMaker
|
||||
ideaMaker,
|
||||
KissSlicer
|
||||
};
|
||||
|
||||
static const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> Producers;
|
||||
|
|
@ -471,6 +487,7 @@ namespace Slic3r {
|
|||
bool process_simplify3d_tags(const std::string_view comment);
|
||||
bool process_craftware_tags(const std::string_view comment);
|
||||
bool process_ideamaker_tags(const std::string_view comment);
|
||||
bool process_kissslicer_tags(const std::string_view comment);
|
||||
|
||||
bool detect_producer(const std::string_view comment);
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "libslic3r/EdgeGrid.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/SVG.hpp"
|
||||
#include "libslic3r/Layer.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
|
@ -191,24 +192,98 @@ void SeamPlacer::init(const Print& print)
|
|||
{
|
||||
m_enforcers.clear();
|
||||
m_blockers.clear();
|
||||
//m_last_seam_position.clear();
|
||||
m_seam_history.clear();
|
||||
m_po_list.clear();
|
||||
|
||||
for (const PrintObject* po : print.objects()) {
|
||||
po->project_and_append_custom_facets(true, EnforcerBlockerType::ENFORCER, m_enforcers);
|
||||
po->project_and_append_custom_facets(true, EnforcerBlockerType::BLOCKER, m_blockers);
|
||||
}
|
||||
const std::vector<double>& nozzle_dmrs = print.config().nozzle_diameter.values;
|
||||
float max_nozzle_dmr = *std::max_element(nozzle_dmrs.begin(), nozzle_dmrs.end());
|
||||
for (ExPolygons& explgs : m_enforcers)
|
||||
explgs = Slic3r::offset_ex(explgs, scale_(max_nozzle_dmr));
|
||||
for (ExPolygons& explgs : m_blockers)
|
||||
explgs = Slic3r::offset_ex(explgs, scale_(max_nozzle_dmr));
|
||||
const std::vector<double>& nozzle_dmrs = print.config().nozzle_diameter.values;
|
||||
float max_nozzle_dmr = *std::max_element(nozzle_dmrs.begin(), nozzle_dmrs.end());
|
||||
|
||||
|
||||
std::vector<ExPolygons> temp_enf;
|
||||
std::vector<ExPolygons> temp_blk;
|
||||
|
||||
for (const PrintObject* po : print.objects()) {
|
||||
temp_enf.clear();
|
||||
temp_blk.clear();
|
||||
po->project_and_append_custom_facets(true, EnforcerBlockerType::ENFORCER, temp_enf);
|
||||
po->project_and_append_custom_facets(true, EnforcerBlockerType::BLOCKER, temp_blk);
|
||||
|
||||
// Offset the triangles out slightly.
|
||||
for (auto* custom_per_object : {&temp_enf, &temp_blk})
|
||||
for (ExPolygons& explgs : *custom_per_object)
|
||||
explgs = Slic3r::offset_ex(explgs, scale_(max_nozzle_dmr));
|
||||
|
||||
// FIXME: Offsetting should be done somehow cheaper, but following does not work
|
||||
// for (auto* custom_per_object : {&temp_enf, &temp_blk}) {
|
||||
// for (ExPolygons& plgs : *custom_per_object) {
|
||||
// for (ExPolygon& plg : plgs) {
|
||||
// auto out = Slic3r::offset_ex(plg, scale_(max_nozzle_dmr));
|
||||
// plg = out.empty() ? ExPolygon() : out.front();
|
||||
// assert(out.empty() || out.size() == 1);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// Remember this PrintObject and initialize a store of enforcers and blockers for it.
|
||||
m_po_list.push_back(po);
|
||||
size_t po_idx = m_po_list.size() - 1;
|
||||
m_enforcers.emplace_back(std::vector<CustomTrianglesPerLayer>(temp_enf.size()));
|
||||
m_blockers.emplace_back(std::vector<CustomTrianglesPerLayer>(temp_blk.size()));
|
||||
|
||||
// A helper class to store data to build the AABB tree from.
|
||||
class CustomTriangleRef {
|
||||
public:
|
||||
CustomTriangleRef(size_t idx,
|
||||
Point&& centroid,
|
||||
BoundingBox&& bb)
|
||||
: m_idx{idx}, m_centroid{centroid},
|
||||
m_bbox{AlignedBoxType(bb.min, bb.max)}
|
||||
{}
|
||||
size_t idx() const { return m_idx; }
|
||||
const Point& centroid() const { return m_centroid; }
|
||||
const TreeType::BoundingBox& bbox() const { return m_bbox; }
|
||||
|
||||
private:
|
||||
size_t m_idx;
|
||||
Point m_centroid;
|
||||
AlignedBoxType m_bbox;
|
||||
};
|
||||
|
||||
// A lambda to extract the ExPolygons and save them into the member AABB tree.
|
||||
// Will be called for enforcers and blockers separately.
|
||||
auto add_custom = [](std::vector<ExPolygons>& src, std::vector<CustomTrianglesPerLayer>& dest) {
|
||||
// Go layer by layer, and append all the ExPolygons into the AABB tree.
|
||||
size_t layer_idx = 0;
|
||||
for (ExPolygons& expolys_on_layer : src) {
|
||||
CustomTrianglesPerLayer& layer_data = dest[layer_idx];
|
||||
std::vector<CustomTriangleRef> triangles_data;
|
||||
layer_data.polys.reserve(expolys_on_layer.size());
|
||||
triangles_data.reserve(expolys_on_layer.size());
|
||||
|
||||
for (ExPolygon& expoly : expolys_on_layer) {
|
||||
if (expoly.empty())
|
||||
continue;
|
||||
layer_data.polys.emplace_back(std::move(expoly));
|
||||
triangles_data.emplace_back(layer_data.polys.size() - 1,
|
||||
layer_data.polys.back().centroid(),
|
||||
layer_data.polys.back().bounding_box());
|
||||
}
|
||||
// All polygons are saved, build the AABB tree for them.
|
||||
layer_data.tree.build(std::move(triangles_data));
|
||||
++layer_idx;
|
||||
}
|
||||
};
|
||||
|
||||
add_custom(temp_enf, m_enforcers.at(po_idx));
|
||||
add_custom(temp_blk, m_blockers.at(po_idx));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_position,
|
||||
Point SeamPlacer::get_seam(const Layer& layer, const SeamPosition seam_position,
|
||||
const ExtrusionLoop& loop, Point last_pos, coordf_t nozzle_dmr,
|
||||
const PrintObject* po, bool was_clockwise, const EdgeGrid::Grid* lower_layer_edge_grid)
|
||||
{
|
||||
|
|
@ -216,7 +291,28 @@ Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_posit
|
|||
BoundingBox polygon_bb = polygon.bounding_box();
|
||||
const coord_t nozzle_r = coord_t(scale_(0.5 * nozzle_dmr) + 0.5);
|
||||
|
||||
if (this->is_custom_seam_on_layer(layer_idx)) {
|
||||
size_t po_idx = std::find(m_po_list.begin(), m_po_list.end(), po) - m_po_list.begin();
|
||||
|
||||
// Find current layer in respective PrintObject. Cache the result so the
|
||||
// lookup is only done once per layer, not for each loop.
|
||||
const Layer* layer_po = nullptr;
|
||||
if (po == m_last_po && layer.print_z == m_last_print_z)
|
||||
layer_po = m_last_layer_po;
|
||||
else {
|
||||
layer_po = po->get_layer_at_printz(layer.print_z);
|
||||
m_last_po = po;
|
||||
m_last_print_z = layer.print_z;
|
||||
m_last_layer_po = layer_po;
|
||||
}
|
||||
if (! layer_po)
|
||||
return last_pos;
|
||||
|
||||
// Index of this layer in the respective PrintObject.
|
||||
size_t layer_idx = layer_po->id() - po->layers().front()->id(); // raft layers
|
||||
|
||||
assert(layer_idx < po->layer_count());
|
||||
|
||||
if (this->is_custom_seam_on_layer(layer_idx, po_idx)) {
|
||||
// Seam enf/blockers can begin and end in between the original vertices.
|
||||
// Let add extra points in between and update the leghths.
|
||||
polygon.densify(MINIMAL_POLYGON_SIDE);
|
||||
|
|
@ -229,11 +325,10 @@ Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_posit
|
|||
if (seam_position == spAligned) {
|
||||
// Seam is aligned to the seam at the preceding layer.
|
||||
if (po != nullptr) {
|
||||
std::optional<Point> pos = m_seam_history.get_last_seam(po, layer_idx, polygon_bb);
|
||||
std::optional<Point> pos = m_seam_history.get_last_seam(m_po_list[po_idx], layer_idx, polygon_bb);
|
||||
if (pos.has_value()) {
|
||||
//last_pos = m_last_seam_position[po];
|
||||
last_pos = *pos;
|
||||
last_pos_weight = is_custom_enforcer_on_layer(layer_idx) ? 0.f : 1.f;
|
||||
last_pos_weight = is_custom_enforcer_on_layer(layer_idx, po_idx) ? 0.f : 1.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -313,12 +408,12 @@ Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_posit
|
|||
|
||||
// Custom seam. Huge (negative) constant penalty is applied inside
|
||||
// blockers (enforcers) to rule out points that should not win.
|
||||
this->apply_custom_seam(polygon, penalties, lengths, layer_idx, seam_position);
|
||||
this->apply_custom_seam(polygon, po_idx, penalties, lengths, layer_idx, seam_position);
|
||||
|
||||
// Find a point with a minimum penalty.
|
||||
size_t idx_min = std::min_element(penalties.begin(), penalties.end()) - penalties.begin();
|
||||
|
||||
if (seam_position != spAligned || ! is_custom_enforcer_on_layer(layer_idx)) {
|
||||
if (seam_position != spAligned || ! is_custom_enforcer_on_layer(layer_idx, po_idx)) {
|
||||
// Very likely the weight of idx_min is very close to the weight of last_pos_proj_idx.
|
||||
// In that case use last_pos_proj_idx instead.
|
||||
float penalty_aligned = penalties[last_pos_proj_idx];
|
||||
|
|
@ -363,29 +458,45 @@ Point SeamPlacer::get_seam(const size_t layer_idx, const SeamPosition seam_posit
|
|||
return polygon.points[idx_min];
|
||||
|
||||
} else { // spRandom
|
||||
if (loop.loop_role() == elrContourInternalPerimeter && loop.role() != erExternalPerimeter) {
|
||||
// This loop does not contain any other loop. Set a random position.
|
||||
// The other loops will get a seam close to the random point chosen
|
||||
// on the innermost contour.
|
||||
//FIXME This works correctly for inner contours first only.
|
||||
last_pos = this->get_random_seam(layer_idx, polygon);
|
||||
}
|
||||
if (loop.role() == erExternalPerimeter && is_custom_seam_on_layer(layer_idx)) {
|
||||
// There is a possibility that the loop will be influenced by custom
|
||||
// seam enforcer/blocker. In this case do not inherit the seam
|
||||
// from internal loops (which may conflict with the custom selection
|
||||
// and generate another random one.
|
||||
bool saw_custom = false;
|
||||
Point candidate = this->get_random_seam(layer_idx, polygon, &saw_custom);
|
||||
if (saw_custom)
|
||||
last_pos = candidate;
|
||||
if (po->print()->default_region_config().external_perimeters_first) {
|
||||
if (loop.role() == erExternalPerimeter)
|
||||
last_pos = this->get_random_seam(layer_idx, polygon, po_idx);
|
||||
else {
|
||||
// Internal perimeters will just use last_pos.
|
||||
}
|
||||
} else {
|
||||
if (loop.loop_role() == elrContourInternalPerimeter && loop.role() != erExternalPerimeter) {
|
||||
// This loop does not contain any other loop. Set a random position.
|
||||
// The other loops will get a seam close to the random point chosen
|
||||
// on the innermost contour.
|
||||
last_pos = this->get_random_seam(layer_idx, polygon, po_idx);
|
||||
m_last_loop_was_external = false;
|
||||
}
|
||||
if (loop.role() == erExternalPerimeter) {
|
||||
if (m_last_loop_was_external) {
|
||||
// There was no internal perimeter before this one.
|
||||
last_pos = this->get_random_seam(layer_idx, polygon, po_idx);
|
||||
} else {
|
||||
if (is_custom_seam_on_layer(layer_idx, po_idx)) {
|
||||
// There is a possibility that the loop will be influenced by custom
|
||||
// seam enforcer/blocker. In this case do not inherit the seam
|
||||
// from internal loops (which may conflict with the custom selection
|
||||
// and generate another random one.
|
||||
bool saw_custom = false;
|
||||
Point candidate = this->get_random_seam(layer_idx, polygon, po_idx, &saw_custom);
|
||||
if (saw_custom)
|
||||
last_pos = candidate;
|
||||
}
|
||||
}
|
||||
m_last_loop_was_external = true;
|
||||
}
|
||||
}
|
||||
return last_pos;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Point SeamPlacer::get_random_seam(size_t layer_idx, const Polygon& polygon,
|
||||
Point SeamPlacer::get_random_seam(size_t layer_idx, const Polygon& polygon, size_t po_idx,
|
||||
bool* saw_custom) const
|
||||
{
|
||||
// Parametrize the polygon by its length.
|
||||
|
|
@ -394,7 +505,7 @@ Point SeamPlacer::get_random_seam(size_t layer_idx, const Polygon& polygon,
|
|||
// Which of the points are inside enforcers/blockers?
|
||||
std::vector<size_t> enforcers_idxs;
|
||||
std::vector<size_t> blockers_idxs;
|
||||
this->get_enforcers_and_blockers(layer_idx, polygon, enforcers_idxs, blockers_idxs);
|
||||
this->get_enforcers_and_blockers(layer_idx, polygon, po_idx, enforcers_idxs, blockers_idxs);
|
||||
|
||||
bool has_enforcers = ! enforcers_idxs.empty();
|
||||
bool has_blockers = ! blockers_idxs.empty();
|
||||
|
|
@ -444,32 +555,44 @@ Point SeamPlacer::get_random_seam(size_t layer_idx, const Polygon& polygon,
|
|||
|
||||
void SeamPlacer::get_enforcers_and_blockers(size_t layer_id,
|
||||
const Polygon& polygon,
|
||||
size_t po_idx,
|
||||
std::vector<size_t>& enforcers_idxs,
|
||||
std::vector<size_t>& blockers_idxs) const
|
||||
{
|
||||
enforcers_idxs.clear();
|
||||
blockers_idxs.clear();
|
||||
|
||||
// FIXME: This is quadratic and it should be improved, maybe by building
|
||||
// an AABB tree (or at least utilize bounding boxes).
|
||||
for (size_t i=0; i<polygon.points.size(); ++i) {
|
||||
auto is_inside = [](const Point& pt,
|
||||
const CustomTrianglesPerLayer& custom_data) -> bool {
|
||||
assert(! custom_data.polys.empty());
|
||||
// Now ask the AABB tree which polygon we should check and check it.
|
||||
size_t candidate = AABBTreeIndirect::get_candidate_idx(custom_data.tree, pt);
|
||||
if (candidate != size_t(-1)
|
||||
&& custom_data.polys[candidate].contains(pt))
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
if (! m_enforcers.empty()) {
|
||||
assert(layer_id < m_enforcers.size());
|
||||
for (const ExPolygon& explg : m_enforcers[layer_id]) {
|
||||
if (explg.contains(polygon.points[i]))
|
||||
enforcers_idxs.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (! m_blockers.empty()) {
|
||||
assert(layer_id < m_blockers.size());
|
||||
for (const ExPolygon& explg : m_blockers[layer_id]) {
|
||||
if (explg.contains(polygon.points[i]))
|
||||
blockers_idxs.push_back(i);
|
||||
if (! m_enforcers[po_idx].empty()) {
|
||||
const CustomTrianglesPerLayer& enforcers = m_enforcers[po_idx][layer_id];
|
||||
if (! enforcers.polys.empty()) {
|
||||
for (size_t i=0; i<polygon.points.size(); ++i) {
|
||||
if (is_inside(polygon.points[i], enforcers))
|
||||
enforcers_idxs.emplace_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! m_blockers[po_idx].empty()) {
|
||||
const CustomTrianglesPerLayer& blockers = m_blockers[po_idx][layer_id];
|
||||
if (! blockers.polys.empty()) {
|
||||
for (size_t i=0; i<polygon.points.size(); ++i) {
|
||||
if (is_inside(polygon.points[i], blockers))
|
||||
blockers_idxs.emplace_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -543,17 +666,17 @@ static std::vector<size_t> find_enforcer_centers(const Polygon& polygon,
|
|||
|
||||
|
||||
|
||||
void SeamPlacer::apply_custom_seam(const Polygon& polygon,
|
||||
void SeamPlacer::apply_custom_seam(const Polygon& polygon, size_t po_idx,
|
||||
std::vector<float>& penalties,
|
||||
const std::vector<float>& lengths,
|
||||
int layer_id, SeamPosition seam_position) const
|
||||
{
|
||||
if (! is_custom_seam_on_layer(layer_id))
|
||||
if (! is_custom_seam_on_layer(layer_id, po_idx))
|
||||
return;
|
||||
|
||||
std::vector<size_t> enforcers_idxs;
|
||||
std::vector<size_t> blockers_idxs;
|
||||
this->get_enforcers_and_blockers(layer_id, polygon, enforcers_idxs, blockers_idxs);
|
||||
this->get_enforcers_and_blockers(layer_id, polygon, po_idx, enforcers_idxs, blockers_idxs);
|
||||
|
||||
for (size_t i : enforcers_idxs) {
|
||||
assert(i < penalties.size());
|
||||
|
|
|
|||
|
|
@ -3,15 +3,17 @@
|
|||
|
||||
#include <optional>
|
||||
|
||||
#include "libslic3r/ExPolygon.hpp"
|
||||
#include "libslic3r/Polygon.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
#include "libslic3r/AABBTreeIndirect.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class PrintObject;
|
||||
class ExtrusionLoop;
|
||||
class Print;
|
||||
class Layer;
|
||||
namespace EdgeGrid { class Grid; }
|
||||
|
||||
|
||||
|
|
@ -39,14 +41,31 @@ class SeamPlacer {
|
|||
public:
|
||||
void init(const Print& print);
|
||||
|
||||
Point get_seam(const size_t layer_idx, const SeamPosition seam_position,
|
||||
Point get_seam(const Layer& layer, const SeamPosition seam_position,
|
||||
const ExtrusionLoop& loop, Point last_pos,
|
||||
coordf_t nozzle_diameter, const PrintObject* po,
|
||||
bool was_clockwise, const EdgeGrid::Grid* lower_layer_edge_grid);
|
||||
|
||||
using TreeType = AABBTreeIndirect::Tree<2, coord_t>;
|
||||
using AlignedBoxType = Eigen::AlignedBox<TreeType::CoordType, TreeType::NumDimensions>;
|
||||
|
||||
private:
|
||||
std::vector<ExPolygons> m_enforcers;
|
||||
std::vector<ExPolygons> m_blockers;
|
||||
|
||||
struct CustomTrianglesPerLayer {
|
||||
Polygons polys;
|
||||
TreeType tree;
|
||||
};
|
||||
|
||||
// Just a cache to save some lookups.
|
||||
const Layer* m_last_layer_po = nullptr;
|
||||
coordf_t m_last_print_z = -1.;
|
||||
const PrintObject* m_last_po = nullptr;
|
||||
|
||||
bool m_last_loop_was_external = true;
|
||||
|
||||
std::vector<std::vector<CustomTrianglesPerLayer>> m_enforcers;
|
||||
std::vector<std::vector<CustomTrianglesPerLayer>> m_blockers;
|
||||
std::vector<const PrintObject*> m_po_list;
|
||||
|
||||
//std::map<const PrintObject*, Point> m_last_seam_position;
|
||||
SeamHistory m_seam_history;
|
||||
|
|
@ -54,32 +73,33 @@ private:
|
|||
// Get indices of points inside enforcers and blockers.
|
||||
void get_enforcers_and_blockers(size_t layer_id,
|
||||
const Polygon& polygon,
|
||||
size_t po_id,
|
||||
std::vector<size_t>& enforcers_idxs,
|
||||
std::vector<size_t>& blockers_idxs) const;
|
||||
|
||||
// Apply penalties to points inside enforcers/blockers.
|
||||
void apply_custom_seam(const Polygon& polygon,
|
||||
void apply_custom_seam(const Polygon& polygon, size_t po_id,
|
||||
std::vector<float>& penalties,
|
||||
const std::vector<float>& lengths,
|
||||
int layer_id, SeamPosition seam_position) const;
|
||||
|
||||
// Return random point of a polygon. The distribution will be uniform
|
||||
// along the contour and account for enforcers and blockers.
|
||||
Point get_random_seam(size_t layer_idx, const Polygon& polygon,
|
||||
Point get_random_seam(size_t layer_idx, const Polygon& polygon, size_t po_id,
|
||||
bool* saw_custom = nullptr) const;
|
||||
|
||||
// Is there any enforcer/blocker on this layer?
|
||||
bool is_custom_seam_on_layer(size_t layer_id) const {
|
||||
return is_custom_enforcer_on_layer(layer_id)
|
||||
|| is_custom_blocker_on_layer(layer_id);
|
||||
bool is_custom_seam_on_layer(size_t layer_id, size_t po_idx) const {
|
||||
return is_custom_enforcer_on_layer(layer_id, po_idx)
|
||||
|| is_custom_blocker_on_layer(layer_id, po_idx);
|
||||
}
|
||||
|
||||
bool is_custom_enforcer_on_layer(size_t layer_id) const {
|
||||
return (! m_enforcers.empty() && ! m_enforcers[layer_id].empty());
|
||||
bool is_custom_enforcer_on_layer(size_t layer_id, size_t po_idx) const {
|
||||
return (! m_enforcers.at(po_idx).empty() && ! m_enforcers.at(po_idx)[layer_id].polys.empty());
|
||||
}
|
||||
|
||||
bool is_custom_blocker_on_layer(size_t layer_id) const {
|
||||
return (! m_blockers.empty() && ! m_blockers[layer_id].empty());
|
||||
bool is_custom_blocker_on_layer(size_t layer_id, size_t po_idx) const {
|
||||
return (! m_blockers.at(po_idx).empty() && ! m_blockers.at(po_idx)[layer_id].polys.empty());
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue