Merge branch 'master' of https://github.com/prusa3d/PrusaSlicer into et_gcode_window

This commit is contained in:
enricoturri1966 2021-03-03 09:07:28 +01:00
commit da7d7ae11b
61 changed files with 6895 additions and 646 deletions

View file

@ -68,14 +68,12 @@ void AppConfig::set_defaults()
if (get("export_sources_full_pathnames").empty())
set("export_sources_full_pathnames", "0");
#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
#ifdef _WIN32
if (get("associate_3mf").empty())
set("associate_3mf", "0");
if (get("associate_stl").empty())
set("associate_stl", "0");
#endif // _WIN32
#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
// remove old 'use_legacy_opengl' parameter from this config, if present
if (!get("use_legacy_opengl").empty())
@ -127,14 +125,12 @@ void AppConfig::set_defaults()
if (get("color_mapinulation_panel").empty())
set("color_mapinulation_panel", "0");
}
#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
else {
#ifdef _WIN32
if (get("associate_gcode").empty())
set("associate_gcode", "0");
#endif // _WIN32
}
#endif // ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
if (get("seq_top_layer_only").empty())
set("seq_top_layer_only", "1");
@ -156,12 +152,10 @@ void AppConfig::set_defaults()
if (get("show_splash_screen").empty())
set("show_splash_screen", "1");
#if ENABLE_CTRL_M_ON_WINDOWS
#ifdef _WIN32
if (get("use_legacy_3DConnexion").empty())
set("use_legacy_3DConnexion", "0");
#endif // _WIN32
#endif // ENABLE_CTRL_M_ON_WINDOWS
// Remove legacy window positions/sizes
erase("", "main_frame_maximized");

View file

@ -145,6 +145,8 @@ add_library(libslic3r STATIC
Point.hpp
Polygon.cpp
Polygon.hpp
MutablePolygon.cpp
MutablePolygon.hpp
PolygonTrimmer.cpp
PolygonTrimmer.hpp
Polyline.cpp

View file

@ -667,6 +667,14 @@ namespace DoExport {
break;
}
}
if (ret.size() < MAX_TAGS_COUNT) {
const CustomGCode::Info& custom_gcode_per_print_z = print.model().custom_gcode_per_print_z;
for (const auto& gcode : custom_gcode_per_print_z.gcodes) {
check(_(L("Custom G-code")), gcode.extra);
if (ret.size() == MAX_TAGS_COUNT)
break;
}
}
return ret;
}
@ -1705,7 +1713,9 @@ namespace ProcessLayer
{
static std::string emit_custom_gcode_per_print_z(
GCode &gcodegen,
const CustomGCode::Item *custom_gcode,
unsigned int current_extruder_id,
// ID of the first extruder printing this layer.
unsigned int first_extruder_id,
const PrintConfig &config)
@ -1746,12 +1756,14 @@ namespace ProcessLayer
// && !MMU1
) {
//! FIXME_in_fw show message during print pause
gcode += config.pause_print_gcode;// pause print
DynamicConfig cfg;
cfg.set_key_value("color_change_extruder", new ConfigOptionInt(m600_extruder_before_layer));
gcode += gcodegen.placeholder_parser_process("pause_print_gcode", config.pause_print_gcode, current_extruder_id, &cfg);
gcode += "\n";
gcode += "M117 Change filament for Extruder " + std::to_string(m600_extruder_before_layer) + "\n";
}
else {
gcode += config.color_change_gcode;//ColorChangeCode;
gcode += gcodegen.placeholder_parser_process("color_change_gcode", config.color_change_gcode, current_extruder_id);
gcode += "\n";
}
}
@ -1767,7 +1779,7 @@ namespace ProcessLayer
//! FIXME_in_fw show message during print pause
if (!pause_print_msg.empty())
gcode += "M117 " + pause_print_msg + "\n";
gcode += config.pause_print_gcode;
gcode += gcodegen.placeholder_parser_process("pause_print_gcode", config.pause_print_gcode, current_extruder_id);
}
else {
// add tag for processor
@ -1776,8 +1788,8 @@ namespace ProcessLayer
#else
gcode += ";" + GCodeProcessor::Custom_Code_Tag + "\n";
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
if (gcode_type == CustomGCode::Template) // Template Cistom Gcode
gcode += config.template_custom_gcode;
if (gcode_type == CustomGCode::Template) // Template Custom Gcode
gcode += gcodegen.placeholder_parser_process("template_custom_gcode", config.template_custom_gcode, current_extruder_id);
else // custom Gcode
gcode += custom_gcode->extra;
@ -1803,7 +1815,6 @@ namespace Skirt {
static std::map<unsigned int, std::pair<size_t, size_t>> make_skirt_loops_per_extruder_1st_layer(
const Print &print,
const std::vector<GCode::LayerToPrint> & /*layers */,
const LayerTools &layer_tools,
// Heights (print_z) at which the skirt has already been extruded.
std::vector<coordf_t> &skirt_done)
@ -1811,7 +1822,8 @@ namespace Skirt {
// Extrude skirt at the print_z of the raft layers and normal object layers
// not at the print_z of the interlaced support material layers.
std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder_out;
if (skirt_done.empty() && print.has_skirt() && ! print.skirt().entities.empty()) {
assert(skirt_done.empty());
if (skirt_done.empty() && print.has_skirt() && ! print.skirt().entities.empty() && layer_tools.has_skirt) {
skirt_loops_per_extruder_all_printing(print, layer_tools, skirt_loops_per_extruder_out);
skirt_done.emplace_back(layer_tools.print_z);
}
@ -1820,36 +1832,34 @@ namespace Skirt {
static std::map<unsigned int, std::pair<size_t, size_t>> make_skirt_loops_per_extruder_other_layers(
const Print &print,
const std::vector<GCode::LayerToPrint> &layers,
const LayerTools &layer_tools,
// First non-empty support layer.
const SupportLayer *support_layer,
// Heights (print_z) at which the skirt has already been extruded.
std::vector<coordf_t> &skirt_done)
{
// Extrude skirt at the print_z of the raft layers and normal object layers
// not at the print_z of the interlaced support material layers.
std::map<unsigned int, std::pair<size_t, size_t>> skirt_loops_per_extruder_out;
if (print.has_skirt() && ! print.skirt().entities.empty() &&
if (print.has_skirt() && ! print.skirt().entities.empty() && layer_tools.has_skirt &&
// Not enough skirt layers printed yet.
//FIXME infinite or high skirt does not make sense for sequential print!
(skirt_done.size() < (size_t)print.config().skirt_height.value || print.has_infinite_skirt()) &&
(skirt_done.size() < (size_t)print.config().skirt_height.value || print.has_infinite_skirt())) {
bool valid = ! skirt_done.empty() && skirt_done.back() < layer_tools.print_z - EPSILON;
assert(valid);
// This print_z has not been extruded yet (sequential print)
// FIXME: The skirt_done should not be empty at this point. The check is a workaround
// of https://github.com/prusa3d/PrusaSlicer/issues/5652, but it deserves a real fix.
(! skirt_done.empty() && skirt_done.back() < layer_tools.print_z - EPSILON) &&
// and this layer is an object layer, or it is a raft layer.
(layer_tools.has_object || support_layer->id() < (size_t)support_layer->object()->config().raft_layers.value)) {
if (valid) {
#if 0
// Prime just the first printing extruder. This is original Slic3r's implementation.
skirt_loops_per_extruder_out[layer_tools.extruders.front()] = std::pair<size_t, size_t>(0, print.config().skirts.value);
// Prime just the first printing extruder. This is original Slic3r's implementation.
skirt_loops_per_extruder_out[layer_tools.extruders.front()] = std::pair<size_t, size_t>(0, print.config().skirts.value);
#else
// Prime all extruders planned for this layer, see
// https://github.com/prusa3d/PrusaSlicer/issues/469#issuecomment-322450619
skirt_loops_per_extruder_all_printing(print, layer_tools, skirt_loops_per_extruder_out);
// Prime all extruders planned for this layer, see
// https://github.com/prusa3d/PrusaSlicer/issues/469#issuecomment-322450619
skirt_loops_per_extruder_all_printing(print, layer_tools, skirt_loops_per_extruder_out);
#endif
assert(!skirt_done.empty());
skirt_done.emplace_back(layer_tools.print_z);
assert(!skirt_done.empty());
skirt_done.emplace_back(layer_tools.print_z);
}
}
return skirt_loops_per_extruder_out;
}
@ -1987,13 +1997,13 @@ void GCode::process_layer(
if (single_object_instance_idx == size_t(-1)) {
// Normal (non-sequential) print.
gcode += ProcessLayer::emit_custom_gcode_per_print_z(layer_tools.custom_gcode, first_extruder_id, print.config());
gcode += ProcessLayer::emit_custom_gcode_per_print_z(*this, layer_tools.custom_gcode, m_writer.extruder()->id(), first_extruder_id, print.config());
}
// Extrude skirt at the print_z of the raft layers and normal object layers
// not at the print_z of the interlaced support material layers.
skirt_loops_per_extruder = first_layer ?
Skirt::make_skirt_loops_per_extruder_1st_layer(print, layers, layer_tools, m_skirt_done) :
Skirt::make_skirt_loops_per_extruder_other_layers(print, layers, layer_tools, support_layer, m_skirt_done);
Skirt::make_skirt_loops_per_extruder_1st_layer(print, layer_tools, m_skirt_done) :
Skirt::make_skirt_loops_per_extruder_other_layers(print, layer_tools, m_skirt_done);
// Group extrusions by an extruder, then by an object, an island and a region.
std::map<unsigned int, std::vector<ObjectByExtruder>> by_extruder;
@ -2840,9 +2850,11 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
Polyline travel { this->last_pos(), point };
// check whether a straight travel move would need retraction
bool needs_retraction = this->needs_retraction(travel, role);
bool needs_retraction = this->needs_retraction(travel, role);
// check whether wipe could be disabled without causing visible stringing
bool could_be_wipe_disabled = false;
bool could_be_wipe_disabled = false;
// Save state of use_external_mp_once for the case that will be needed to call twice m_avoid_crossing_perimeters.travel_to.
const bool used_external_mp_once = m_avoid_crossing_perimeters.used_external_mp_once();
// if a retraction would be needed, try to use avoid_crossing_perimeters to plan a
// multi-hop travel path inside the configuration space
@ -2870,8 +2882,13 @@ std::string GCode::travel_to(const Point &point, ExtrusionRole role, std::string
// Because of it, it is necessary to call avoid crossing perimeters again with new starting point after calling retraction()
// FIXME Lukas H.: Try to predict if this second calling of avoid crossing perimeters will be needed or not. It could save computations.
if (last_post_before_retract != this->last_pos() && m_config.avoid_crossing_perimeters) {
Polyline retract_travel = m_avoid_crossing_perimeters.travel_to(*this, point);
travel = std::move(retract_travel);
// If in the previous call of m_avoid_crossing_perimeters.travel_to was use_external_mp_once set to true restore this value for next call.
if (used_external_mp_once)
m_avoid_crossing_perimeters.use_external_mp_once();
travel = m_avoid_crossing_perimeters.travel_to(*this, point);
// If state of use_external_mp_once was changed reset it to right value.
if (used_external_mp_once)
m_avoid_crossing_perimeters.reset_once_modifiers();
}
} else
// Reset the wipe path when traveling, so one would not wipe along an old path.

View file

@ -18,6 +18,7 @@ public:
// Routing around the objects vs. inside a single object.
void use_external_mp(bool use = true) { m_use_external_mp = use; };
void use_external_mp_once() { m_use_external_mp_once = true; }
bool used_external_mp_once() { return m_use_external_mp_once; }
void disable_once() { m_disabled_once = true; }
bool disabled_once() const { return m_disabled_once; }
void reset_once_modifiers() { m_use_external_mp_once = false; m_disabled_once = false; }

View file

@ -4,6 +4,9 @@
#include "GCodeProcessor.hpp"
#include <boost/log/trivial.hpp>
#if ENABLE_VALIDATE_CUSTOM_GCODE
#include <boost/algorithm/string/predicate.hpp>
#endif // ENABLE_VALIDATE_CUSTOM_GCODE
#include <boost/nowide/fstream.hpp>
#include <boost/nowide/cstdio.hpp>
#if ENABLE_GCODE_WINDOW
@ -597,12 +600,6 @@ const std::vector<std::pair<GCodeProcessor::EProducer, std::string>> GCodeProces
unsigned int GCodeProcessor::s_result_id = 0;
#if ENABLE_VALIDATE_CUSTOM_GCODE
static inline bool starts_with(const std::string_view comment, const std::string_view tag)
{
size_t tag_len = tag.size();
return comment.size() >= tag_len && comment.substr(0, tag_len) == tag;
}
bool GCodeProcessor::contains_reserved_tag(const std::string& gcode, std::string& found_tag)
{
bool ret = false;
@ -613,7 +610,7 @@ bool GCodeProcessor::contains_reserved_tag(const std::string& gcode, std::string
if (comment.length() > 2 && comment.front() == ';') {
comment = comment.substr(1);
for (const std::string& s : Reserved_Tags) {
if (starts_with(comment, s)) {
if (boost::starts_with(comment, s)) {
ret = true;
found_tag = comment;
parser.quit_parsing();
@ -638,7 +635,7 @@ bool GCodeProcessor::contains_reserved_tags(const std::string& gcode, unsigned i
if (comment.length() > 2 && comment.front() == ';') {
comment = comment.substr(1);
for (const std::string& s : Reserved_Tags) {
if (starts_with(comment, s)) {
if (boost::starts_with(comment, s)) {
ret = true;
found_tag.push_back(comment);
if (found_tag.size() == max_count) {
@ -681,6 +678,8 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_extruder_colors[i] = static_cast<unsigned char>(i);
}
m_extruder_temps.resize(extruders_count);
m_filament_diameters.resize(config.filament_diameter.values.size());
for (size_t i = 0; i < config.filament_diameter.values.size(); ++i) {
m_filament_diameters[i] = static_cast<float>(config.filament_diameter.values[i]);
@ -708,10 +707,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
}
m_time_processor.export_remaining_time_enabled = config.remaining_times.value;
#if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
m_use_volumetric_e = config.use_volumetric_e;
#endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
}
void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
@ -782,6 +778,8 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
m_extruder_colors[i] = static_cast<unsigned char>(i);
}
m_extruder_temps.resize(m_result.extruders_count);
const ConfigOptionFloats* filament_load_time = config.option<ConfigOptionFloats>("filament_load_time");
if (filament_load_time != nullptr) {
m_time_processor.filament_load_times.resize(filament_load_time->values.size());
@ -873,11 +871,9 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
if (m_time_processor.machine_limits.machine_max_acceleration_x.values.size() > 1)
enable_stealth_time_estimator(true);
#if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
const ConfigOptionBool* use_volumetric_e = config.option<ConfigOptionBool>("use_volumetric_e");
if (use_volumetric_e != nullptr)
m_use_volumetric_e = use_volumetric_e->value;
#endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
}
void GCodeProcessor::enable_stealth_time_estimator(bool enabled)
@ -918,6 +914,10 @@ void GCodeProcessor::reset()
for (size_t i = 0; i < Min_Extruder_Count; ++i) {
m_extruder_colors[i] = static_cast<unsigned char>(i);
}
m_extruder_temps.resize(Min_Extruder_Count);
for (size_t i = 0; i < Min_Extruder_Count; ++i) {
m_extruder_temps[i] = 0.0f;
}
m_filament_diameters = std::vector<float>(Min_Extruder_Count, 1.75f);
m_extruded_last_z = 0.0f;
@ -933,9 +933,7 @@ void GCodeProcessor::reset()
m_result.reset();
m_result.id = ++s_result_id;
#if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
m_use_volumetric_e = false;
#endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
m_mm3_per_mm_compare.reset();
@ -1136,9 +1134,11 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line)
case 1: { process_M1(line); break; } // Sleep or Conditional stop
case 82: { process_M82(line); break; } // Set extruder to absolute mode
case 83: { process_M83(line); break; } // Set extruder to relative mode
case 104: { process_M104(line); break; } // Set extruder temperature
case 106: { process_M106(line); break; } // Set fan speed
case 107: { process_M107(line); break; } // Disable fan
case 108: { process_M108(line); break; } // Set tool (Sailfish)
case 109: { process_M109(line); break; } // Set extruder temperature and wait
case 132: { process_M132(line); break; } // Recall stored home offsets
case 135: { process_M135(line); break; } // Set tool (MakerWare)
case 201: { process_M201(line); break; } // Set max printing acceleration
@ -1171,14 +1171,6 @@ void GCodeProcessor::process_gcode_line(const GCodeReader::GCodeLine& line)
}
}
#if !ENABLE_VALIDATE_CUSTOM_GCODE
static inline bool starts_with(const std::string_view comment, const std::string_view tag)
{
size_t tag_len = tag.size();
return comment.size() >= tag_len && comment.substr(0, tag_len) == tag;
}
#endif // !ENABLE_VALIDATE_CUSTOM_GCODE
#if __has_include(<charconv>)
template <typename T, typename = void>
struct is_from_chars_convertible : std::false_type {};
@ -1232,37 +1224,37 @@ void GCodeProcessor::process_tags(const std::string_view comment)
#if ENABLE_VALIDATE_CUSTOM_GCODE
// extrusion role tag
if (starts_with(comment, reserved_tag(ETags::Role))) {
if (boost::starts_with(comment, reserved_tag(ETags::Role))) {
m_extrusion_role = ExtrusionEntity::string_to_role(comment.substr(reserved_tag(ETags::Role).length()));
return;
}
// wipe start tag
if (starts_with(comment, reserved_tag(ETags::Wipe_Start))) {
if (boost::starts_with(comment, reserved_tag(ETags::Wipe_Start))) {
m_wiping = true;
return;
}
// wipe end tag
if (starts_with(comment, reserved_tag(ETags::Wipe_End))) {
if (boost::starts_with(comment, reserved_tag(ETags::Wipe_End))) {
m_wiping = false;
return;
}
#else
// extrusion role tag
if (starts_with(comment, Extrusion_Role_Tag)) {
if (boost::starts_with(comment, Extrusion_Role_Tag)) {
m_extrusion_role = ExtrusionEntity::string_to_role(comment.substr(Extrusion_Role_Tag.length()));
return;
}
// wipe start tag
if (starts_with(comment, Wipe_Start_Tag)) {
if (boost::starts_with(comment, Wipe_Start_Tag)) {
m_wiping = true;
return;
}
// wipe end tag
if (starts_with(comment, Wipe_End_Tag)) {
if (boost::starts_with(comment, Wipe_End_Tag)) {
m_wiping = false;
return;
}
@ -1271,26 +1263,26 @@ void GCodeProcessor::process_tags(const std::string_view comment)
if (!m_producers_enabled || m_producer == EProducer::PrusaSlicer) {
#if ENABLE_VALIDATE_CUSTOM_GCODE
// height tag
if (starts_with(comment, reserved_tag(ETags::Height))) {
if (boost::starts_with(comment, reserved_tag(ETags::Height))) {
if (!parse_number(comment.substr(reserved_tag(ETags::Height).size()), m_forced_height))
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ").";
return;
}
// width tag
if (starts_with(comment, reserved_tag(ETags::Width))) {
if (boost::starts_with(comment, reserved_tag(ETags::Width))) {
if (!parse_number(comment.substr(reserved_tag(ETags::Width).size()), m_forced_width))
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ").";
return;
}
#else
// height tag
if (starts_with(comment, Height_Tag)) {
if (boost::starts_with(comment, Height_Tag)) {
if (!parse_number(comment.substr(Height_Tag.size()), m_forced_height))
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Height (" << comment << ").";
return;
}
// width tag
if (starts_with(comment, Width_Tag)) {
if (boost::starts_with(comment, Width_Tag)) {
if (!parse_number(comment.substr(Width_Tag.size()), m_forced_width))
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Width (" << comment << ").";
return;
@ -1300,9 +1292,9 @@ void GCodeProcessor::process_tags(const std::string_view comment)
#if ENABLE_VALIDATE_CUSTOM_GCODE
// color change tag
if (starts_with(comment, reserved_tag(ETags::Color_Change))) {
if (boost::starts_with(comment, reserved_tag(ETags::Color_Change))) {
unsigned char extruder_id = 0;
if (starts_with(comment.substr(reserved_tag(ETags::Color_Change).size()), ",T")) {
if (boost::starts_with(comment.substr(reserved_tag(ETags::Color_Change).size()), ",T")) {
int eid;
if (!parse_number(comment.substr(reserved_tag(ETags::Color_Change).size() + 2), eid) || eid < 0 || eid > 255) {
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Color_Change (" << comment << ").";
@ -1346,9 +1338,9 @@ void GCodeProcessor::process_tags(const std::string_view comment)
}
#else
// color change tag
if (starts_with(comment, Color_Change_Tag)) {
if (boost::starts_with(comment, Color_Change_Tag)) {
unsigned char extruder_id = 0;
if (starts_with(comment.substr(Color_Change_Tag.size()), ",T")) {
if (boost::starts_with(comment.substr(Color_Change_Tag.size()), ",T")) {
int eid;
if (! parse_number(comment.substr(Color_Change_Tag.size() + 2), eid) || eid < 0 || eid > 255) {
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Color_Change (" << comment << ").";
@ -1394,7 +1386,7 @@ void GCodeProcessor::process_tags(const std::string_view comment)
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
// mm3_per_mm print tag
if (starts_with(comment, Mm3_Per_Mm_Tag)) {
if (boost::starts_with(comment, Mm3_Per_Mm_Tag)) {
if (! parse_number(comment.substr(Mm3_Per_Mm_Tag.size()), m_mm3_per_mm_compare.last_tag_value))
BOOST_LOG_TRIVIAL(error) << "GCodeProcessor encountered an invalid value for Mm3_Per_Mm (" << comment << ").";
return;
@ -1846,14 +1838,10 @@ void GCodeProcessor::process_G0(const GCodeReader::GCodeLine& line)
void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
{
#if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
float filament_diameter = (static_cast<size_t>(m_extruder_id) < m_filament_diameters.size()) ? m_filament_diameters[m_extruder_id] : m_filament_diameters.back();
float filament_radius = 0.5f * filament_diameter;
float area_filament_cross_section = static_cast<float>(M_PI) * sqr(filament_radius);
auto absolute_position = [this, area_filament_cross_section](Axis axis, const GCodeReader::GCodeLine& lineG1) {
#else
auto absolute_position = [this](Axis axis, const GCodeReader::GCodeLine& lineG1) {
#endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
bool is_relative = (m_global_positioning_type == EPositioningType::Relative);
if (axis == E)
is_relative |= (m_e_local_positioning_type == EPositioningType::Relative);
@ -1861,10 +1849,8 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
if (lineG1.has(Slic3r::Axis(axis))) {
float lengthsScaleFactor = (m_units == EUnits::Inches) ? INCHES_TO_MM : 1.0f;
float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor;
#if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
if (axis == E && m_use_volumetric_e)
ret /= area_filament_cross_section;
#endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
return is_relative ? m_start_position[axis] + ret : m_origin[axis] + ret;
}
else
@ -1922,11 +1908,6 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
if (type == EMoveType::Extrude) {
float delta_xyz = std::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]));
#if !ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
float filament_diameter = (static_cast<size_t>(m_extruder_id) < m_filament_diameters.size()) ? m_filament_diameters[m_extruder_id] : m_filament_diameters.back();
float filament_radius = 0.5f * filament_diameter;
float area_filament_cross_section = static_cast<float>(M_PI) * sqr(filament_radius);
#endif // !ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
float volume_extruded_filament = area_filament_cross_section * delta_pos[E];
float area_toolpath_cross_section = volume_extruded_filament / delta_xyz;
@ -2235,6 +2216,13 @@ void GCodeProcessor::process_M83(const GCodeReader::GCodeLine& line)
m_e_local_positioning_type = EPositioningType::Relative;
}
void GCodeProcessor::process_M104(const GCodeReader::GCodeLine& line)
{
float new_temp;
if (line.has_value('S', new_temp))
m_extruder_temps[m_extruder_id] = new_temp;
}
void GCodeProcessor::process_M106(const GCodeReader::GCodeLine& line)
{
if (!line.has('P')) {
@ -2267,6 +2255,21 @@ void GCodeProcessor::process_M108(const GCodeReader::GCodeLine& line)
process_T(cmd.substr(pos));
}
void GCodeProcessor::process_M109(const GCodeReader::GCodeLine& line)
{
float new_temp;
if (line.has_value('R', new_temp)) {
float val;
if (line.has_value('T', val)) {
size_t eid = static_cast<size_t>(val);
if (eid < m_extruder_temps.size())
m_extruder_temps[eid] = new_temp;
}
else
m_extruder_temps[m_extruder_id] = new_temp;
}
}
void GCodeProcessor::process_M132(const GCodeReader::GCodeLine& line)
{
// This command is used by Makerbot to load the current home position from EEPROM
@ -2555,6 +2558,7 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
m_height,
m_mm3_per_mm,
m_fan_speed,
m_extruder_temps[m_extruder_id],
static_cast<float>(m_result.moves.size())
};
m_result.moves.emplace_back(vertex);

View file

@ -121,6 +121,7 @@ namespace Slic3r {
private:
using AxisCoords = std::array<float, 4>;
using ExtruderColors = std::vector<unsigned char>;
using ExtruderTemps = std::vector<float>;
enum class EUnits : unsigned char
{
@ -211,6 +212,7 @@ namespace Slic3r {
float height{ 0.0f }; // mm
float mm3_per_mm{ 0.0f };
float fan_speed{ 0.0f }; // percentage
float temperature{ 0.0f }; // Celsius degrees
float time{ 0.0f }; // s
float volumetric_rate() const { return feedrate * mm3_per_mm; }
@ -320,6 +322,7 @@ namespace Slic3r {
float height{ 0.0f }; // mm
float mm3_per_mm{ 0.0f };
float fan_speed{ 0.0f }; // percentage
float temperature{ 0.0f }; // Celsius degrees
float time{ 0.0f }; // s
float volumetric_rate() const { return feedrate * mm3_per_mm; }
@ -470,14 +473,13 @@ namespace Slic3r {
ExtrusionRole m_extrusion_role;
unsigned char m_extruder_id;
ExtruderColors m_extruder_colors;
ExtruderTemps m_extruder_temps;
std::vector<float> m_filament_diameters;
float m_extruded_last_z;
unsigned int m_g1_line_id;
unsigned int m_layer_id;
CpColor m_cp_color;
#if ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
bool m_use_volumetric_e;
#endif // ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING
enum class EProducer
{
@ -590,6 +592,9 @@ namespace Slic3r {
// Set extruder to relative mode
void process_M83(const GCodeReader::GCodeLine& line);
// Set extruder temperature
void process_M104(const GCodeReader::GCodeLine& line);
// Set fan speed
void process_M106(const GCodeReader::GCodeLine& line);
@ -599,6 +604,9 @@ namespace Slic3r {
// Set tool (Sailfish)
void process_M108(const GCodeReader::GCodeLine& line);
// Set extruder temperature and wait
void process_M109(const GCodeReader::GCodeLine& line);
// Recall stored home offsets
void process_M132(const GCodeReader::GCodeLine& line);

View file

@ -70,6 +70,20 @@ unsigned int LayerTools::extruder(const ExtrusionEntityCollection &extrusions, c
return (extruder == 0) ? 0 : extruder - 1;
}
static double calc_max_layer_height(const PrintConfig &config, double max_object_layer_height)
{
double max_layer_height = std::numeric_limits<double>::max();
for (size_t i = 0; i < config.nozzle_diameter.values.size(); ++ i) {
double mlh = config.max_layer_height.values[i];
if (mlh == 0.)
mlh = 0.75 * config.nozzle_diameter.values[i];
max_layer_height = std::min(max_layer_height, mlh);
}
// The Prusa3D Fast (0.35mm layer height) print profile sets a higher layer height than what is normally allowed
// by the nozzle. This is a hack and it works by increasing extrusion width. See GH #3919.
return std::max(max_layer_height, max_object_layer_height);
}
// For the use case when each object is printed separately
// (print.config().complete_objects is true).
ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material)
@ -87,6 +101,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
zs.emplace_back(layer->print_z);
this->initialize_layers(zs);
}
double max_layer_height = calc_max_layer_height(object.print()->config(), object.config().layer_height);
// Collect extruders reuqired to print the layers.
this->collect_extruders(object, std::vector<std::pair<double, unsigned int>>());
@ -94,9 +109,11 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
// Reorder the extruders to minimize tool switches.
this->reorder_extruders(first_extruder);
this->fill_wipe_tower_partitions(object.print()->config(), object.layers().front()->print_z - object.layers().front()->height, object.config().layer_height);
this->fill_wipe_tower_partitions(object.print()->config(), object.layers().front()->print_z - object.layers().front()->height, max_layer_height);
this->collect_extruder_statistics(prime_multi_material);
this->mark_skirt_layers(object.print()->config(), max_layer_height);
}
// For the use case when all objects are printed at once.
@ -128,6 +145,7 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
}
this->initialize_layers(zs);
}
max_layer_height = calc_max_layer_height(print.config(), max_layer_height);
// Use the extruder switches from Model::custom_gcode_per_print_z to override the extruder to print the object.
// Do it only if all the objects were configured to be printed with a single extruder.
@ -150,6 +168,8 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
this->fill_wipe_tower_partitions(print.config(), object_bottom_z, max_layer_height);
this->collect_extruder_statistics(prime_multi_material);
this->mark_skirt_layers(print.config(), max_layer_height);
}
void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs)
@ -321,7 +341,7 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id)
}
}
void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z, coordf_t max_object_layer_height)
void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z, coordf_t max_layer_height)
{
if (m_layer_tools.empty())
return;
@ -347,17 +367,6 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_
lt.has_wipe_tower = (lt.has_object && lt.wipe_tower_partitions > 0) || lt.print_z < object_bottom_z + EPSILON;
// Test for a raft, insert additional wipe tower layer to fill in the raft separation gap.
double max_layer_height = std::numeric_limits<double>::max();
for (size_t i = 0; i < config.nozzle_diameter.values.size(); ++ i) {
double mlh = config.max_layer_height.values[i];
if (mlh == 0.)
mlh = 0.75 * config.nozzle_diameter.values[i];
max_layer_height = std::min(max_layer_height, mlh);
}
// The Prusa3D Fast (0.35mm layer height) print profile sets a higher layer height than what is normally allowed
// by the nozzle. This is a hack and it works by increasing extrusion width. See GH #3919.
max_layer_height = std::max(max_layer_height, max_object_layer_height);
for (size_t i = 0; i + 1 < m_layer_tools.size(); ++ i) {
const LayerTools &lt = m_layer_tools[i];
const LayerTools &lt_next = m_layer_tools[i + 1];
@ -460,6 +469,48 @@ void ToolOrdering::collect_extruder_statistics(bool prime_multi_material)
}
}
// Layers are marked for infinite skirt aka draft shield. Not all the layers have to be printed.
void ToolOrdering::mark_skirt_layers(const PrintConfig &config, coordf_t max_layer_height)
{
if (m_layer_tools.empty())
return;
if (m_layer_tools.front().extruders.empty()) {
// Empty first layer, no skirt will be printed.
//FIXME throw an exception?
return;
}
size_t i = 0;
for (;;) {
m_layer_tools[i].has_skirt = true;
size_t j = i + 1;
for (; j < m_layer_tools.size() && ! m_layer_tools[j].has_object; ++ j);
// i and j are two successive layers printing an object.
if (j == m_layer_tools.size())
// Don't print skirt above the last object layer.
break;
// Mark some printing intermediate layers as having skirt.
double last_z = m_layer_tools[i].print_z;
for (size_t k = i + 1; k < j; ++ k) {
if (m_layer_tools[k + 1].print_z - last_z > max_layer_height + EPSILON) {
// Layer k is the last one not violating the maximum layer height.
// Don't extrude skirt on empty layers.
while (m_layer_tools[k].extruders.empty())
-- k;
if (m_layer_tools[k].has_skirt) {
// Skirt cannot be generated due to empty layers, there would be a missing layer in the skirt.
//FIXME throw an exception?
break;
}
m_layer_tools[k].has_skirt = true;
last_z = m_layer_tools[k].print_z;
}
}
i = j;
}
}
// Assign a pointer to a custom G-code to the respective ToolOrdering::LayerTools.
// Ignore color changes, which are performed on a layer and for such an extruder, that the extruder will not be printing above that layer.
// If multiple events are planned over a span of a single layer, use the last one.

View file

@ -17,8 +17,6 @@ class LayerTools;
namespace CustomGCode { struct Item; }
class PrintRegion;
// Object of this class holds information about whether an extrusion is printed immediately
// after a toolchange (as part of infill/perimeter wiping) or not. One extrusion can be a part
// of several copies - this has to be taken into account.
@ -69,8 +67,6 @@ private:
const LayerTools* m_layer_tools = nullptr; // so we know which LayerTools object this belongs to
};
class LayerTools
{
public:
@ -99,6 +95,9 @@ public:
// If per layer extruder switches are inserted by the G-code preview slider, this value contains the new (1 based) extruder, with which the whole object layer is being printed with.
// If not overriden, it is set to 0.
unsigned int extruder_override = 0;
// Should a skirt be printed at this layer?
// Layers are marked for infinite skirt aka draft shield. Not all the layers have to be printed.
bool has_skirt = false;
// Will there be anything extruded on this layer for the wipe tower?
// Due to the support layers possibly interleaving the object layers,
// wipe tower will be disabled for some support only layers.
@ -120,12 +119,10 @@ private:
WipingExtrusions m_wiping_extrusions;
};
class ToolOrdering
{
public:
ToolOrdering() {}
ToolOrdering() = default;
// For the use case when each object is printed separately
// (print.config.complete_objects is true).
@ -169,6 +166,7 @@ private:
void collect_extruders(const PrintObject &object, const std::vector<std::pair<double, unsigned int>> &per_layer_extruder_switches);
void reorder_extruders(unsigned int last_extruder_id);
void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z, coordf_t max_layer_height);
void mark_skirt_layers(const PrintConfig &config, coordf_t max_layer_height);
void collect_extruder_statistics(bool prime_multi_material);
std::vector<LayerTools> m_layer_tools;
@ -182,8 +180,6 @@ private:
const PrintConfig* m_print_config_ptr = nullptr;
};
} // namespace SLic3r
#endif /* slic3r_ToolOrdering_hpp_ */

View file

@ -245,8 +245,7 @@ Polygon convex_hull(Points points)
return hull;
}
Pointf3s
convex_hull(Pointf3s points)
Pointf3s convex_hull(Pointf3s points)
{
assert(points.size() >= 3);
// sort input points
@ -304,8 +303,7 @@ convex_hull(Pointf3s points)
return hull;
}
Polygon
convex_hull(const Polygons &polygons)
Polygon convex_hull(const Polygons &polygons)
{
Points pp;
for (Polygons::const_iterator p = polygons.begin(); p != polygons.end(); ++p) {

View file

@ -0,0 +1,242 @@
#include "MutablePolygon.hpp"
#include "Line.hpp"
namespace Slic3r {
// Remove exact duplicate points. May reduce the polygon down to empty polygon.
void remove_duplicates(MutablePolygon &polygon)
{
if (! polygon.empty()) {
auto begin = polygon.begin();
auto it = begin;
for (++ it; it != begin;) {
auto prev = it.prev();
if (*prev == *it)
it = it.remove();
else
++ it;
}
}
}
// Remove nearly duplicate points. May reduce the polygon down to empty polygon.
void remove_duplicates(MutablePolygon &polygon, double eps)
{
if (! polygon.empty()) {
auto eps2 = eps * eps;
auto begin = polygon.begin();
auto it = begin;
for (++ it; it != begin;) {
auto prev = it.prev();
if ((*it - *prev).cast<double>().squaredNorm() < eps2)
it = it.remove();
else
++ it;
}
}
}
// Sample a point on line (a, b) at distance "dist" from ref_pt.
// If two points fulfill the condition, then the first one (closer to point a) is taken.
// If none of the two points falls on line (a, b), return false.
template<typename VectorType>
static inline VectorType point_on_line_at_dist(const VectorType &a, const VectorType &b, const VectorType &ref_pt, const double dist)
{
using T = typename VectorType::Scalar;
auto v = b - a;
auto l2 = v.squaredNorm();
assert(l2 > T(0));
auto vpt = ref_pt - a;
// Parameter of the foot point of ref_pt on line (a, b).
auto t = v.dot(vpt) / l2;
// Foot point of ref_pt on line (a, b).
auto foot_pt = a + t * v;
auto dfoot2 = vpt.squaredNorm() - (foot_pt - ref_pt).squaredNorm();
// Distance of the result point from the foot point, normalized to length of (a, b).
auto dfoot = dfoot2 > T(0) ? sqrt(dfoot2) / sqrt(l2) : T(0);
auto t_result = t - dfoot;
if (t_result < T(0))
t_result = t + dfoot;
t_result = Slic3r::clamp(0., 1., t_result);
return a + v * t;
}
static bool smooth_corner_complex(const Vec2d p1, MutablePolygon::iterator &it0, MutablePolygon::iterator &it2, const double shortcut_length)
{
// walk away from the corner until the shortcut > shortcut_length or it would smooth a piece inward
// - walk in both directions untill shortcut > shortcut_length
// - stop walking in one direction if it would otherwise cut off a corner in that direction
// - same in the other direction
// - stop if both are cut off
// walk by updating p0_it and p2_it
double shortcut_length2 = shortcut_length * shortcut_length;
bool forward_is_blocked = false;
bool forward_is_too_far = false;
bool backward_is_blocked = false;
bool backward_is_too_far = false;
for (;;) {
const bool forward_has_converged = forward_is_blocked || forward_is_too_far;
const bool backward_has_converged = backward_is_blocked || backward_is_too_far;
if (forward_has_converged && backward_has_converged) {
if (forward_is_too_far && backward_is_too_far && (*it0.prev() - *it2.next()).cast<double>().squaredNorm() < shortcut_length2) {
// Trim the narrowing region.
-- it0;
++ it2;
forward_is_too_far = false;
backward_is_too_far = false;
continue;
} else
break;
}
const Vec2d p0 = it0->cast<double>();
const Vec2d p2 = it2->cast<double>();
if (! forward_has_converged && (backward_has_converged || (p2 - p1).squaredNorm() < (p0 - p1).squaredNorm())) {
// walk forward
const auto it2_2 = it2.next();
const Vec2d p2_2 = it2_2->cast<double>();
if (cross2(p2 - p0, p2_2 - p0) > 0) {
forward_is_blocked = true;
} else if ((p2_2 - p0).squaredNorm() > shortcut_length2) {
forward_is_too_far = true;
} else {
it2 = it2_2; // make one step in the forward direction
backward_is_blocked = false; // invalidate data about backward walking
backward_is_too_far = false;
}
} else {
// walk backward
const auto it0_2 = it0.prev();
const Vec2d p0_2 = it0_2->cast<double>();
if (cross2(p0_2 - p0, p2 - p0_2) > 0) {
backward_is_blocked = true;
} else if ((p2 - p0_2).squaredNorm() > shortcut_length2) {
backward_is_too_far = true;
} else {
it0 = it0_2; // make one step in the backward direction
forward_is_blocked = false; // invalidate data about forward walking
forward_is_too_far = false;
}
}
if (it0.prev() == it2 || it0 == it2) {
// stop if we went all the way around the polygon
// this should only be the case for hole polygons (?)
if (forward_is_too_far && backward_is_too_far) {
// in case p0_it.prev() == p2_it :
// / .
// / /|
// | becomes | |
// \ \|
// \ .
// in case p0_it == p2_it :
// / .
// / becomes /|
// \ \|
// \ .
break;
} else {
// this whole polygon can be removed
return true;
}
}
}
const Vec2d p0 = it0->cast<double>();
const Vec2d p2 = it2->cast<double>();
const Vec2d v02 = p2 - p0;
const int64_t l2_v02 = v02.squaredNorm();
if (std::abs(l2_v02 - shortcut_length2) < shortcut_length * 10) // i.e. if (size2 < l * (l+10) && size2 > l * (l-10))
{ // v02 is approximately shortcut length
// handle this separately to avoid rounding problems below in the getPointOnLineWithDist function
// p0_it and p2_it are already correct
} else if (! backward_is_blocked && ! forward_is_blocked) {
const auto l_v02 = sqrt(l2_v02);
const Vec2d p0_2 = it0.prev()->cast<double>();
const Vec2d p2_2 = it2.next()->cast<double>();
double t = Slic3r::clamp(0., 1., (shortcut_length - l_v02) / ((p2_2 - p0_2).norm() - l_v02));
it0 = it0.prev().insert((p0 + (p0_2 - p0) * t).cast<coord_t>());
it2 = it2.insert((p2 + (p2_2 - p2) * t).cast<coord_t>());
} else if (! backward_is_blocked) {
it0 = it0.prev().insert(point_on_line_at_dist(p0, Vec2d(it0.prev()->cast<double>()), p2, shortcut_length).cast<coord_t>());
} else if (! forward_is_blocked) {
it2 = it2.insert(point_on_line_at_dist(p2, Vec2d(it2.next()->cast<double>()), p0, shortcut_length).cast<coord_t>());
} else {
// |
// __|2
// | / > shortcut cannot be of the desired length
// ___|/ .
// 0
// both are blocked and p0_it and p2_it are already correct
}
// Delete all the points between it0 and it2.
while (it0.next() != it2)
it0.next().remove();
return false;
}
void smooth_outward(MutablePolygon &polygon, double shortcut_length)
{
remove_duplicates(polygon, scaled<double>(0.01));
const int shortcut_length2 = shortcut_length * shortcut_length;
static constexpr const double cos_min_angle = -0.70710678118654752440084436210485; // cos(135 degrees)
MutablePolygon::iterator it1 = polygon.begin();
do {
const Vec2d p1 = it1->cast<double>();
auto it0 = it1.prev();
auto it2 = it1.next();
const Vec2d p0 = it0->cast<double>();
const Vec2d p2 = it2->cast<double>();
const Vec2d v1 = p0 - p1;
const Vec2d v2 = p2 - p1;
const double cos_angle = v1.dot(v2);
if (cos_angle < cos_min_angle && cross2(v1, v2) < 0) {
// Simplify the sharp angle.
const Vec2d v02 = p2 - p0;
const double l2_v02 = v02.squaredNorm();
if (l2_v02 >= shortcut_length2) {
// Trim an obtuse corner.
it1.remove();
if (l2_v02 > Slic3r::sqr(shortcut_length + SCALED_EPSILON)) {
double l2_1 = v1.squaredNorm();
double l2_2 = v2.squaredNorm();
bool trim = true;
if (cos_angle > 0.9999) {
// The triangle p0, p1, p2 is likely degenerate.
// Measure height of the triangle.
double d2 = l2_1 > l2_2 ? line_alg::distance_to_squared(Linef{ p0, p1 }, p2) : line_alg::distance_to_squared(Linef{ p2, p1 }, p0);
if (d2 < Slic3r::sqr(scaled<double>(0.02)))
trim = false;
}
if (trim) {
Vec2d bisector = v1 / l2_1 + v2 / l2_2;
double d1 = v1.dot(bisector) / l2_1;
double d2 = v2.dot(bisector) / l2_2;
double lbisector = bisector.norm();
if (d1 < shortcut_length && d2 < shortcut_length) {
it0.insert((p1 + v1 * (shortcut_length / d1)).cast<coord_t>())
.insert((p1 + v2 * (shortcut_length / d2)).cast<coord_t>());
} else if (v1.squaredNorm() < v2.squaredNorm())
it0.insert(point_on_line_at_dist(p1, p2, p0, shortcut_length).cast<coord_t>());
else
it0.insert(point_on_line_at_dist(p1, p0, p2, shortcut_length).cast<coord_t>());
}
}
} else {
bool remove_poly = smooth_corner_complex(p1, it0, it2, shortcut_length); // edits p0_it and p2_it!
if (remove_poly) {
// don't convert ListPolygon into result
return;
}
}
// update:
it1 = it2; // next point to consider for whether it's an internal corner
}
else
++ it1;
} while (it1 != polygon.begin());
}
} // namespace Slic3r

View file

@ -0,0 +1,227 @@
#ifndef slic3r_MutablePolygon_hpp_
#define slic3r_MutablePolygon_hpp_
#include "Point.hpp"
#include "Polygon.hpp"
namespace Slic3r {
class MutablePolygon
{
public:
using IndexType = int32_t;
using PointType = Point;
class const_iterator {
public:
bool operator==(const const_iterator &rhs) const { assert(m_data == rhs.m_data); assert(this->valid()); return m_idx == rhs.m_idx; }
bool operator!=(const const_iterator &rhs) const { return ! (*this == rhs); }
const_iterator& operator--() { assert(this->valid()); m_idx = m_data->at(m_idx).prev; return *this; }
const_iterator operator--(int) { const_iterator result(*this); --(*this); return result; }
const_iterator& operator++() { assert(this->valid()); m_idx = m_data->at(m_idx).next; return *this; }
const_iterator operator++(int) { const_iterator result(*this); ++(*this); return result; }
const_iterator prev() const { assert(this->valid()); return { m_data, m_data->at(m_idx).prev }; }
const_iterator next() const { assert(this->valid()); return { m_data, m_data->at(m_idx).next }; }
bool valid() const { return m_idx >= 0; }
const PointType& operator*() const { return m_data->at(m_idx).point; }
const PointType* operator->() const { return &m_data->at(m_idx).point; }
const MutablePolygon& polygon() const { assert(this->valid()); return *m_data; }
IndexType size() const { assert(this->valid()); return m_data->size(); }
private:
const_iterator(const MutablePolygon *data, IndexType idx) : m_data(data), m_idx(idx) {}
friend class MutablePolygon;
const MutablePolygon *m_data;
IndexType m_idx;
};
class iterator {
public:
bool operator==(const iterator &rhs) const { assert(m_data == rhs.m_data); assert(this->valid()); return m_idx == rhs.m_idx; }
bool operator!=(const iterator &rhs) const { return !(*this == rhs); }
iterator& operator--() { assert(this->valid()); m_idx = m_data->at(m_idx).prev; return *this; }
iterator operator--(int) { iterator result(*this); --(*this); return result; }
iterator& operator++() { assert(this->valid()); m_idx = m_data->at(m_idx).next; return *this; }
iterator operator++(int) { iterator result(*this); ++(*this); return result; }
iterator prev() const { assert(this->valid()); return { m_data, m_data->at(m_idx).prev }; }
iterator next() const { assert(this->valid()); return { m_data, m_data->at(m_idx).next }; }
bool valid() const { return m_idx >= 0; }
PointType& operator*() const { return m_data->at(m_idx).point; }
PointType* operator->() const { return &m_data->at(m_idx).point; }
MutablePolygon& polygon() const { assert(this->valid()); return *m_data; }
IndexType size() const { assert(this->valid()); return m_data->size(); }
iterator& remove() { this->m_idx = m_data->remove(*this).m_idx; return *this; }
iterator insert(const PointType pt) const { return m_data->insert(*this, pt); }
private:
iterator(MutablePolygon *data, IndexType idx) : m_data(data), m_idx(idx) {}
friend class MutablePolygon;
MutablePolygon *m_data;
IndexType m_idx;
};
MutablePolygon() = default;
MutablePolygon(const Polygon &rhs, size_t reserve = 0) : MutablePolygon(rhs.points.begin(), rhs.points.end(), reserve) {}
MutablePolygon(std::initializer_list<Point> rhs, size_t reserve = 0) : MutablePolygon(rhs.begin(), rhs.end(), reserve) {}
template<typename IT>
MutablePolygon(IT begin, IT end, size_t reserve = 0) {
m_size = IndexType(end - begin);
if (m_size > 0) {
m_head = 0;
m_data.reserve(std::max<size_t>(m_size, reserve));
auto i = IndexType(-1);
auto j = IndexType(1);
for (auto it = begin; it != end; ++ it)
m_data.push_back({ *it, i ++, j ++ });
m_data.front().prev = m_size - 1;
m_data.back ().next = 0;
}
};
Polygon polygon() const {
Polygon out;
if (this->valid()) {
out.points.reserve(this->size());
for (auto it = this->cbegin(); it != this->cend(); ++ it)
out.points.emplace_back(*it);
}
return out;
};
bool empty() const { return this->m_size == 0; }
size_t size() const { return this->m_size; }
size_t capacity() const { return this->m_data.capacity(); }
bool valid() const { return this->m_size >= 3; }
iterator begin() { return { this, m_head }; }
const_iterator cbegin() const { return { this, m_head }; }
const_iterator begin() const { return this->cbegin(); }
// End points to the last item before roll over. This is different from the usual end() concept!
iterator end() { return { this, this->empty() ? -1 : this->at(m_head).prev }; }
const_iterator cend() const { return { this, this->empty() ? -1 : this->at(m_head).prev }; }
const_iterator end() const { return this->cend(); }
// Returns iterator following the removed element. Returned iterator will become invalid if last point is removed.
// If begin() is removed, then the next element will become the new begin().
iterator remove(const iterator it) { assert(it.m_data == this); return { this, this->remove(it.m_idx) }; }
// Insert a new point before it. Returns iterator to the newly inserted point.
// begin() will not change, end() may point to the newly inserted point.
iterator insert(const iterator it, const PointType pt) { assert(it.m_data == this); return { this, this->insert(it.m_idx, pt) }; }
private:
struct LinkedPoint {
PointType point;
IndexType prev;
IndexType next;
};
std::vector<LinkedPoint> m_data;
// Number of points in the linked list.
IndexType m_size { 0 };
IndexType m_head { IndexType(-1) };
// Head of the free list.
IndexType m_head_free { IndexType(-1) };
LinkedPoint& at(IndexType i) { return m_data[i]; }
const LinkedPoint& at(IndexType i) const { return m_data[i]; }
IndexType remove(const IndexType i) {
assert(i >= 0);
assert(m_size > 0);
assert(m_head != -1);
LinkedPoint &lp = this->at(i);
IndexType prev = lp.prev;
IndexType next = lp.next;
lp.next = m_head_free;
m_head_free = i;
if (-- m_size == 0)
m_head = -1;
else if (m_head == i)
m_head = next;
assert(! this->empty() || (prev == i && next == i));
if (this->empty())
return IndexType(-1);
this->at(prev).next = next;
this->at(next).prev = prev;
return next;
}
IndexType insert(const IndexType i, const Point pt) {
assert(i >= 0);
IndexType n;
IndexType j = this->at(i).prev;
if (m_head_free == -1) {
// Allocate a new item.
n = IndexType(m_data.size());
m_data.push_back({ pt, j, i });
} else {
n = m_head_free;
LinkedPoint &nlp = this->at(n);
m_head_free = nlp.next;
nlp = { pt, j, i };
}
this->at(j).next = n;
this->at(i).prev = n;
++ m_size;
return n;
}
/*
IndexType insert(const IndexType i, const Point pt) {
assert(i >= 0);
if (this->at(i).point == pt)
return i;
IndexType j = this->at(i).next;
if (this->at(j).point == pt)
return i;
IndexType n;
if (m_head_free == -1) {
// Allocate a new item.
n = IndexType(m_data.size());
m_data.push_back({ pt, i, j });
} else {
LinkedPoint &nlp = this->at(m_head_free);
m_head_free = nlp.next;
nlp = { pt, i, j };
}
this->at(i).next = n;
this->at(j).prev = n;
++ m_size;
return n;
}
*/
};
inline bool operator==(const MutablePolygon &p1, const MutablePolygon &p2)
{
if (p1.size() != p2.size())
return false;
if (p1.empty())
return true;
auto begin = p1.cbegin();
auto it = begin;
auto it2 = p2.cbegin();
for (;;) {
if (! (*it == *it2))
return false;
if (++ it == begin)
return true;
++ it2;
}
}
inline bool operator!=(const MutablePolygon &p1, const MutablePolygon &p2) { return ! (p1 == p2); }
// Remove exact duplicate points. May reduce the polygon down to empty polygon.
void remove_duplicates(MutablePolygon &polygon);
void remove_duplicates(MutablePolygon &polygon, double eps);
void smooth_outward(MutablePolygon &polygon, double shortcut_length);
inline Polygon smooth_outward(const Polygon &polygon, double shortcut_length)
{
MutablePolygon mp(polygon, polygon.size() * 2);
smooth_outward(mp, shortcut_length);
return mp.polygon();
}
}
#endif // slic3r_MutablePolygon_hpp_

View file

@ -56,11 +56,21 @@ typedef Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign> Transform3d
inline bool operator<(const Vec2d &lhs, const Vec2d &rhs) { return lhs(0) < rhs(0) || (lhs(0) == rhs(0) && lhs(1) < rhs(1)); }
// One likely does not want to perform the cross product with a 32bit accumulator.
//inline int32_t cross2(const Vec2i32 &v1, const Vec2i32 &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
inline int64_t cross2(const Vec2i64 &v1, const Vec2i64 &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
inline float cross2(const Vec2f &v1, const Vec2f &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
inline double cross2(const Vec2d &v1, const Vec2d &v2) { return v1(0) * v2(1) - v1(1) * v2(0); }
template<int Options>
int32_t cross2(const Eigen::MatrixBase<Eigen::Matrix<int32_t, 2, 1, Options>> &v1, const Eigen::MatrixBase<Eigen::Matrix<int32_t, 2, 1, Options>> &v2) = delete;
template<typename T, int Options>
inline T cross2(const Eigen::MatrixBase<Eigen::Matrix<T, 2, 1, Options>> &v1, const Eigen::MatrixBase<Eigen::Matrix<T, 2, 1, Options>> &v2)
{
return v1(0) * v2(1) - v1(1) * v2(0);
}
template<typename Derived, typename Derived2>
inline typename Derived::Scalar cross2(const Eigen::MatrixBase<Derived> &v1, const Eigen::MatrixBase<Derived2> &v2)
{
static_assert(std::is_same<typename Derived::Scalar, typename Derived2::Scalar>::value, "cross2(): Scalar types of 1st and 2nd operand must be equal.");
return v1(0) * v2(1) - v1(1) * v2(0);
}
template<typename T, int Options>
inline Eigen::Matrix<T, 2, 1, Eigen::DontAlign> perp(const Eigen::MatrixBase<Eigen::Matrix<T, 2, 1, Options>> &v) { return Eigen::Matrix<T, 2, 1, Eigen::DontAlign>(- v.y(), v.x()); }

View file

@ -1393,9 +1393,8 @@ const std::vector<std::string>& PhysicalPrinter::printer_options()
static std::vector<std::string> s_opts;
if (s_opts.empty()) {
s_opts = {
"preset_name",
"preset_names",
"printer_technology",
// "printer_model",
"host_type",
"print_host",
"printhost_apikey",
@ -1453,11 +1452,10 @@ bool PhysicalPrinter::has_empty_config() const
void PhysicalPrinter::update_preset_names_in_config()
{
if (!preset_names.empty()) {
std::string name;
for (auto el : preset_names)
name += el + ";";
name.pop_back();
config.set_key_value("preset_name", new ConfigOptionString(name));
std::vector<std::string>& values = config.option<ConfigOptionStrings>("preset_names")->values;
values.clear();
for (auto preset : preset_names)
values.push_back(preset);
}
}
@ -1482,14 +1480,13 @@ void PhysicalPrinter::update_from_config(const DynamicPrintConfig& new_config)
{
config.apply_only(new_config, printer_options(), false);
std::string str = config.opt_string("preset_name");
std::set<std::string> values{};
if (!str.empty()) {
boost::split(values, str, boost::is_any_of(";"));
const std::vector<std::string>& values = config.option<ConfigOptionStrings>("preset_names")->values;
if (values.empty())
preset_names.clear();
else
for (const std::string& val : values)
preset_names.emplace(val);
}
preset_names = values;
}
void PhysicalPrinter::reset_presets()
@ -1817,7 +1814,7 @@ bool PhysicalPrinterCollection::delete_preset_from_printers( const std::string&
return true;
}
// Get list of printers which have more than one preset and "preset_name" preset is one of them
// Get list of printers which have more than one preset and "preset_names" preset is one of them
std::vector<std::string> PhysicalPrinterCollection::get_printers_with_preset(const std::string& preset_name)
{
std::vector<std::string> printers;
@ -1832,7 +1829,7 @@ std::vector<std::string> PhysicalPrinterCollection::get_printers_with_preset(con
return printers;
}
// Get list of printers which has only "preset_name" preset
// Get list of printers which has only "preset_names" preset
std::vector<std::string> PhysicalPrinterCollection::get_printers_with_only_preset(const std::string& preset_name)
{
std::vector<std::string> printers;

View file

@ -685,9 +685,9 @@ public:
// returns true if all presets were deleted successfully.
bool delete_preset_from_printers(const std::string& preset_name);
// Get list of printers which have more than one preset and "preset_name" preset is one of them
// Get list of printers which have more than one preset and "preset_names" preset is one of them
std::vector<std::string> get_printers_with_preset( const std::string &preset_name);
// Get list of printers which has only "preset_name" preset
// Get list of printers which has only "preset_names" preset
std::vector<std::string> get_printers_with_only_preset( const std::string &preset_name);
// Return the selected preset, without the user modifications applied.

View file

@ -876,6 +876,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
this->filament_presets[i] = loaded->name;
}
}
// 4) Load the project config values (the per extruder wipe matrix etc).
this->project_config.apply_only(config, s_project_options);

View file

@ -94,12 +94,14 @@ std::string PrintBase::output_filepath(const std::string &path, const std::strin
return path;
}
void PrintBase::status_update_warnings(ObjectID object_id, int step, PrintStateBase::WarningLevel /* warning_level */, const std::string &message)
void PrintBase::status_update_warnings(int step, PrintStateBase::WarningLevel /* warning_level */, const std::string &message, const PrintObjectBase* print_object)
{
if (this->m_status_callback)
m_status_callback(SlicingStatus(*this, step));
if (this->m_status_callback) {
auto status = print_object ? SlicingStatus(*print_object, step) : SlicingStatus(*this, step);
m_status_callback(status);
}
else if (! message.empty())
printf("%s warning: %s\n", (object_id == this->id()) ? "print" : "print object", message.c_str());
printf("%s warning: %s\n", print_object ? "print_object" : "print", message.c_str());
}
tbb::mutex& PrintObjectBase::state_mutex(PrintBase *print)
@ -114,7 +116,7 @@ std::function<void()> PrintObjectBase::cancel_callback(PrintBase *print)
void PrintObjectBase::status_update_warnings(PrintBase *print, int step, PrintStateBase::WarningLevel warning_level, const std::string &message)
{
print->status_update_warnings(this->id(), step, warning_level, message);
print->status_update_warnings(step, warning_level, message, this);
}
} // namespace Slic3r

View file

@ -481,7 +481,7 @@ protected:
// Notify UI about a new warning of a milestone "step" on this PrintBase.
// The UI will be notified by calling a status callback.
// If no status callback is registered, the message is printed to console.
void status_update_warnings(ObjectID object_id, int step, PrintStateBase::WarningLevel warning_level, const std::string &message);
void status_update_warnings(int step, PrintStateBase::WarningLevel warning_level, const std::string &message, const PrintObjectBase* print_object = nullptr);
// If the background processing stop was requested, throw CanceledException.
// To be called by the worker thread and its sub-threads (mostly launched on the TBB thread pool) regularly.
@ -528,7 +528,7 @@ protected:
PrintStateBase::TimeStamp set_done(PrintStepEnum step) {
std::pair<PrintStateBase::TimeStamp, bool> status = m_state.set_done(step, this->state_mutex(), [this](){ this->throw_if_canceled(); });
if (status.second)
this->status_update_warnings(this->id(), static_cast<int>(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string());
this->status_update_warnings(static_cast<int>(step), PrintStateBase::WarningLevel::NON_CRITICAL, std::string());
return status.first;
}
bool invalidate_step(PrintStepEnum step)
@ -550,7 +550,7 @@ protected:
std::pair<PrintStepEnum, bool> active_step = m_state.active_step_add_warning(warning_level, message, message_id, this->state_mutex());
if (active_step.second)
// Update UI.
this->status_update_warnings(this->id(), static_cast<int>(active_step.first), warning_level, message);
this->status_update_warnings(static_cast<int>(active_step.first), warning_level, message);
}
private:

View file

@ -151,11 +151,11 @@ void PrintConfigDef::init_common_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString(""));
def = this->add("preset_name", coString);
def->label = L("Printer preset name");
def->tooltip = L("Related printer preset name");
def = this->add("preset_names", coStrings);
def->label = L("Printer preset names");
def->tooltip = L("Names of presets related to the physical printer");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString(""));
def->set_default_value(new ConfigOptionStrings());
def = this->add("printhost_authorization_type", coEnum);
def->label = L("Authorization Type");
@ -3366,6 +3366,8 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
opt_key = "printhost_cafile";
} else if (opt_key == "octoprint_apikey") {
opt_key = "printhost_apikey";
} else if (opt_key == "preset_name") {
opt_key = "preset_names";
}
// Ignore the following obsolete configuration keys:

View file

@ -513,7 +513,7 @@ public:
ConfigOptionFloat support_material_interface_spacing;
ConfigOptionFloatOrPercent support_material_interface_speed;
ConfigOptionEnum<SupportMaterialPattern> support_material_pattern;
ConfigOptionEnum<SupportMaterialPattern> support_material_interface_pattern;
ConfigOptionEnum<SupportMaterialInterfacePattern> support_material_interface_pattern;
// Spacing between support material lines (the hatching distance).
ConfigOptionFloat support_material_spacing;
ConfigOptionFloat support_material_speed;

View file

@ -418,7 +418,7 @@ void PrintObject::generate_support_material()
{
if (this->set_started(posSupportMaterial)) {
this->clear_support_layers();
if (this->has_support_material() && m_layers.size() > 1) {
if ((this->has_support() && m_layers.size() > 1) || (this->has_raft() && ! m_layers.empty())) {
m_print->set_status(85, L("Generating support material"));
this->_generate_support_material();
m_print->throw_if_canceled();

View file

@ -501,7 +501,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
// If raft is to be generated, the 1st top_contact layer will contain the 1st object layer silhouette with holes filled.
// There is also a 1st intermediate layer containing bases of support columns.
// Inflate the bases of the support columns and create the raft base under the object.
MyLayersPtr raft_layers = this->generate_raft_base(object, top_contacts, interface_layers, intermediate_layers, layer_storage);
MyLayersPtr raft_layers = this->generate_raft_base(object, top_contacts, interface_layers, intermediate_layers, base_interface_layers, layer_storage);
#ifdef SLIC3R_DEBUG
for (const MyLayer *l : interface_layers)
@ -1264,6 +1264,14 @@ namespace SupportMaterialInternal {
offset(layerm->unsupported_bridge_edges, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS));
// Remove bridged areas from the supported areas.
contact_polygons = diff(contact_polygons, bridges, true);
#ifdef SLIC3R_DEBUG
static int iRun = 0;
SVG::export_expolygons(debug_out_path("support-top-contacts-remove-bridges-run%d.svg", iRun ++),
{ { { union_ex(offset(layerm->unsupported_bridge_edges, scale_(SUPPORT_MATERIAL_MARGIN), SUPPORT_SURFACES_OFFSET_PARAMETERS), false) }, { "unsupported_bridge_edges", "orange", 0.5f } },
{ { union_ex(contact_polygons, false) }, { "contact_polygons", "blue", 0.5f } },
{ { union_ex(bridges, false) }, { "bridges", "red", "black", "", scaled<coord_t>(0.1f), 0.5f } } });
#endif /* SLIC3R_DEBUG */
}
}
@ -1678,7 +1686,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
#endif /* SLIC3R_DEBUG */
}
}
#ifdef SLIC3R_DEBUG
#ifdef SLIC3R_DEBUG
SVG::export_expolygons(debug_out_path("support-top-contacts-final0-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z),
{ { { union_ex(lower_layer_polygons, false) }, { "lower_layer_polygons", "gray", 0.2f } },
{ { union_ex(*new_layer.contact_polygons, false) }, { "new_layer.contact_polygons", "yellow", 0.5f } },
@ -2538,6 +2546,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
const PrintObject &object,
const MyLayersPtr &top_contacts,
const MyLayersPtr &interface_layers,
const MyLayersPtr &base_interface_layers,
const MyLayersPtr &base_layers,
MyLayerStorage &layer_storage) const
{
@ -2573,15 +2582,19 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
// How much to inflate the support columns to be stable. This also applies to the 1st layer, if no raft layers are to be printed.
const float inflate_factor_fine = float(scale_((m_slicing_params.raft_layers() > 1) ? 0.5 : EPSILON));
const float inflate_factor_1st_layer = std::max(0.f, float(scale_(object.config().raft_first_layer_expansion)) - inflate_factor_fine);
MyLayer *contacts = top_contacts .empty() ? nullptr : top_contacts .front();
MyLayer *interfaces = interface_layers.empty() ? nullptr : interface_layers.front();
MyLayer *columns_base = base_layers .empty() ? nullptr : base_layers .front();
MyLayer *contacts = top_contacts .empty() ? nullptr : top_contacts .front();
MyLayer *interfaces = interface_layers .empty() ? nullptr : interface_layers .front();
MyLayer *base_interfaces = base_interface_layers.empty() ? nullptr : base_interface_layers.front();
MyLayer *columns_base = base_layers .empty() ? nullptr : base_layers .front();
if (contacts != nullptr && contacts->print_z > std::max(m_slicing_params.first_print_layer_height, m_slicing_params.raft_contact_top_z) + EPSILON)
// This is not the raft contact layer.
contacts = nullptr;
if (interfaces != nullptr && interfaces->bottom_print_z() > m_slicing_params.raft_interface_top_z + EPSILON)
// This is not the raft column base layer.
interfaces = nullptr;
if (base_interfaces != nullptr && base_interfaces->bottom_print_z() > m_slicing_params.raft_interface_top_z + EPSILON)
// This is not the raft column base layer.
base_interfaces = nullptr;
if (columns_base != nullptr && columns_base->bottom_print_z() > m_slicing_params.raft_interface_top_z + EPSILON)
// This is not the raft interface layer.
columns_base = nullptr;
@ -2591,6 +2604,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
polygons_append(interface_polygons, offset(contacts->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS));
if (interfaces != nullptr && ! interfaces->polygons.empty())
polygons_append(interface_polygons, offset(interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS));
if (base_interfaces != nullptr && ! base_interfaces->polygons.empty())
polygons_append(interface_polygons, offset(base_interfaces->polygons, inflate_factor_fine, SUPPORT_SURFACES_OFFSET_PARAMETERS));
// Output vector.
MyLayersPtr raft_layers;
@ -2643,9 +2658,18 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
}
} else if (columns_base != nullptr) {
// Expand the bases of the support columns in the 1st layer.
columns_base->polygons = diff(
inflate_factor_1st_layer > 0 ? offset(columns_base->polygons, inflate_factor_1st_layer) : columns_base->polygons,
offset(m_object->layers().front()->lslices, (float)scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS));
{
Polygons &raft = columns_base->polygons;
Polygons trimming = offset(m_object->layers().front()->lslices, (float)scale_(m_gap_xy), SUPPORT_SURFACES_OFFSET_PARAMETERS);
if (inflate_factor_1st_layer > SCALED_EPSILON) {
// Inflate in multiple steps to avoid leaking of the support 1st layer through object walls.
auto nsteps = std::max(5, int(ceil(inflate_factor_1st_layer / m_first_layer_flow.scaled_width())));
float step = inflate_factor_1st_layer / nsteps;
for (int i = 0; i < nsteps; ++ i)
raft = diff(offset(raft, step), trimming);
} else
raft = diff(raft, trimming);
}
if (contacts != nullptr)
columns_base->polygons = diff(columns_base->polygons, interface_polygons);
if (! brim.empty()) {
@ -2654,6 +2678,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::generate_raf
contacts->polygons = diff(contacts->polygons, brim);
if (interfaces)
interfaces->polygons = diff(interfaces->polygons, brim);
if (base_interfaces)
base_interfaces->polygons = diff(base_interfaces->polygons, brim);
}
}
@ -2706,7 +2732,7 @@ std::pair<PrintObjectSupportMaterial::MyLayersPtr, PrintObjectSupportMaterial::M
layer_new.bridging = intermediate_layer.bridging;
// Merge top into bottom, unite them with a safety offset.
append(bottom, std::move(top));
layer_new.polygons = union_(std::move(bottom), true);
layer_new.polygons = intersection(union_(std::move(bottom), true), intermediate_layer.polygons);
// Subtract the interface from the base regions.
intermediate_layer.polygons = diff(intermediate_layer.polygons, layer_new.polygons, false);
if (subtract)
@ -3564,8 +3590,7 @@ void PrintObjectSupportMaterial::generate_toolpaths(
// Base flange.
filler->angle = raft_angle_1st_layer;
filler->spacing = m_first_layer_flow.spacing();
// 70% of density on the 1st layer.
density = 0.7f;
density = float(m_object_config->raft_first_layer_density.value * 0.01);
} else if (support_layer_id >= m_slicing_params.base_raft_layers) {
filler->angle = raft_angle_interface;
// We don't use $base_flow->spacing because we need a constant spacing

View file

@ -195,6 +195,7 @@ private:
const PrintObject &object,
const MyLayersPtr &top_contacts,
const MyLayersPtr &interface_layers,
const MyLayersPtr &base_interface_layers,
const MyLayersPtr &base_layers,
MyLayerStorage &layer_storage) const;

View file

@ -4,7 +4,6 @@
//=============
// debug techs
//=============
// Shows camera target in the 3D scene
#define ENABLE_SHOW_CAMERA_TARGET 0
// Log debug messages to console when changing selection
@ -29,63 +28,12 @@
#define ENABLE_GCODE_VIEWER_DATA_CHECKING 0
//=================
// 2.2.0.rc1 techs
//=================
#define ENABLE_2_2_0_RC1 1
// Enable hack to remove crash when closing on OSX 10.9.5
#define ENABLE_HACK_CLOSING_ON_OSX_10_9_5 (1 && ENABLE_2_2_0_RC1)
//====================
// 2.3.0.alpha1 techs
//====================
#define ENABLE_2_3_0_ALPHA1 1
// Enable rendering of objects using environment map
#define ENABLE_ENVIRONMENT_MAP (0 && ENABLE_2_3_0_ALPHA1)
#define ENABLE_ENVIRONMENT_MAP 0
// Enable smoothing of objects normals
#define ENABLE_SMOOTH_NORMALS (0 && ENABLE_2_3_0_ALPHA1)
// Enable error logging for OpenGL calls when SLIC3R_LOGLEVEL >= 5
#define ENABLE_OPENGL_ERROR_LOGGING (1 && ENABLE_2_3_0_ALPHA1)
// Enable built-in DPI changed event handler of wxWidgets 3.1.3
#define ENABLE_WX_3_1_3_DPI_CHANGED_EVENT (1 && ENABLE_2_3_0_ALPHA1)
//====================
// 2.3.0.alpha3 techs
//====================
#define ENABLE_2_3_0_ALPHA3 1
#define ENABLE_CTRL_M_ON_WINDOWS (1 && ENABLE_2_3_0_ALPHA3)
//====================
// 2.3.0.alpha4 techs
//====================
#define ENABLE_2_3_0_ALPHA4 1
#define ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS (1 && ENABLE_2_3_0_ALPHA4)
//===================
// 2.3.0.beta1 techs
//===================
#define ENABLE_2_3_0_BETA1 1
#define ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN (1 && ENABLE_2_3_0_BETA1)
//=================
// 2.3.0.rc1 techs
//=================
#define ENABLE_2_3_0_RC1 1
#define ENABLE_VOLUMETRIC_EXTRUSION_PROCESSING (1 && ENABLE_2_3_0_RC1)
#define ENABLE_SMOOTH_NORMALS 0
// Enable rendering markers for options in preview as fixed screen size points
#define ENABLE_FIXED_SCREEN_SIZE_POINT_MARKERS 1
//====================
@ -93,11 +41,17 @@
//====================
#define ENABLE_2_3_1_ALPHA1 1
// Enable splitting of vertex buffers used to render toolpaths
#define ENABLE_SPLITTED_VERTEX_BUFFER (1 && ENABLE_2_3_1_ALPHA1)
#define ENABLE_RELOAD_FROM_DISK_FOR_3MF (1 && ENABLE_2_3_1_ALPHA1)
// Enable rendering only starting and final caps for toolpaths
#define ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS (1 && ENABLE_SPLITTED_VERTEX_BUFFER)
// Enable reload from disk command for 3mf files
#define ENABLE_RELOAD_FROM_DISK_FOR_3MF (1 && ENABLE_2_3_1_ALPHA1)
// Removes obsolete warning texture code
#define ENABLE_WARNING_TEXTURE_REMOVAL (1 && ENABLE_2_3_1_ALPHA1)
// Enable showing gcode line numbers in previeww horizontal slider
#define ENABLE_GCODE_LINES_ID_IN_H_SLIDER (1 && ENABLE_2_3_1_ALPHA1)
// Enable validation of custom gcode against gcode processor reserved keywords
#define ENABLE_VALIDATE_CUSTOM_GCODE (1 && ENABLE_2_3_1_ALPHA1)
#define ENABLE_GCODE_WINDOW (1 && ENABLE_2_3_1_ALPHA1)