Merge remote-tracking branch 'origin/master' into ys_cp_improvements

This commit is contained in:
YuSanka 2020-01-15 11:40:54 +01:00
commit 1844fca780
52 changed files with 1433 additions and 922 deletions

View File

@ -72,6 +72,9 @@ if (MSVC)
# error C3859: virtual memory range for PCH exceeded; please recompile with a command line option of '-Zm90' or greater # error C3859: virtual memory range for PCH exceeded; please recompile with a command line option of '-Zm90' or greater
# Generate symbols at every build target, even for the release. # Generate symbols at every build target, even for the release.
add_compile_options(-bigobj -Zm520 /Zi) add_compile_options(-bigobj -Zm520 /Zi)
# Disable STL4007: Many result_type typedefs and all argument_type, first_argument_type, and second_argument_type typedefs are deprecated in C++17.
#FIXME Remove this line after eigen library adapts to the new C++17 adaptor rules.
add_compile_options(-D_SILENCE_CXX17_ADAPTOR_TYPEDEFS_DEPRECATION_WARNING)
endif () endif ()
if (MINGW) if (MINGW)

View File

@ -18,6 +18,7 @@ config_update_url = http://files.prusa3d.com/wp-content/uploads/repository/Prusa
name = Creality Ender-3 name = Creality Ender-3
variants = 0.4 variants = 0.4
technology = FFF technology = FFF
default_materials = Creality PLA @ENDER3; Prusament PLA @ENDER3
# All presets starting with asterisk, for example *common*, are intermediate and they will # All presets starting with asterisk, for example *common*, are intermediate and they will
# not make it into the user interface. # not make it into the user interface.

View File

@ -22,72 +22,84 @@ name = Original Prusa MINI
variants = 0.4; 0.25; 0.6 variants = 0.4; 0.25; 0.6
technology = FFF technology = FFF
family = MINI family = MINI
default_materials = Prusament PLA; Prusament PETG @MINI
[printer_model:MK3S] [printer_model:MK3S]
name = Original Prusa i3 MK3S name = Original Prusa i3 MK3S
variants = 0.4; 0.25; 0.6 variants = 0.4; 0.25; 0.6
technology = FFF technology = FFF
family = MK3 family = MK3
default_materials = Prusament PLA; Prusament PETG
[printer_model:MK3] [printer_model:MK3]
name = Original Prusa i3 MK3 name = Original Prusa i3 MK3
variants = 0.4; 0.25; 0.6 variants = 0.4; 0.25; 0.6
technology = FFF technology = FFF
family = MK3 family = MK3
default_materials = Prusament PLA; Prusament PETG
[printer_model:MK3SMMU2S] [printer_model:MK3SMMU2S]
name = Original Prusa i3 MK3S MMU2S name = Original Prusa i3 MK3S MMU2S
variants = 0.4; 0.25; 0.6 variants = 0.4; 0.25; 0.6
technology = FFF technology = FFF
family = MK3 family = MK3
default_materials = Prusament PLA @MMU2; Prusament PETG @MMU2
[printer_model:MK3MMU2] [printer_model:MK3MMU2]
name = Original Prusa i3 MK3 MMU2 name = Original Prusa i3 MK3 MMU2
variants = 0.4; 0.25; 0.6 variants = 0.4; 0.25; 0.6
technology = FFF technology = FFF
family = MK3 family = MK3
default_materials = Prusament PLA @MMU2; Prusament PETG @MMU2
[printer_model:MK2.5S] [printer_model:MK2.5S]
name = Original Prusa i3 MK2.5S name = Original Prusa i3 MK2.5S
variants = 0.4; 0.25; 0.6 variants = 0.4; 0.25; 0.6
technology = FFF technology = FFF
family = MK2.5 family = MK2.5
default_materials = Prusament PLA; Prusament PETG
[printer_model:MK2.5] [printer_model:MK2.5]
name = Original Prusa i3 MK2.5 name = Original Prusa i3 MK2.5
variants = 0.4; 0.25; 0.6 variants = 0.4; 0.25; 0.6
technology = FFF technology = FFF
family = MK2.5 family = MK2.5
default_materials = Prusament PLA; Prusament PETG
[printer_model:MK2.5SMMU2S] [printer_model:MK2.5SMMU2S]
name = Original Prusa i3 MK2.5S MMU2S name = Original Prusa i3 MK2.5S MMU2S
variants = 0.4; 0.25; 0.6 variants = 0.4; 0.25; 0.6
technology = FFF technology = FFF
family = MK2.5 family = MK2.5
default_materials = Prusament PLA @MMU2; Prusament PETG @MMU2
[printer_model:MK2.5MMU2] [printer_model:MK2.5MMU2]
name = Original Prusa i3 MK2.5 MMU2 name = Original Prusa i3 MK2.5 MMU2
variants = 0.4; 0.25; 0.6 variants = 0.4; 0.25; 0.6
technology = FFF technology = FFF
family = MK2.5 family = MK2.5
default_materials = Prusament PLA @MMU2; Prusament PETG @MMU2
[printer_model:MK2S] [printer_model:MK2S]
name = Original Prusa i3 MK2S name = Original Prusa i3 MK2S
variants = 0.4; 0.25; 0.6 variants = 0.4; 0.25; 0.6
technology = FFF technology = FFF
family = MK2 family = MK2
default_materials = Prusament PLA; Prusament PETG
[printer_model:MK2SMM] [printer_model:MK2SMM]
name = Original Prusa i3 MK2S MMU1 name = Original Prusa i3 MK2S MMU1
variants = 0.4; 0.6 variants = 0.4; 0.6
technology = FFF technology = FFF
family = MK2 family = MK2
default_materials = Prusament PLA; Prusament PETG @MMU1
[printer_model:SL1] [printer_model:SL1]
name = Original Prusa SL1 name = Original Prusa SL1
variants = default variants = default
technology = SLA technology = SLA
family = SL1 family = SL1
default_materials = Prusa Transparent Tough @0.05
# All presets starting with asterisk, for example *common*, are intermediate and they will # All presets starting with asterisk, for example *common*, are intermediate and they will
# not make it into the user interface. # not make it into the user interface.

View File

@ -641,10 +641,18 @@ bool CLI::export_models(IO::ExportFormat format)
const std::string path = this->output_filepath(model, format); const std::string path = this->output_filepath(model, format);
bool success = false; bool success = false;
switch (format) { switch (format) {
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
case IO::AMF: success = Slic3r::store_amf(path.c_str(), &model, nullptr, false); break;
#else
case IO::AMF: success = Slic3r::store_amf(path.c_str(), &model, nullptr); break; case IO::AMF: success = Slic3r::store_amf(path.c_str(), &model, nullptr); break;
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
case IO::OBJ: success = Slic3r::store_obj(path.c_str(), &model); break; case IO::OBJ: success = Slic3r::store_obj(path.c_str(), &model); break;
case IO::STL: success = Slic3r::store_stl(path.c_str(), &model, true); break; case IO::STL: success = Slic3r::store_stl(path.c_str(), &model, true); break;
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
case IO::TMF: success = Slic3r::store_3mf(path.c_str(), &model, nullptr, false); break;
#else
case IO::TMF: success = Slic3r::store_3mf(path.c_str(), &model, nullptr); break; case IO::TMF: success = Slic3r::store_3mf(path.c_str(), &model, nullptr); break;
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
default: assert(false); break; default: assert(false); break;
} }
if (success) if (success)

View File

@ -24,7 +24,7 @@ set(LIBNEST2D_SRCFILES
src/libnest2d.cpp src/libnest2d.cpp
) )
add_library(libnest2d ${LIBNEST2D_SRCFILES}) add_library(libnest2d STATIC ${LIBNEST2D_SRCFILES})
target_include_directories(libnest2d PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) target_include_directories(libnest2d PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
target_link_libraries(libnest2d PUBLIC clipper NLopt::nlopt TBB::tbb Boost::boost) target_link_libraries(libnest2d PUBLIC clipper NLopt::nlopt TBB::tbb Boost::boost)

View File

@ -1116,12 +1116,8 @@ private:
for(Item& item : items_) item.translate(d); for(Item& item : items_) item.translate(d);
} }
void setInitialPosition(Item& item) { void setInitialPosition(Item& item) {
auto sh = item.rawShape(); Box bb = item.boundingBox();
sl::translate(sh, item.translation());
sl::rotate(sh, item.rotation());
Box bb = sl::boundingBox(sh);
Vertex ci, cb; Vertex ci, cb;
auto bbin = sl::boundingBox(bin_); auto bbin = sl::boundingBox(bin_);

View File

@ -500,7 +500,7 @@ public:
if (NULLABLE) if (NULLABLE)
this->values.push_back(nil_value()); this->values.push_back(nil_value());
else else
std::runtime_error("Deserializing nil into a non-nullable object"); throw std::runtime_error("Deserializing nil into a non-nullable object");
} else { } else {
std::istringstream iss(item_str); std::istringstream iss(item_str);
double value; double value;
@ -525,9 +525,9 @@ protected:
if (NULLABLE) if (NULLABLE)
ss << "nil"; ss << "nil";
else else
std::runtime_error("Serializing NaN"); throw std::runtime_error("Serializing NaN");
} else } else
std::runtime_error("Serializing invalid number"); throw std::runtime_error("Serializing invalid number");
} }
static bool vectors_equal(const std::vector<double> &v1, const std::vector<double> &v2) { static bool vectors_equal(const std::vector<double> &v1, const std::vector<double> &v2) {
if (NULLABLE) { if (NULLABLE) {
@ -646,7 +646,7 @@ public:
if (NULLABLE) if (NULLABLE)
this->values.push_back(nil_value()); this->values.push_back(nil_value());
else else
std::runtime_error("Deserializing nil into a non-nullable object"); throw std::runtime_error("Deserializing nil into a non-nullable object");
} else { } else {
std::istringstream iss(item_str); std::istringstream iss(item_str);
int value; int value;
@ -663,7 +663,7 @@ private:
if (NULLABLE) if (NULLABLE)
ss << "nil"; ss << "nil";
else else
std::runtime_error("Serializing NaN"); throw std::runtime_error("Serializing NaN");
} else } else
ss << v; ss << v;
} }
@ -1126,7 +1126,7 @@ public:
if (NULLABLE) if (NULLABLE)
this->values.push_back(nil_value()); this->values.push_back(nil_value());
else else
std::runtime_error("Deserializing nil into a non-nullable object"); throw std::runtime_error("Deserializing nil into a non-nullable object");
} else } else
this->values.push_back(item_str.compare("1") == 0); this->values.push_back(item_str.compare("1") == 0);
} }
@ -1139,7 +1139,7 @@ protected:
if (NULLABLE) if (NULLABLE)
ss << "nil"; ss << "nil";
else else
std::runtime_error("Serializing NaN"); throw std::runtime_error("Serializing NaN");
} else } else
ss << (v ? "1" : "0"); ss << (v ? "1" : "0");
} }
@ -1638,7 +1638,7 @@ class DynamicConfig : public virtual ConfigBase
public: public:
DynamicConfig() {} DynamicConfig() {}
DynamicConfig(const DynamicConfig &rhs) { *this = rhs; } DynamicConfig(const DynamicConfig &rhs) { *this = rhs; }
DynamicConfig(DynamicConfig &&rhs) : options(std::move(rhs.options)) { rhs.options.clear(); } DynamicConfig(DynamicConfig &&rhs) noexcept : options(std::move(rhs.options)) { rhs.options.clear(); }
explicit DynamicConfig(const ConfigBase &rhs, const t_config_option_keys &keys); explicit DynamicConfig(const ConfigBase &rhs, const t_config_option_keys &keys);
explicit DynamicConfig(const ConfigBase& rhs) : DynamicConfig(rhs, rhs.keys()) {} explicit DynamicConfig(const ConfigBase& rhs) : DynamicConfig(rhs, rhs.keys()) {}
virtual ~DynamicConfig() override { clear(); } virtual ~DynamicConfig() override { clear(); }
@ -1656,7 +1656,7 @@ public:
// Move a content of one DynamicConfig to another DynamicConfig. // Move a content of one DynamicConfig to another DynamicConfig.
// If rhs.def() is not null, then it has to be equal to this->def(). // If rhs.def() is not null, then it has to be equal to this->def().
DynamicConfig& operator=(DynamicConfig &&rhs) DynamicConfig& operator=(DynamicConfig &&rhs) noexcept
{ {
assert(this->def() == nullptr || this->def() == rhs.def()); assert(this->def() == nullptr || this->def() == rhs.def());
this->clear(); this->clear();

View File

@ -19,7 +19,7 @@ class ExPolygon
public: public:
ExPolygon() {} ExPolygon() {}
ExPolygon(const ExPolygon &other) : contour(other.contour), holes(other.holes) {} ExPolygon(const ExPolygon &other) : contour(other.contour), holes(other.holes) {}
ExPolygon(ExPolygon &&other) : contour(std::move(other.contour)), holes(std::move(other.holes)) {} ExPolygon(ExPolygon &&other) noexcept : contour(std::move(other.contour)), holes(std::move(other.holes)) {}
explicit ExPolygon(const Polygon &contour) : contour(contour) {} explicit ExPolygon(const Polygon &contour) : contour(contour) {}
explicit ExPolygon(Polygon &&contour) : contour(std::move(contour)) {} explicit ExPolygon(Polygon &&contour) : contour(std::move(contour)) {}
explicit ExPolygon(const Points &contour) : contour(contour) {} explicit ExPolygon(const Points &contour) : contour(contour) {}
@ -32,7 +32,7 @@ public:
ExPolygon(std::initializer_list<Point> contour, std::initializer_list<Point> hole) : contour(contour), holes({ hole }) {} ExPolygon(std::initializer_list<Point> contour, std::initializer_list<Point> hole) : contour(contour), holes({ hole }) {}
ExPolygon& operator=(const ExPolygon &other) { contour = other.contour; holes = other.holes; return *this; } ExPolygon& operator=(const ExPolygon &other) { contour = other.contour; holes = other.holes; return *this; }
ExPolygon& operator=(ExPolygon &&other) { contour = std::move(other.contour); holes = std::move(other.holes); return *this; } ExPolygon& operator=(ExPolygon &&other) noexcept { contour = std::move(other.contour); holes = std::move(other.holes); return *this; }
Polygon contour; Polygon contour;
Polygons holes; Polygons holes;

View File

@ -48,9 +48,6 @@ public:
double retract_length_toolchange() const; double retract_length_toolchange() const;
double retract_restart_extra_toolchange() const; double retract_restart_extra_toolchange() const;
// Constructor for a key object, to be used by the stdlib search functions.
static Extruder key(unsigned int id) { return Extruder(id); }
private: private:
// Private constructor to create a key for a search in std::set. // Private constructor to create a key for a search in std::set.
Extruder(unsigned int id) : m_id(id) {} Extruder(unsigned int id) : m_id(id) {}

View File

@ -6,6 +6,26 @@
namespace Slic3r { namespace Slic3r {
void filter_by_extrusion_role_in_place(ExtrusionEntitiesPtr &extrusion_entities, ExtrusionRole role)
{
if (role != erMixed) {
auto first = extrusion_entities.begin();
auto last = extrusion_entities.end();
auto result = first;
while (first != last) {
// The caller wants only paths with a specific extrusion role.
auto role2 = (*first)->role();
if (role != role2) {
// This extrusion entity does not match the role asked.
assert(role2 != erMixed);
*result = *first;
++ result;
}
++ first;
}
}
}
ExtrusionEntityCollection::ExtrusionEntityCollection(const ExtrusionPaths &paths) ExtrusionEntityCollection::ExtrusionEntityCollection(const ExtrusionPaths &paths)
: no_sort(false) : no_sort(false)
{ {
@ -74,31 +94,16 @@ void ExtrusionEntityCollection::remove(size_t i)
this->entities.erase(this->entities.begin() + i); this->entities.erase(this->entities.begin() + i);
} }
ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(const Point &start_near, ExtrusionRole role) const ExtrusionEntityCollection ExtrusionEntityCollection::chained_path_from(const ExtrusionEntitiesPtr& extrusion_entities, const Point &start_near, ExtrusionRole role)
{ {
ExtrusionEntityCollection out; // Return a filtered copy of the collection.
if (this->no_sort) { ExtrusionEntityCollection out;
out = *this; out.entities = filter_by_extrusion_role(extrusion_entities, role);
} else { // Clone the extrusion entities.
if (role == erMixed) for (auto &ptr : out.entities)
out = *this; ptr = ptr->clone();
else { chain_and_reorder_extrusion_entities(out.entities, &start_near);
for (const ExtrusionEntity *ee : this->entities) { return out;
if (role != erMixed) {
// The caller wants only paths with a specific extrusion role.
auto role2 = ee->role();
if (role != role2) {
// This extrusion entity does not match the role asked.
assert(role2 != erMixed);
continue;
}
}
out.entities.emplace_back(ee->clone());
}
}
chain_and_reorder_extrusion_entities(out.entities, &start_near);
}
return out;
} }
void ExtrusionEntityCollection::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const void ExtrusionEntityCollection::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const

View File

@ -6,6 +6,21 @@
namespace Slic3r { namespace Slic3r {
// Remove those items from extrusion_entities, that do not match role.
// Do nothing if role is mixed.
// Removed elements are NOT being deleted.
void filter_by_extrusion_role_in_place(ExtrusionEntitiesPtr &extrusion_entities, ExtrusionRole role);
// Return new vector of ExtrusionEntities* with only those items from input extrusion_entities, that match role.
// Return all extrusion entities if role is mixed.
// Returned extrusion entities are shared with the source vector, they are NOT cloned, they are considered to be owned by extrusion_entities.
inline ExtrusionEntitiesPtr filter_by_extrusion_role(const ExtrusionEntitiesPtr &extrusion_entities, ExtrusionRole role)
{
ExtrusionEntitiesPtr out { extrusion_entities };
filter_by_extrusion_role_in_place(out, role);
return out;
}
class ExtrusionEntityCollection : public ExtrusionEntity class ExtrusionEntityCollection : public ExtrusionEntity
{ {
public: public:
@ -65,7 +80,9 @@ public:
} }
void replace(size_t i, const ExtrusionEntity &entity); void replace(size_t i, const ExtrusionEntity &entity);
void remove(size_t i); void remove(size_t i);
ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erMixed) const; static ExtrusionEntityCollection chained_path_from(const ExtrusionEntitiesPtr &extrusion_entities, const Point &start_near, ExtrusionRole role = erMixed);
ExtrusionEntityCollection chained_path_from(const Point &start_near, ExtrusionRole role = erMixed) const
{ return this->no_sort ? *this : chained_path_from(this->entities, start_near, role); }
void reverse(); void reverse();
const Point& first_point() const { return this->entities.front()->first_point(); } const Point& first_point() const { return this->entities.front()->first_point(); }
const Point& last_point() const { return this->entities.back()->last_point(); } const Point& last_point() const { return this->entities.back()->last_point(); }
@ -105,6 +122,6 @@ public:
} }
}; };
} } // namespace Slic3r
#endif #endif

View File

@ -107,9 +107,9 @@ double Flow::mm3_per_mm() const
{ {
float res = this->bridge ? float res = this->bridge ?
// Area of a circle with dmr of this->width. // Area of a circle with dmr of this->width.
(this->width * this->width) * 0.25 * PI : float((this->width * this->width) * 0.25 * PI) :
// Rectangle with semicircles at the ends. ~ h (w - 0.215 h) // Rectangle with semicircles at the ends. ~ h (w - 0.215 h)
this->height * (this->width - this->height * (1. - 0.25 * PI)); float(this->height * (this->width - this->height * (1. - 0.25 * PI)));
//assert(res > 0.); //assert(res > 0.);
if (res <= 0.) if (res <= 0.)
throw std::runtime_error("Flow::mm3_per_mm() produced negative flow. Did you set some extrusion width too small?"); throw std::runtime_error("Flow::mm3_per_mm() produced negative flow. Did you set some extrusion width too small?");

View File

@ -1876,12 +1876,24 @@ namespace Slic3r {
typedef std::vector<BuildItem> BuildItemsList; typedef std::vector<BuildItem> BuildItemsList;
typedef std::map<int, ObjectData> IdToObjectDataMap; typedef std::map<int, ObjectData> IdToObjectDataMap;
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
bool m_fullpath_sources{ true };
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
public: public:
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
#if ENABLE_THUMBNAIL_GENERATOR
bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr);
#else
bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources);
#endif // ENABLE_THUMBNAIL_GENERATOR
#else
#if ENABLE_THUMBNAIL_GENERATOR #if ENABLE_THUMBNAIL_GENERATOR
bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data = nullptr); bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data = nullptr);
#else #else
bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config); bool save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config);
#endif // ENABLE_THUMBNAIL_GENERATOR #endif // ENABLE_THUMBNAIL_GENERATOR
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
private: private:
#if ENABLE_THUMBNAIL_GENERATOR #if ENABLE_THUMBNAIL_GENERATOR
@ -1906,6 +1918,22 @@ namespace Slic3r {
bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model); bool _add_custom_gcode_per_print_z_file_to_archive(mz_zip_archive& archive, Model& model);
}; };
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
#if ENABLE_THUMBNAIL_GENERATOR
bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data)
{
clear_errors();
m_fullpath_sources = fullpath_sources;
return _save_model_to_file(filename, model, config, thumbnail_data);
}
#else
bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, bool fullpath_sources)
{
clear_errors();
return _save_model_to_file(filename, model, config);
}
#endif // ENABLE_THUMBNAIL_GENERATOR
#else
#if ENABLE_THUMBNAIL_GENERATOR #if ENABLE_THUMBNAIL_GENERATOR
bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data) bool _3MF_Exporter::save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data)
{ {
@ -1919,6 +1947,7 @@ namespace Slic3r {
return _save_model_to_file(filename, model, config); return _save_model_to_file(filename, model, config);
} }
#endif // ENABLE_THUMBNAIL_GENERATOR #endif // ENABLE_THUMBNAIL_GENERATOR
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
#if ENABLE_THUMBNAIL_GENERATOR #if ENABLE_THUMBNAIL_GENERATOR
bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data) bool _3MF_Exporter::_save_model_to_file(const std::string& filename, Model& model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data)
@ -2557,7 +2586,12 @@ namespace Slic3r {
// stores volume's source data // stores volume's source data
if (!volume->source.input_file.empty()) if (!volume->source.input_file.empty())
{ {
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
std::string input_file = xml_escape(m_fullpath_sources ? volume->source.input_file : boost::filesystem::path(volume->source.input_file).filename().string());
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << input_file << "\"/>\n";
#else
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->source.input_file) << "\"/>\n"; stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_FILE_KEY << "\" " << VALUE_ATTR << "=\"" << xml_escape(volume->source.input_file) << "\"/>\n";
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n"; stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OBJECT_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.object_idx << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n"; stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_VOLUME_ID_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.volume_idx << "\"/>\n";
stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n"; stream << " <" << METADATA_TAG << " " << TYPE_ATTR << "=\"" << VOLUME_TYPE << "\" " << KEY_ATTR << "=\"" << SOURCE_OFFSET_X_KEY << "\" " << VALUE_ATTR << "=\"" << volume->source.mesh_offset(0) << "\"/>\n";
@ -2646,21 +2680,37 @@ bool load_3mf(const char* path, DynamicPrintConfig* config, Model* model, bool c
return res; return res;
} }
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
#if ENABLE_THUMBNAIL_GENERATOR
bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data)
#else
bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources)
#endif // ENABLE_THUMBNAIL_GENERATOR
#else
#if ENABLE_THUMBNAIL_GENERATOR #if ENABLE_THUMBNAIL_GENERATOR
bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data) bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data)
#else #else
bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config) bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config)
#endif // ENABLE_THUMBNAIL_GENERATOR #endif // ENABLE_THUMBNAIL_GENERATOR
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
{ {
if ((path == nullptr) || (model == nullptr)) if ((path == nullptr) || (model == nullptr))
return false; return false;
_3MF_Exporter exporter; _3MF_Exporter exporter;
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
#if ENABLE_THUMBNAIL_GENERATOR
bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources, thumbnail_data);
#else
bool res = exporter.save_model_to_file(path, *model, config, fullpath_sources);
#endif // ENABLE_THUMBNAIL_GENERATOR
#else
#if ENABLE_THUMBNAIL_GENERATOR #if ENABLE_THUMBNAIL_GENERATOR
bool res = exporter.save_model_to_file(path, *model, config, thumbnail_data); bool res = exporter.save_model_to_file(path, *model, config, thumbnail_data);
#else #else
bool res = exporter.save_model_to_file(path, *model, config); bool res = exporter.save_model_to_file(path, *model, config);
#endif // ENABLE_THUMBNAIL_GENERATOR #endif // ENABLE_THUMBNAIL_GENERATOR
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
if (!res) if (!res)
exporter.log_errors(); exporter.log_errors();

View File

@ -31,11 +31,19 @@ namespace Slic3r {
// Save the given model and the config data contained in the given Print into a 3mf file. // Save the given model and the config data contained in the given Print into a 3mf file.
// The model could be modified during the export process if meshes are not repaired or have no shared vertices // The model could be modified during the export process if meshes are not repaired or have no shared vertices
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
#if ENABLE_THUMBNAIL_GENERATOR
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr);
#else
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources);
#endif // ENABLE_THUMBNAIL_GENERATOR
#else
#if ENABLE_THUMBNAIL_GENERATOR #if ENABLE_THUMBNAIL_GENERATOR
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data = nullptr); extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, const ThumbnailData* thumbnail_data = nullptr);
#else #else
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config); extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config);
#endif // ENABLE_THUMBNAIL_GENERATOR #endif // ENABLE_THUMBNAIL_GENERATOR
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
}; // namespace Slic3r }; // namespace Slic3r

View File

@ -1019,7 +1019,11 @@ bool load_amf(const char* path, DynamicPrintConfig* config, Model* model, bool c
return false; return false;
} }
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources)
#else
bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config) bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
{ {
if ((path == nullptr) || (model == nullptr)) if ((path == nullptr) || (model == nullptr))
return false; return false;
@ -1177,7 +1181,12 @@ bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config)
stream << "</metadata>\n"; stream << "</metadata>\n";
if (!volume->source.input_file.empty()) if (!volume->source.input_file.empty())
{ {
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
std::string input_file = xml_escape(fullpath_sources ? volume->source.input_file : boost::filesystem::path(volume->source.input_file).filename().string());
stream << " <metadata type=\"slic3r.source_file\">" << input_file << "</metadata>\n";
#else
stream << " <metadata type=\"slic3r.source_file\">" << xml_escape(volume->source.input_file) << "</metadata>\n"; stream << " <metadata type=\"slic3r.source_file\">" << xml_escape(volume->source.input_file) << "</metadata>\n";
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
stream << " <metadata type=\"slic3r.source_object_id\">" << volume->source.object_idx << "</metadata>\n"; stream << " <metadata type=\"slic3r.source_object_id\">" << volume->source.object_idx << "</metadata>\n";
stream << " <metadata type=\"slic3r.source_volume_id\">" << volume->source.volume_idx << "</metadata>\n"; stream << " <metadata type=\"slic3r.source_volume_id\">" << volume->source.volume_idx << "</metadata>\n";
stream << " <metadata type=\"slic3r.source_offset_x\">" << volume->source.mesh_offset(0) << "</metadata>\n"; stream << " <metadata type=\"slic3r.source_offset_x\">" << volume->source.mesh_offset(0) << "</metadata>\n";

View File

@ -11,7 +11,11 @@ extern bool load_amf(const char* path, DynamicPrintConfig* config, Model* model,
// Save the given model and the config data into an amf file. // Save the given model and the config data into an amf file.
// The model could be modified during the export process if meshes are not repaired or have no shared vertices // The model could be modified during the export process if meshes are not repaired or have no shared vertices
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
extern bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources);
#else
extern bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config); extern bool store_amf(const char *path, Model *model, const DynamicPrintConfig *config);
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
}; // namespace Slic3r }; // namespace Slic3r

File diff suppressed because it is too large Load Diff

View File

@ -197,23 +197,25 @@ public:
// append full config to the given string // append full config to the given string
static void append_full_config(const Print& print, std::string& str); static void append_full_config(const Print& print, std::string& str);
protected: // Object and support extrusions of the same PrintObject at the same print_z.
// public, so that it could be accessed by free helper functions from GCode.cpp
struct LayerToPrint
{
LayerToPrint() : object_layer(nullptr), support_layer(nullptr) {}
const Layer* object_layer;
const SupportLayer* support_layer;
const Layer* layer() const { return (object_layer != nullptr) ? object_layer : support_layer; }
const PrintObject* object() const { return (this->layer() != nullptr) ? this->layer()->object() : nullptr; }
coordf_t print_z() const { return (object_layer != nullptr && support_layer != nullptr) ? 0.5 * (object_layer->print_z + support_layer->print_z) : this->layer()->print_z; }
};
private:
#if ENABLE_THUMBNAIL_GENERATOR #if ENABLE_THUMBNAIL_GENERATOR
void _do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb); void _do_export(Print &print, FILE *file, ThumbnailsGeneratorCallback thumbnail_cb);
#else #else
void _do_export(Print &print, FILE *file); void _do_export(Print &print, FILE *file);
#endif //ENABLE_THUMBNAIL_GENERATOR #endif //ENABLE_THUMBNAIL_GENERATOR
// Object and support extrusions of the same PrintObject at the same print_z.
struct LayerToPrint
{
LayerToPrint() : object_layer(nullptr), support_layer(nullptr) {}
const Layer *object_layer;
const SupportLayer *support_layer;
const Layer* layer() const { return (object_layer != nullptr) ? object_layer : support_layer; }
const PrintObject* object() const { return (this->layer() != nullptr) ? this->layer()->object() : nullptr; }
coordf_t print_z() const { return (object_layer != nullptr && support_layer != nullptr) ? 0.5 * (object_layer->print_z + support_layer->print_z) : this->layer()->print_z; }
};
static std::vector<LayerToPrint> collect_layers_to_print(const PrintObject &object); static std::vector<LayerToPrint> collect_layers_to_print(const PrintObject &object);
static std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> collect_layers_to_print(const Print &print); static std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> collect_layers_to_print(const Print &print);
void process_layer( void process_layer(
@ -239,7 +241,6 @@ protected:
std::string extrude_multi_path(ExtrusionMultiPath multipath, std::string description = "", double speed = -1.); std::string extrude_multi_path(ExtrusionMultiPath multipath, std::string description = "", double speed = -1.);
std::string extrude_path(ExtrusionPath path, std::string description = "", double speed = -1.); std::string extrude_path(ExtrusionPath path, std::string description = "", double speed = -1.);
typedef std::vector<int> ExtruderPerCopy;
// Extruding multiple objects with soluble / non-soluble / combined supports // Extruding multiple objects with soluble / non-soluble / combined supports
// on a multi-material printer, trying to minimize tool switches. // on a multi-material printer, trying to minimize tool switches.
// Following structures sort extrusions by the extruder ID, by an order of objects and object islands. // Following structures sort extrusions by the extruder ID, by an order of objects and object islands.
@ -253,21 +254,29 @@ protected:
struct Island struct Island
{ {
struct Region { struct Region {
ExtrusionEntityCollection perimeters; // Non-owned references to LayerRegion::perimeters::entities
ExtrusionEntityCollection infills; // std::vector<const ExtrusionEntity*> would be better here, but there is no way in C++ to convert from std::vector<T*> std::vector<const T*> without copying.
ExtrusionEntitiesPtr perimeters;
// Non-owned references to LayerRegion::fills::entities
ExtrusionEntitiesPtr infills;
std::vector<const ExtruderPerCopy*> infills_overrides; std::vector<const WipingExtrusions::ExtruderPerCopy*> infills_overrides;
std::vector<const ExtruderPerCopy*> perimeters_overrides; std::vector<const WipingExtrusions::ExtruderPerCopy*> perimeters_overrides;
enum Type {
PERIMETERS,
INFILL,
};
// Appends perimeter/infill entities and writes don't indices of those that are not to be extruder as part of perimeter/infill wiping // Appends perimeter/infill entities and writes don't indices of those that are not to be extruder as part of perimeter/infill wiping
void append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copy_extruders, size_t object_copies_num); void append(const Type type, const ExtrusionEntityCollection* eec, const WipingExtrusions::ExtruderPerCopy* copy_extruders);
}; };
std::vector<Region> by_region; // all extrusions for this island, grouped by regions
const std::vector<Region>& by_region_per_copy(unsigned int copy, int extruder, bool wiping_entities = false); // returns reference to subvector of by_region
private: std::vector<Region> by_region; // all extrusions for this island, grouped by regions
std::vector<Region> by_region_per_copy_cache; // caches vector generated by function above to avoid copying and recalculating
// Fills in by_region_per_copy_cache and returns its reference.
const std::vector<Region>& by_region_per_copy(std::vector<Region> &by_region_per_copy_cache, unsigned int copy, unsigned int extruder, bool wiping_entities = false) const;
}; };
std::vector<Island> islands; std::vector<Island> islands;
}; };
@ -277,7 +286,9 @@ protected:
InstanceToPrint(ObjectByExtruder &object_by_extruder, size_t layer_id, const PrintObject &print_object, size_t instance_id) : InstanceToPrint(ObjectByExtruder &object_by_extruder, size_t layer_id, const PrintObject &print_object, size_t instance_id) :
object_by_extruder(object_by_extruder), layer_id(layer_id), print_object(print_object), instance_id(instance_id) {} object_by_extruder(object_by_extruder), layer_id(layer_id), print_object(print_object), instance_id(instance_id) {}
ObjectByExtruder &object_by_extruder; // Repository
ObjectByExtruder &object_by_extruder;
// Index into std::vector<LayerToPrint>, which contains Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances.
const size_t layer_id; const size_t layer_id;
const PrintObject &print_object; const PrintObject &print_object;
// Instance idx of the copy of a print object. // Instance idx of the copy of a print object.
@ -285,7 +296,8 @@ protected:
}; };
std::vector<InstanceToPrint> sort_print_object_instances( std::vector<InstanceToPrint> sort_print_object_instances(
std::vector<ObjectByExtruder> &objects_by_extruder, std::vector<ObjectByExtruder> &objects_by_extruder,
// Object and Support layers for the current print_z, collected for a single object, or for possibly multiple objects with multiple instances.
const std::vector<LayerToPrint> &layers, const std::vector<LayerToPrint> &layers,
// Ordering must be defined for normal (non-sequential print). // Ordering must be defined for normal (non-sequential print).
const std::vector<std::pair<size_t, size_t>> *ordering, const std::vector<std::pair<size_t, size_t>> *ordering,
@ -354,7 +366,7 @@ protected:
#endif /* HAS_PRESSURE_EQUALIZER */ #endif /* HAS_PRESSURE_EQUALIZER */
std::unique_ptr<WipeTowerIntegration> m_wipe_tower; std::unique_ptr<WipeTowerIntegration> m_wipe_tower;
// Heights at which the skirt has already been extruded. // Heights (print_z) at which the skirt has already been extruded.
std::vector<coordf_t> m_skirt_done; std::vector<coordf_t> m_skirt_done;
// Has the brim been extruded already? Brim is being extruded only for the first object of a multi-object print. // Has the brim been extruded already? Brim is being extruded only for the first object of a multi-object print.
bool m_brim_done; bool m_brim_done;
@ -362,11 +374,6 @@ protected:
bool m_second_layer_things_done; bool m_second_layer_things_done;
// Index of a last object copy extruded. // Index of a last object copy extruded.
std::pair<const PrintObject*, Point> m_last_obj_copy; std::pair<const PrintObject*, Point> m_last_obj_copy;
// Extensions for colorprint - now it's not a just color_print_heights,
// there can be some custom gcode.
// Updated before the export and erased during the process,
// so no toolchange occurs twice.
std::vector<Model::CustomGCode> m_custom_gcode_per_print_z;
// Time estimators // Time estimators
GCodeTimeEstimator m_normal_time_estimator; GCodeTimeEstimator m_normal_time_estimator;

View File

@ -311,24 +311,22 @@ void GCodeAnalyzer::_processG1(const GCodeReader::GCodeLine& line)
{ {
auto axis_absolute_position = [this](GCodeAnalyzer::EAxis axis, const GCodeReader::GCodeLine& lineG1) -> float auto axis_absolute_position = [this](GCodeAnalyzer::EAxis axis, const GCodeReader::GCodeLine& lineG1) -> float
{ {
float current_absolute_position = _get_axis_position(axis);
float current_origin = _get_axis_origin(axis);
float lengthsScaleFactor = (_get_units() == GCodeAnalyzer::Inches) ? INCHES_TO_MM : 1.0f;
bool is_relative = (_get_global_positioning_type() == Relative); bool is_relative = (_get_global_positioning_type() == Relative);
if (axis == E) if (axis == E)
is_relative |= (_get_e_local_positioning_type() == Relative); is_relative |= (_get_e_local_positioning_type() == Relative);
if (lineG1.has(Slic3r::Axis(axis))) if (lineG1.has(Slic3r::Axis(axis)))
{ {
float lengthsScaleFactor = (_get_units() == GCodeAnalyzer::Inches) ? INCHES_TO_MM : 1.0f;
float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor; float ret = lineG1.value(Slic3r::Axis(axis)) * lengthsScaleFactor;
return is_relative ? current_absolute_position + ret : ret + current_origin; return is_relative ? _get_axis_position(axis) + ret : _get_axis_origin(axis) + ret;
} }
else else
return current_absolute_position; return _get_axis_position(axis);
}; };
// updates axes positions from line // updates axes positions from line
float new_pos[Num_Axis]; float new_pos[Num_Axis];
for (unsigned char a = X; a < Num_Axis; ++a) for (unsigned char a = X; a < Num_Axis; ++a)
{ {
@ -352,7 +350,7 @@ void GCodeAnalyzer::_processG1(const GCodeReader::GCodeLine& line)
if (delta_pos[E] < 0.0f) if (delta_pos[E] < 0.0f)
{ {
if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f) || (delta_pos[Z] != 0.0f)) if ((delta_pos[X] != 0.0f) || (delta_pos[Y] != 0.0f) || (delta_pos[Z] != 0.0f))
type = GCodeMove::Move; type = GCodeMove::Move;
else else
type = GCodeMove::Retract; type = GCodeMove::Retract;
} }
@ -440,7 +438,9 @@ void GCodeAnalyzer::_processG92(const GCodeReader::GCodeLine& line)
if (line.has_e()) if (line.has_e())
{ {
_set_axis_origin(E, _get_axis_position(E) - line.e() * lengthsScaleFactor); // extruder coordinate can grow to the point where its float representation does not allow for proper addition with small increments,
// we set the value taken from the G92 line as the new current position for it
_set_axis_position(E, line.e() * lengthsScaleFactor);
anyFound = true; anyFound = true;
} }
@ -956,7 +956,7 @@ void GCodeAnalyzer::_calc_gcode_preview_extrusion_layers(GCodePreviewData& previ
GCodePreviewData::Extrusion::Path &path = paths.back(); GCodePreviewData::Extrusion::Path &path = paths.back();
path.polyline = polyline; path.polyline = polyline;
path.extrusion_role = data.extrusion_role; path.extrusion_role = data.extrusion_role;
path.mm3_per_mm = data.mm3_per_mm; path.mm3_per_mm = float(data.mm3_per_mm);
path.width = data.width; path.width = data.width;
path.height = data.height; path.height = data.height;
path.feedrate = data.feedrate; path.feedrate = data.feedrate;

View File

@ -303,8 +303,8 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
unsigned int extruder_id = extruders[i].id(); unsigned int extruder_id = extruders[i].id();
adj.extruder_id = extruder_id; adj.extruder_id = extruder_id;
adj.cooling_slow_down_enabled = config.cooling.get_at(extruder_id); adj.cooling_slow_down_enabled = config.cooling.get_at(extruder_id);
adj.slowdown_below_layer_time = config.slowdown_below_layer_time.get_at(extruder_id); adj.slowdown_below_layer_time = float(config.slowdown_below_layer_time.get_at(extruder_id));
adj.min_print_speed = config.min_print_speed.get_at(extruder_id); adj.min_print_speed = float(config.min_print_speed.get_at(extruder_id));
map_extruder_to_per_extruder_adjustment[extruder_id] = i; map_extruder_to_per_extruder_adjustment[extruder_id] = i;
} }

View File

@ -29,7 +29,7 @@ static inline BoundingBox extrusion_polyline_extents(const Polyline &polyline, c
static inline BoundingBoxf extrusionentity_extents(const ExtrusionPath &extrusion_path) static inline BoundingBoxf extrusionentity_extents(const ExtrusionPath &extrusion_path)
{ {
BoundingBox bbox = extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width)); BoundingBox bbox = extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width)));
BoundingBoxf bboxf; BoundingBoxf bboxf;
if (! empty(bbox)) { if (! empty(bbox)) {
bboxf.min = unscale(bbox.min); bboxf.min = unscale(bbox.min);
@ -43,7 +43,7 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionLoop &extrusio
{ {
BoundingBox bbox; BoundingBox bbox;
for (const ExtrusionPath &extrusion_path : extrusion_loop.paths) for (const ExtrusionPath &extrusion_path : extrusion_loop.paths)
bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width))); bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width))));
BoundingBoxf bboxf; BoundingBoxf bboxf;
if (! empty(bbox)) { if (! empty(bbox)) {
bboxf.min = unscale(bbox.min); bboxf.min = unscale(bbox.min);
@ -57,7 +57,7 @@ static inline BoundingBoxf extrusionentity_extents(const ExtrusionMultiPath &ext
{ {
BoundingBox bbox; BoundingBox bbox;
for (const ExtrusionPath &extrusion_path : extrusion_multi_path.paths) for (const ExtrusionPath &extrusion_path : extrusion_multi_path.paths)
bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, scale_(0.5 * extrusion_path.width))); bbox.merge(extrusion_polyline_extents(extrusion_path.polyline, coord_t(scale_(0.5 * extrusion_path.width))));
BoundingBoxf bboxf; BoundingBoxf bboxf;
if (! empty(bbox)) { if (! empty(bbox)) {
bboxf.min = unscale(bbox.min); bboxf.min = unscale(bbox.min);

View File

@ -13,13 +13,17 @@
#include <cassert> #include <cassert>
#include <limits> #include <limits>
#include <libslic3r.h>
#include "../GCodeWriter.hpp"
namespace Slic3r { namespace Slic3r {
// Returns true in case that extruder a comes before b (b does not have to be present). False otherwise. // Returns true in case that extruder a comes before b (b does not have to be present). False otherwise.
bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const
{ {
if (a==b) if (a == b)
return false; return false;
for (auto extruder : extruders) { for (auto extruder : extruders) {
@ -32,6 +36,39 @@ bool LayerTools::is_extruder_order(unsigned int a, unsigned int b) const
return false; return false;
} }
// Return a zero based extruder from the region, or extruder_override if overriden.
unsigned int LayerTools::perimeter_extruder(const PrintRegion &region) const
{
assert(region.config().perimeter_extruder.value > 0);
return ((this->extruder_override == 0) ? region.config().perimeter_extruder.value : this->extruder_override) - 1;
}
unsigned int LayerTools::infill_extruder(const PrintRegion &region) const
{
assert(region.config().infill_extruder.value > 0);
return ((this->extruder_override == 0) ? region.config().infill_extruder.value : this->extruder_override) - 1;
}
unsigned int LayerTools::solid_infill_extruder(const PrintRegion &region) const
{
assert(region.config().solid_infill_extruder.value > 0);
return ((this->extruder_override == 0) ? region.config().solid_infill_extruder.value : this->extruder_override) - 1;
}
// Returns a zero based extruder this eec should be printed with, according to PrintRegion config or extruder_override if overriden.
unsigned int LayerTools::extruder(const ExtrusionEntityCollection &extrusions, const PrintRegion &region) const
{
assert(region.config().perimeter_extruder.value > 0);
assert(region.config().infill_extruder.value > 0);
assert(region.config().solid_infill_extruder.value > 0);
// 1 based extruder ID.
unsigned int extruder = ((this->extruder_override == 0) ?
(is_infill(extrusions.role()) ?
(is_solid_infill(extrusions.entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) :
region.config().perimeter_extruder.value) :
this->extruder_override);
return (extruder == 0) ? 0 : extruder - 1;
}
// For the use case when each object is printed separately // For the use case when each object is printed separately
// (print.config().complete_objects is true). // (print.config().complete_objects is true).
@ -52,7 +89,7 @@ ToolOrdering::ToolOrdering(const PrintObject &object, unsigned int first_extrude
} }
// Collect extruders reuqired to print the layers. // Collect extruders reuqired to print the layers.
this->collect_extruders(object); this->collect_extruders(object, std::vector<std::pair<double, unsigned int>>());
// Reorder the extruders to minimize tool switches. // Reorder the extruders to minimize tool switches.
this->reorder_extruders(first_extruder); this->reorder_extruders(first_extruder);
@ -89,9 +126,15 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
this->initialize_layers(zs); this->initialize_layers(zs);
} }
// 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.
std::vector<std::pair<double, unsigned int>> per_layer_extruder_switches;
if (print.object_extruders().size() == 1)
per_layer_extruder_switches = custom_tool_changes(print.model(), (unsigned int)print.config().nozzle_diameter.size());
// Collect extruders reuqired to print the layers. // Collect extruders reuqired to print the layers.
for (auto object : print.objects()) for (auto object : print.objects())
this->collect_extruders(*object); this->collect_extruders(*object, per_layer_extruder_switches);
// Reorder the extruders to minimize tool switches. // Reorder the extruders to minimize tool switches.
this->reorder_extruders(first_extruder); this->reorder_extruders(first_extruder);
@ -99,6 +142,11 @@ ToolOrdering::ToolOrdering(const Print &print, unsigned int first_extruder, bool
this->fill_wipe_tower_partitions(print.config(), object_bottom_z); this->fill_wipe_tower_partitions(print.config(), object_bottom_z);
this->collect_extruder_statistics(prime_multi_material); this->collect_extruder_statistics(prime_multi_material);
// Assign custom G-code actions from Model::custom_gcode_per_print_z to their respecive layers,
// ignoring the extruder switches, which were processed above, and ignoring color changes for extruders,
// that do not print above their respective print_z.
this->assign_custom_gcodes(print);
} }
void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs) void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs)
@ -111,13 +159,13 @@ void ToolOrdering::initialize_layers(std::vector<coordf_t> &zs)
coordf_t zmax = zs[i] + EPSILON; coordf_t zmax = zs[i] + EPSILON;
for (; j < zs.size() && zs[j] <= zmax; ++ j) ; for (; j < zs.size() && zs[j] <= zmax; ++ j) ;
// Assign an average print_z to the set of layers with nearly equal print_z. // Assign an average print_z to the set of layers with nearly equal print_z.
m_layer_tools.emplace_back(LayerTools(0.5 * (zs[i] + zs[j-1]), m_print_config_ptr)); m_layer_tools.emplace_back(LayerTools(0.5 * (zs[i] + zs[j-1])));
i = j; i = j;
} }
} }
// Collect extruders reuqired to print layers. // Collect extruders reuqired to print layers.
void ToolOrdering::collect_extruders(const PrintObject &object) void ToolOrdering::collect_extruders(const PrintObject &object, const std::vector<std::pair<double, unsigned int>> &per_layer_extruder_switches)
{ {
// Collect the support extruders. // Collect the support extruders.
for (auto support_layer : object.support_layers()) { for (auto support_layer : object.support_layers()) {
@ -134,9 +182,23 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
if (has_support || has_interface) if (has_support || has_interface)
layer_tools.has_support = true; layer_tools.has_support = true;
} }
// Extruder overrides are ordered by print_z.
std::vector<std::pair<double, unsigned int>>::const_iterator it_per_layer_extruder_override;
it_per_layer_extruder_override = per_layer_extruder_switches.begin();
unsigned int extruder_override = 0;
// Collect the object extruders. // Collect the object extruders.
for (auto layer : object.layers()) { for (auto layer : object.layers()) {
LayerTools &layer_tools = this->tools_for_layer(layer->print_z); LayerTools &layer_tools = this->tools_for_layer(layer->print_z);
// Override extruder with the next
for (; it_per_layer_extruder_override != per_layer_extruder_switches.end() && it_per_layer_extruder_override->first < layer->print_z + EPSILON; ++ it_per_layer_extruder_override)
extruder_override = (int)it_per_layer_extruder_override->second;
// Store the current extruder override (set to zero if no overriden), so that layer_tools.wiping_extrusions().is_overridable_and_mark() will use it.
layer_tools.extruder_override = extruder_override;
// What extruders are required to print this object layer? // What extruders are required to print this object layer?
for (size_t region_id = 0; region_id < object.region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < object.region_volumes.size(); ++ region_id) {
const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr; const LayerRegion *layerm = (region_id < layer->regions().size()) ? layer->regions()[region_id] : nullptr;
@ -150,19 +212,18 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
if (m_print_config_ptr) { // in this case complete_objects is false (see ToolOrdering constructors) if (m_print_config_ptr) { // in this case complete_objects is false (see ToolOrdering constructors)
something_nonoverriddable = false; something_nonoverriddable = false;
for (const auto& eec : layerm->perimeters.entities) // let's check if there are nonoverriddable entities for (const auto& eec : layerm->perimeters.entities) // let's check if there are nonoverriddable entities
if (!layer_tools.wiping_extrusions().is_overriddable(dynamic_cast<const ExtrusionEntityCollection&>(*eec), *m_print_config_ptr, object, region)) { if (!layer_tools.wiping_extrusions().is_overriddable_and_mark(dynamic_cast<const ExtrusionEntityCollection&>(*eec), *m_print_config_ptr, object, region)) {
something_nonoverriddable = true; something_nonoverriddable = true;
break; break;
} }
} }
if (something_nonoverriddable) if (something_nonoverriddable)
layer_tools.extruders.push_back(region.config().perimeter_extruder.value); layer_tools.extruders.emplace_back((extruder_override == 0) ? region.config().perimeter_extruder.value : extruder_override);
layer_tools.has_object = true; layer_tools.has_object = true;
} }
bool has_infill = false; bool has_infill = false;
bool has_solid_infill = false; bool has_solid_infill = false;
bool something_nonoverriddable = false; bool something_nonoverriddable = false;
@ -176,17 +237,19 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
has_infill = true; has_infill = true;
if (m_print_config_ptr) { if (m_print_config_ptr) {
if (!something_nonoverriddable && !layer_tools.wiping_extrusions().is_overriddable(*fill, *m_print_config_ptr, object, region)) if (!something_nonoverriddable && !layer_tools.wiping_extrusions().is_overriddable_and_mark(*fill, *m_print_config_ptr, object, region))
something_nonoverriddable = true; something_nonoverriddable = true;
} }
} }
if (something_nonoverriddable || !m_print_config_ptr) if (something_nonoverriddable || !m_print_config_ptr) {
{ if (extruder_override == 0) {
if (has_solid_infill) if (has_solid_infill)
layer_tools.extruders.push_back(region.config().solid_infill_extruder); layer_tools.extruders.emplace_back(region.config().solid_infill_extruder);
if (has_infill) if (has_infill)
layer_tools.extruders.push_back(region.config().infill_extruder); layer_tools.extruders.emplace_back(region.config().infill_extruder);
} else if (has_solid_infill || has_infill)
layer_tools.extruders.emplace_back(extruder_override);
} }
if (has_solid_infill || has_infill) if (has_solid_infill || has_infill)
layer_tools.has_object = true; layer_tools.has_object = true;
@ -199,7 +262,7 @@ void ToolOrdering::collect_extruders(const PrintObject &object)
// make sure that there are some tools for each object layer (e.g. tall wiping object will result in empty extruders vector) // make sure that there are some tools for each object layer (e.g. tall wiping object will result in empty extruders vector)
if (layer.extruders.empty() && layer.has_object) if (layer.extruders.empty() && layer.has_object)
layer.extruders.push_back(0); // 0="dontcare" extruder - it will be taken care of in reorder_extruders layer.extruders.emplace_back(0); // 0="dontcare" extruder - it will be taken care of in reorder_extruders
} }
} }
@ -254,11 +317,9 @@ void ToolOrdering::reorder_extruders(unsigned int last_extruder_id)
for (unsigned int &extruder_id : lt.extruders) { for (unsigned int &extruder_id : lt.extruders) {
assert(extruder_id > 0); assert(extruder_id > 0);
-- extruder_id; -- extruder_id;
} }
} }
void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z) void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z)
{ {
if (m_layer_tools.empty()) if (m_layer_tools.empty())
@ -394,6 +455,46 @@ void ToolOrdering::collect_extruder_statistics(bool prime_multi_material)
} }
} }
// 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.
void ToolOrdering::assign_custom_gcodes(const Print &print)
{
const std::vector<Model::CustomGCode> &custom_gcode_per_print_z = print.model().custom_gcode_per_print_z;
if (custom_gcode_per_print_z.empty())
return;
unsigned int num_extruders = *std::max_element(m_all_printing_extruders.begin(), m_all_printing_extruders.end()) + 1;
std::vector<unsigned char> extruder_printing_above(num_extruders, false);
auto custom_gcode_it = custom_gcode_per_print_z.rbegin();
// From the last layer to the first one:
for (auto it_lt = m_layer_tools.rbegin(); it_lt != m_layer_tools.rend(); ++ it_lt) {
LayerTools &lt = *it_lt;
// Add the extruders of the current layer to the set of extruders printing at and above this print_z.
for (unsigned int i : lt.extruders)
extruder_printing_above[i] = true;
// Skip all custom G-codes above this layer and skip all extruder switches.
for (; custom_gcode_it != custom_gcode_per_print_z.rend() && (custom_gcode_it->print_z > lt.print_z + EPSILON || custom_gcode_it->gcode == ExtruderChangeCode); ++ custom_gcode_it);
if (custom_gcode_it == custom_gcode_per_print_z.rend())
// Custom G-codes were processed.
break;
// Some custom G-code is configured for this layer or a layer below.
const Model::CustomGCode &custom_gcode = *custom_gcode_it;
// print_z of the layer below the current layer.
coordf_t print_z_below = 0.;
if (auto it_lt_below = it_lt; ++ it_lt_below != m_layer_tools.rend())
print_z_below = it_lt_below->print_z;
if (custom_gcode.print_z > print_z_below + 0.5 * EPSILON) {
// The custom G-code applies to the current layer.
if (custom_gcode.gcode != ColorChangeCode || extruder_printing_above[unsigned(custom_gcode.extruder - 1)])
// If it is color change, it will actually be useful as the exturder above will print.
lt.custom_gcode = &custom_gcode;
// Consume that custom G-code event.
++ custom_gcode_it;
}
}
}
const LayerTools& ToolOrdering::tools_for_layer(coordf_t print_z) const const LayerTools& ToolOrdering::tools_for_layer(coordf_t print_z) const
{ {
auto it_layer_tools = std::lower_bound(m_layer_tools.begin(), m_layer_tools.end(), LayerTools(print_z - EPSILON)); auto it_layer_tools = std::lower_bound(m_layer_tools.begin(), m_layer_tools.end(), LayerTools(print_z - EPSILON));
@ -411,14 +512,13 @@ const LayerTools& ToolOrdering::tools_for_layer(coordf_t print_z) const
} }
// This function is called from Print::mark_wiping_extrusions and sets extruder this entity should be printed with (-1 .. as usual) // This function is called from Print::mark_wiping_extrusions and sets extruder this entity should be printed with (-1 .. as usual)
void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies) void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, size_t copy_id, int extruder, size_t num_of_copies)
{ {
something_overridden = true; something_overridden = true;
auto entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first; // (add and) return iterator auto entity_map_it = (entity_map.emplace(entity, ExtruderPerCopy())).first; // (add and) return iterator
auto& copies_vector = entity_map_it->second; ExtruderPerCopy& copies_vector = entity_map_it->second;
if (copies_vector.size() < num_of_copies) copies_vector.resize(num_of_copies, -1);
copies_vector.resize(num_of_copies, -1);
if (copies_vector[copy_id] != -1) if (copies_vector[copy_id] != -1)
std::cout << "ERROR: Entity extruder overriden multiple times!!!\n"; // A debugging message - this must never happen. std::cout << "ERROR: Entity extruder overriden multiple times!!!\n"; // A debugging message - this must never happen.
@ -426,7 +526,6 @@ void WipingExtrusions::set_extruder_override(const ExtrusionEntity* entity, unsi
copies_vector[copy_id] = extruder; copies_vector[copy_id] = extruder;
} }
// Finds first non-soluble extruder on the layer // Finds first non-soluble extruder on the layer
int WipingExtrusions::first_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const int WipingExtrusions::first_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const
{ {
@ -449,11 +548,10 @@ int WipingExtrusions::last_nonsoluble_extruder_on_layer(const PrintConfig& print
return (-1); return (-1);
} }
// Decides whether this entity could be overridden // Decides whether this entity could be overridden
bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const
{ {
if (print_config.filament_soluble.get_at(Print::get_extruder(eec, region))) if (print_config.filament_soluble.get_at(m_layer_tools->extruder(eec, region)))
return false; return false;
if (object.config().wipe_into_objects) if (object.config().wipe_into_objects)
@ -465,7 +563,6 @@ bool WipingExtrusions::is_overriddable(const ExtrusionEntityCollection& eec, con
return true; return true;
} }
// Following function iterates through all extrusions on the layer, remembers those that could be used for wiping after toolchange // Following function iterates through all extrusions on the layer, remembers those that could be used for wiping after toolchange
// and returns volume that is left to be wiped on the wipe tower. // and returns volume that is left to be wiped on the wipe tower.
float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int old_extruder, unsigned int new_extruder, float volume_to_wipe) float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int old_extruder, unsigned int new_extruder, float volume_to_wipe)
@ -473,8 +570,8 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
const LayerTools& lt = *m_layer_tools; const LayerTools& lt = *m_layer_tools;
const float min_infill_volume = 0.f; // ignore infill with smaller volume than this const float min_infill_volume = 0.f; // ignore infill with smaller volume than this
if (print.config().filament_soluble.get_at(old_extruder) || print.config().filament_soluble.get_at(new_extruder)) if (! this->something_overridable || volume_to_wipe <= 0. || print.config().filament_soluble.get_at(old_extruder) || print.config().filament_soluble.get_at(new_extruder))
return volume_to_wipe; // Soluble filament cannot be wiped in a random infill, neither the filament after it return std::max(0.f, volume_to_wipe); // Soluble filament cannot be wiped in a random infill, neither the filament after it
// we will sort objects so that dedicated for wiping are at the beginning: // we will sort objects so that dedicated for wiping are at the beginning:
PrintObjectPtrs object_list = print.objects(); PrintObjectPtrs object_list = print.objects();
@ -497,13 +594,13 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
const PrintObject* object = object_list[i]; const PrintObject* object = object_list[i];
// Finds this layer: // Finds this layer:
auto this_layer_it = std::find_if(object->layers().begin(), object->layers().end(), [&lt](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; }); const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON);
if (this_layer_it == object->layers().end()) if (this_layer == nullptr)
continue; continue;
const Layer* this_layer = *this_layer_it;
size_t num_of_copies = object->copies().size(); size_t num_of_copies = object->copies().size();
for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves // iterate through copies (aka PrintObject instances) first, so that we mark neighbouring infills to minimize travel moves
for (unsigned int copy = 0; copy < num_of_copies; ++copy) {
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
const auto& region = *object->print()->regions()[region_id]; const auto& region = *object->print()->regions()[region_id];
@ -511,51 +608,48 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
if (!region.config().wipe_into_infill && !object->config().wipe_into_objects) if (!region.config().wipe_into_infill && !object->config().wipe_into_objects)
continue; continue;
bool wipe_into_infill_only = ! object->config().wipe_into_objects && region.config().wipe_into_infill;
if ((!print.config().infill_first ? perimeters_done : !perimeters_done) || (!object->config().wipe_into_objects && region.config().wipe_into_infill)) { if (print.config().infill_first != perimeters_done || wipe_into_infill_only) {
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->fills.entities) { // iterate through all infill Collections
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee); auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (!is_overriddable(*fill, print.config(), *object, region)) if (!is_overriddable(*fill, print.config(), *object, region))
continue; continue;
if (volume_to_wipe<=0) if (wipe_into_infill_only && ! print.config().infill_first)
continue;
if (!object->config().wipe_into_objects && !print.config().infill_first && region.config().wipe_into_infill)
// In this case we must check that the original extruder is used on this layer before the one we are overridding // In this case we must check that the original extruder is used on this layer before the one we are overridding
// (and the perimeters will be finished before the infill is printed): // (and the perimeters will be finished before the infill is printed):
if (!lt.is_extruder_order(region.config().perimeter_extruder - 1, new_extruder)) if (!lt.is_extruder_order(lt.perimeter_extruder(region), new_extruder))
continue; continue;
if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { // this infill will be used to wipe this extruder if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { // this infill will be used to wipe this extruder
set_extruder_override(fill, copy, new_extruder, num_of_copies); set_extruder_override(fill, copy, new_extruder, num_of_copies);
volume_to_wipe -= float(fill->total_volume()); if ((volume_to_wipe -= float(fill->total_volume())) <= 0.f)
// More material was purged already than asked for.
return 0.f;
} }
} }
} }
// Now the same for perimeters - see comments above for explanation: // Now the same for perimeters - see comments above for explanation:
if (object->config().wipe_into_objects && (print.config().infill_first ? perimeters_done : !perimeters_done)) if (object->config().wipe_into_objects && print.config().infill_first == perimeters_done)
{ {
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) { for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) {
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee); auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (!is_overriddable(*fill, print.config(), *object, region)) if (is_overriddable(*fill, print.config(), *object, region) && !is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume) {
continue;
if (volume_to_wipe<=0)
continue;
if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) {
set_extruder_override(fill, copy, new_extruder, num_of_copies); set_extruder_override(fill, copy, new_extruder, num_of_copies);
volume_to_wipe -= float(fill->total_volume()); if ((volume_to_wipe -= float(fill->total_volume())) <= 0.f)
// More material was purged already than asked for.
return 0.f;
} }
} }
} }
} }
} }
} }
return std::max(0.f, volume_to_wipe); // Some purge remains to be done on the Wipe Tower.
assert(volume_to_wipe > 0.);
return volume_to_wipe;
} }
@ -566,16 +660,18 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
// them again and make sure we override it. // them again and make sure we override it.
void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
{ {
if (! this->something_overridable)
return;
const LayerTools& lt = *m_layer_tools; const LayerTools& lt = *m_layer_tools;
unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config()); unsigned int first_nonsoluble_extruder = first_nonsoluble_extruder_on_layer(print.config());
unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config()); unsigned int last_nonsoluble_extruder = last_nonsoluble_extruder_on_layer(print.config());
for (const PrintObject* object : print.objects()) { for (const PrintObject* object : print.objects()) {
// Finds this layer: // Finds this layer:
auto this_layer_it = std::find_if(object->layers().begin(), object->layers().end(), [&lt](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; }); const Layer* this_layer = object->get_layer_at_printz(lt.print_z, EPSILON);
if (this_layer_it == object->layers().end()) if (this_layer == nullptr)
continue; continue;
const Layer* this_layer = *this_layer_it;
size_t num_of_copies = object->copies().size(); size_t num_of_copies = object->copies().size();
for (size_t copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves for (size_t copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves
@ -598,9 +694,8 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
// Either way, we will now force-override it with something suitable: // Either way, we will now force-override it with something suitable:
if (print.config().infill_first if (print.config().infill_first
|| object->config().wipe_into_objects // in this case the perimeter is overridden, so we can override by the last one safely || object->config().wipe_into_objects // in this case the perimeter is overridden, so we can override by the last one safely
|| lt.is_extruder_order(region.config().perimeter_extruder - 1, last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints || lt.is_extruder_order(lt.perimeter_extruder(region), last_nonsoluble_extruder // !infill_first, but perimeter is already printed when last extruder prints
|| std::find(lt.extruders.begin(), lt.extruders.end(), region.config().infill_extruder - 1) == lt.extruders.end()) // we have to force override - this could violate infill_first (FIXME) || ! lt.has_extruder(lt.infill_extruder(region)))) // we have to force override - this could violate infill_first (FIXME)
)
set_extruder_override(fill, copy, (print.config().infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies); set_extruder_override(fill, copy, (print.config().infill_first ? first_nonsoluble_extruder : last_nonsoluble_extruder), num_of_copies);
else { else {
// In this case we can (and should) leave it to be printed normally. // In this case we can (and should) leave it to be printed normally.
@ -611,42 +706,31 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
// Now the same for perimeters - see comments above for explanation: // Now the same for perimeters - see comments above for explanation:
for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) { // iterate through all perimeter Collections for (const ExtrusionEntity* ee : this_layer->regions()[region_id]->perimeters.entities) { // iterate through all perimeter Collections
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee); auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (!is_overriddable(*fill, print.config(), *object, region) if (is_overriddable(*fill, print.config(), *object, region) && ! is_entity_overridden(fill, copy))
|| is_entity_overridden(fill, copy) ) set_extruder_override(fill, copy, (print.config().infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies);
continue;
set_extruder_override(fill, copy, (print.config().infill_first ? last_nonsoluble_extruder : first_nonsoluble_extruder), num_of_copies);
} }
} }
} }
} }
} }
// Following function is called from GCode::process_layer and returns pointer to vector with information about which extruders should be used for given copy of this entity.
// If this extrusion does not have any override, nullptr is returned.
// Otherwise it modifies the vector in place and changes all -1 to correct_extruder_id (at the time the overrides were created, correct extruders were not known,
// so -1 was used as "print as usual").
// The resulting vector therefore keeps track of which extrusions are the ones that were overridden and which were not. If the extruder used is overridden,
// its number is saved as is (zero-based index). Regular extrusions are saved as -number-1 (unfortunately there is no negative zero).
// Following function is called from process_layer and returns pointer to vector with information about which extruders should be used for given copy of this entity. const WipingExtrusions::ExtruderPerCopy* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies)
// It first makes sure the pointer is valid (creates the vector if it does not exist) and contains a record for each copy
// It also modifies the vector in place and changes all -1 to correct_extruder_id (at the time the overrides were created, correct extruders were not known,
// so -1 was used as "print as usual".
// The resulting vector has to keep track of which extrusions are the ones that were overridden and which were not. In the extruder is used as overridden,
// its number is saved as it is (zero-based index). Usual extrusions are saved as -number-1 (unfortunately there is no negative zero).
const std::vector<int>* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies)
{ {
ExtruderPerCopy *overrides = nullptr;
auto entity_map_it = entity_map.find(entity); auto entity_map_it = entity_map.find(entity);
if (entity_map_it == entity_map.end()) if (entity_map_it != entity_map.end()) {
entity_map_it = (entity_map.insert(std::make_pair(entity, std::vector<int>()))).first; overrides = &entity_map_it->second;
overrides->resize(num_of_copies, -1);
// Now the entity_map_it should be valid, let's make sure the vector is long enough: // Each -1 now means "print as usual" - we will replace it with actual extruder id (shifted it so we don't lose that information):
entity_map_it->second.resize(num_of_copies, -1); std::replace(overrides->begin(), overrides->end(), -1, -correct_extruder_id-1);
}
// Each -1 now means "print as usual" - we will replace it with actual extruder id (shifted it so we don't lose that information): return overrides;
std::replace(entity_map_it->second.begin(), entity_map_it->second.end(), -1, -correct_extruder_id-1);
return &(entity_map_it->second);
} }

View File

@ -7,6 +7,8 @@
#include <utility> #include <utility>
#include <boost/container/small_vector.hpp>
namespace Slic3r { namespace Slic3r {
class Print; class Print;
@ -25,8 +27,19 @@ public:
return something_overridden; return something_overridden;
} }
// When allocating extruder overrides of an object's ExtrusionEntity, overrides for maximum 3 copies are allocated in place.
typedef boost::container::small_vector<int32_t, 3> ExtruderPerCopy;
class ExtruderOverrides
{
public:
ExtruderOverrides(const ExtruderPerCopy *overrides, const int correct_extruder_id) : m_overrides(overrides) {}
private:
const ExtruderPerCopy *m_overrides;
};
// This is called from GCode::process_layer - see implementation for further comments: // This is called from GCode::process_layer - see implementation for further comments:
const std::vector<int>* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies); const ExtruderPerCopy* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies);
// This function goes through all infill entities, decides which ones will be used for wiping and // This function goes through all infill entities, decides which ones will be used for wiping and
// marks them by the extruder id. Returns volume that remains to be wiped on the wipe tower: // marks them by the extruder id. Returns volume that remains to be wiped on the wipe tower:
@ -35,6 +48,11 @@ public:
void ensure_perimeters_infills_order(const Print& print); void ensure_perimeters_infills_order(const Print& print);
bool is_overriddable(const ExtrusionEntityCollection& ee, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const; bool is_overriddable(const ExtrusionEntityCollection& ee, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) const;
bool is_overriddable_and_mark(const ExtrusionEntityCollection& ee, const PrintConfig& print_config, const PrintObject& object, const PrintRegion& region) {
bool out = this->is_overriddable(ee, print_config, object, region);
this->something_overridable |= out;
return out;
}
void set_layer_tools_ptr(const LayerTools* lt) { m_layer_tools = lt; } void set_layer_tools_ptr(const LayerTools* lt) { m_layer_tools = lt; }
@ -43,14 +61,16 @@ private:
int last_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const; int last_nonsoluble_extruder_on_layer(const PrintConfig& print_config) const;
// This function is called from mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual) // This function is called from mark_wiping_extrusions and sets extruder that it should be printed with (-1 .. as usual)
void set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies); void set_extruder_override(const ExtrusionEntity* entity, size_t copy_id, int extruder, size_t num_of_copies);
// Returns true in case that entity is not printed with its usual extruder for a given copy: // Returns true in case that entity is not printed with its usual extruder for a given copy:
bool is_entity_overridden(const ExtrusionEntity* entity, size_t copy_id) const { bool is_entity_overridden(const ExtrusionEntity* entity, size_t copy_id) const {
return (entity_map.find(entity) == entity_map.end() ? false : entity_map.at(entity).at(copy_id) != -1); auto it = entity_map.find(entity);
return it == entity_map.end() ? false : it->second[copy_id] != -1;
} }
std::map<const ExtrusionEntity*, std::vector<int>> entity_map; // to keep track of who prints what std::map<const ExtrusionEntity*, ExtruderPerCopy> entity_map; // to keep track of who prints what
bool something_overridable = false;
bool something_overridden = false; bool something_overridden = false;
const LayerTools* m_layer_tools; // so we know which LayerTools object this belongs to const LayerTools* m_layer_tools; // so we know which LayerTools object this belongs to
}; };
@ -60,13 +80,7 @@ private:
class LayerTools class LayerTools
{ {
public: public:
LayerTools(const coordf_t z, const PrintConfig* print_config_ptr = nullptr) : LayerTools(const coordf_t z) : print_z(z) {}
print_z(z),
has_object(false),
has_support(false),
has_wipe_tower(false),
wipe_tower_partitions(0),
wipe_tower_layer_height(0.) {}
// Changing these operators to epsilon version can make a problem in cases where support and object layers get close to each other. // Changing these operators to epsilon version can make a problem in cases where support and object layers get close to each other.
// In case someone tries to do it, make sure you know what you're doing and test it properly (slice multiple objects at once with supports). // In case someone tries to do it, make sure you know what you're doing and test it properly (slice multiple objects at once with supports).
@ -74,20 +88,33 @@ public:
bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; } bool operator==(const LayerTools &rhs) const { return print_z == rhs.print_z; }
bool is_extruder_order(unsigned int a, unsigned int b) const; bool is_extruder_order(unsigned int a, unsigned int b) const;
bool has_extruder(unsigned int extruder) const { return std::find(this->extruders.begin(), this->extruders.end(), extruder) != this->extruders.end(); }
coordf_t print_z; // Return a zero based extruder from the region, or extruder_override if overriden.
bool has_object; unsigned int perimeter_extruder(const PrintRegion &region) const;
bool has_support; unsigned int infill_extruder(const PrintRegion &region) const;
unsigned int solid_infill_extruder(const PrintRegion &region) const;
// Returns a zero based extruder this eec should be printed with, according to PrintRegion config or extruder_override if overriden.
unsigned int extruder(const ExtrusionEntityCollection &extrusions, const PrintRegion &region) const;
coordf_t print_z = 0.;
bool has_object = false;
bool has_support = false;
// Zero based extruder IDs, ordered to minimize tool switches. // Zero based extruder IDs, ordered to minimize tool switches.
std::vector<unsigned int> extruders; std::vector<unsigned int> extruders;
// 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;
// Will there be anything extruded on this layer for the wipe tower? // Will there be anything extruded on this layer for the wipe tower?
// Due to the support layers possibly interleaving the object layers, // Due to the support layers possibly interleaving the object layers,
// wipe tower will be disabled for some support only layers. // wipe tower will be disabled for some support only layers.
bool has_wipe_tower; bool has_wipe_tower = false;
// Number of wipe tower partitions to support the required number of tool switches // Number of wipe tower partitions to support the required number of tool switches
// and to support the wipe tower partitions above this one. // and to support the wipe tower partitions above this one.
size_t wipe_tower_partitions; size_t wipe_tower_partitions = 0;
coordf_t wipe_tower_layer_height; coordf_t wipe_tower_layer_height = 0.;
// Custom G-code (color change, extruder switch, pause) to be performed before this layer starts to print.
const Model::CustomGCode *custom_gcode = nullptr;
WipingExtrusions& wiping_extrusions() { WipingExtrusions& wiping_extrusions() {
m_wiping_extrusions.set_layer_tools_ptr(this); m_wiping_extrusions.set_layer_tools_ptr(this);
@ -108,11 +135,11 @@ public:
// For the use case when each object is printed separately // For the use case when each object is printed separately
// (print.config.complete_objects is true). // (print.config.complete_objects is true).
ToolOrdering(const PrintObject &object, unsigned int first_extruder = (unsigned int)-1, bool prime_multi_material = false); ToolOrdering(const PrintObject &object, unsigned int first_extruder, bool prime_multi_material = false);
// For the use case when all objects are printed at once. // For the use case when all objects are printed at once.
// (print.config.complete_objects is false). // (print.config.complete_objects is false).
ToolOrdering(const Print &print, unsigned int first_extruder = (unsigned int)-1, bool prime_multi_material = false); ToolOrdering(const Print &print, unsigned int first_extruder, bool prime_multi_material = false);
void clear() { m_layer_tools.clear(); } void clear() { m_layer_tools.clear(); }
@ -139,10 +166,11 @@ public:
private: private:
void initialize_layers(std::vector<coordf_t> &zs); void initialize_layers(std::vector<coordf_t> &zs);
void collect_extruders(const PrintObject &object); 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 reorder_extruders(unsigned int last_extruder_id);
void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z); void fill_wipe_tower_partitions(const PrintConfig &config, coordf_t object_bottom_z);
void collect_extruder_statistics(bool prime_multi_material); void collect_extruder_statistics(bool prime_multi_material);
void assign_custom_gcodes(const Print &print);
std::vector<LayerTools> m_layer_tools; std::vector<LayerTools> m_layer_tools;
// First printing extruder, including the multi-material priming sequence. // First printing extruder, including the multi-material priming sequence.
@ -152,7 +180,6 @@ private:
// All extruders, which extrude some material over m_layer_tools. // All extruders, which extrude some material over m_layer_tools.
std::vector<unsigned int> m_all_printing_extruders; std::vector<unsigned int> m_all_printing_extruders;
const PrintConfig* m_print_config_ptr = nullptr; const PrintConfig* m_print_config_ptr = nullptr;
}; };

View File

@ -1261,7 +1261,9 @@ namespace Slic3r {
if (line.has_e()) if (line.has_e())
{ {
set_axis_origin(E, get_axis_position(E) - line.e() * lengthsScaleFactor); // extruder coordinate can grow to the point where its float representation does not allow for proper addition with small increments,
// we set the value taken from the G92 line as the new current position for it
set_axis_position(E, line.e() * lengthsScaleFactor);
anyFound = true; anyFound = true;
} }
else else

View File

@ -19,12 +19,13 @@ void GCodeWriter::apply_print_config(const PrintConfig &print_config)
this->config.apply(print_config, true); this->config.apply(print_config, true);
m_extrusion_axis = this->config.get_extrusion_axis(); m_extrusion_axis = this->config.get_extrusion_axis();
m_single_extruder_multi_material = print_config.single_extruder_multi_material.value; m_single_extruder_multi_material = print_config.single_extruder_multi_material.value;
m_max_acceleration = (print_config.gcode_flavor.value == gcfMarlin) ? m_max_acceleration = std::lrint((print_config.gcode_flavor.value == gcfMarlin) ?
print_config.machine_max_acceleration_extruding.values.front() : 0; print_config.machine_max_acceleration_extruding.values.front() : 0);
} }
void GCodeWriter::set_extruders(const std::vector<unsigned int> &extruder_ids) void GCodeWriter::set_extruders(std::vector<unsigned int> extruder_ids)
{ {
std::sort(extruder_ids.begin(), extruder_ids.end());
m_extruders.clear(); m_extruders.clear();
m_extruders.reserve(extruder_ids.size()); m_extruders.reserve(extruder_ids.size());
for (unsigned int extruder_id : extruder_ids) for (unsigned int extruder_id : extruder_ids)
@ -247,9 +248,9 @@ std::string GCodeWriter::toolchange_prefix() const
std::string GCodeWriter::toolchange(unsigned int extruder_id) std::string GCodeWriter::toolchange(unsigned int extruder_id)
{ {
// set the new extruder // set the new extruder
auto it_extruder = std::lower_bound(m_extruders.begin(), m_extruders.end(), Extruder::key(extruder_id)); auto it_extruder = Slic3r::lower_bound_by_predicate(m_extruders.begin(), m_extruders.end(), [extruder_id](const Extruder &e) { return e.id() < extruder_id; });
assert(it_extruder != m_extruders.end()); assert(it_extruder != m_extruders.end() && it_extruder->id() == extruder_id);
m_extruder = const_cast<Extruder*>(&*it_extruder); m_extruder = &*it_extruder;
// return the toolchange command // return the toolchange command
// if we are running a single-extruder setup, just set the extruder and return nothing // if we are running a single-extruder setup, just set the extruder and return nothing

View File

@ -33,7 +33,7 @@ public:
std::string extrusion_axis() const { return m_extrusion_axis; } std::string extrusion_axis() const { return m_extrusion_axis; }
void apply_print_config(const PrintConfig &print_config); void apply_print_config(const PrintConfig &print_config);
// Extruders are expected to be sorted in an increasing order. // Extruders are expected to be sorted in an increasing order.
void set_extruders(const std::vector<unsigned int> &extruder_ids); void set_extruders(std::vector<unsigned int> extruder_ids);
const std::vector<Extruder>& extruders() const { return m_extruders; } const std::vector<Extruder>& extruders() const { return m_extruders; }
std::vector<unsigned int> extruder_ids() const { std::vector<unsigned int> extruder_ids() const {
std::vector<unsigned int> out; std::vector<unsigned int> out;
@ -74,7 +74,8 @@ public:
Vec3d get_position() const { return m_pos; } Vec3d get_position() const { return m_pos; }
private: private:
std::vector<Extruder> m_extruders; // Extruders are sorted by their ID, so that binary search is possible.
std::vector<Extruder> m_extruders;
std::string m_extrusion_axis; std::string m_extrusion_axis;
bool m_single_extruder_multi_material; bool m_single_extruder_multi_material;
Extruder* m_extruder; Extruder* m_extruder;

View File

@ -598,21 +598,6 @@ std::string Model::propose_export_file_name_and_path(const std::string &new_exte
return boost::filesystem::path(this->propose_export_file_name_and_path()).replace_extension(new_extension).string(); return boost::filesystem::path(this->propose_export_file_name_and_path()).replace_extension(new_extension).string();
} }
std::vector<std::pair<double, DynamicPrintConfig>> Model::get_custom_tool_changes(double default_layer_height, size_t num_extruders) const
{
std::vector<std::pair<double, DynamicPrintConfig>> custom_tool_changes;
for (const CustomGCode& custom_gcode : custom_gcode_per_print_z)
if (custom_gcode.gcode == ExtruderChangeCode) {
DynamicPrintConfig config;
// If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders
config.set_key_value("extruder", new ConfigOptionInt(custom_gcode.extruder > num_extruders ? 0 : custom_gcode.extruder));
// For correct extruders(tools) changing, we should decrease custom_gcode.height value by one default layer height
custom_tool_changes.push_back({ custom_gcode.print_z - default_layer_height, config });
}
return custom_tool_changes;
}
ModelObject::~ModelObject() ModelObject::~ModelObject()
{ {
this->clear_volumes(); this->clear_volumes();
@ -1856,6 +1841,19 @@ arrangement::ArrangePolygon ModelInstance::get_arrange_polygon() const
return ret; return ret;
} }
// Return pairs of <print_z, 1-based extruder ID> sorted by increasing print_z from custom_gcode_per_print_z.
// print_z corresponds to the first layer printed with the new extruder.
std::vector<std::pair<double, unsigned int>> custom_tool_changes(const Model &model, size_t num_extruders)
{
std::vector<std::pair<double, unsigned int>> custom_tool_changes;
for (const Model::CustomGCode &custom_gcode : model.custom_gcode_per_print_z)
if (custom_gcode.gcode == ExtruderChangeCode) {
// If extruder count in PrinterSettings was changed, use default (0) extruder for extruders, more than num_extruders
custom_tool_changes.emplace_back(custom_gcode.print_z, static_cast<unsigned int>(custom_gcode.extruder > num_extruders ? 1 : custom_gcode.extruder));
}
return custom_tool_changes;
}
// Test whether the two models contain the same number of ModelObjects with the same set of IDs // Test whether the two models contain the same number of ModelObjects with the same set of IDs
// ordered in the same order. In that case it is not necessary to kill the background processing. // ordered in the same order. In that case it is not necessary to kill the background processing.
bool model_object_list_equal(const Model &model_old, const Model &model_new) bool model_object_list_equal(const Model &model_old, const Model &model_new)

View File

@ -838,9 +838,6 @@ public:
// Propose an output path, replace extension. The new_extension shall contain the initial dot. // Propose an output path, replace extension. The new_extension shall contain the initial dot.
std::string propose_export_file_name_and_path(const std::string &new_extension) const; std::string propose_export_file_name_and_path(const std::string &new_extension) const;
// from custom_gcode_per_print_z get just tool_change codes
std::vector<std::pair<double, DynamicPrintConfig>> get_custom_tool_changes(double default_layer_height, size_t num_extruders) const;
private: private:
explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); }; explicit Model(int) : ObjectBase(-1) { assert(this->id().invalid()); };
void assign_new_unique_ids_recursive(); void assign_new_unique_ids_recursive();
@ -857,6 +854,10 @@ private:
#undef OBJECTBASE_DERIVED_COPY_MOVE_CLONE #undef OBJECTBASE_DERIVED_COPY_MOVE_CLONE
#undef OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE #undef OBJECTBASE_DERIVED_PRIVATE_COPY_MOVE
// Return pairs of <print_z, 1-based extruder ID> sorted by increasing print_z from custom_gcode_per_print_z.
// print_z corresponds to the first layer printed with the new extruder.
extern std::vector<std::pair<double, unsigned int>> custom_tool_changes(const Model &model, size_t num_extruders);
// Test whether the two models contain the same number of ModelObjects with the same set of IDs // Test whether the two models contain the same number of ModelObjects with the same set of IDs
// ordered in the same order. In that case it is not necessary to kill the background processing. // ordered in the same order. In that case it is not necessary to kill the background processing.
extern bool model_object_list_equal(const Model &model_old, const Model &model_new); extern bool model_object_list_equal(const Model &model_old, const Model &model_new);

View File

@ -638,48 +638,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr); m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr);
} }
// Convert input config ranges into continuous non-overlapping sorted vector of intervals and their configs,
// considering custom_tool_change values
void assign(const t_layer_config_ranges &in, const std::vector<std::pair<double, DynamicPrintConfig>> &custom_tool_changes) {
m_ranges.clear();
m_ranges.reserve(in.size());
// Input ranges are sorted lexicographically. First range trims the other ranges.
coordf_t last_z = 0;
for (const std::pair<const t_layer_height_range, DynamicPrintConfig> &range : in)
if (range.first.second > last_z) {
coordf_t min_z = std::max(range.first.first, 0.);
if (min_z > last_z + EPSILON) {
m_ranges.emplace_back(t_layer_height_range(last_z, min_z), nullptr);
last_z = min_z;
}
if (range.first.second > last_z + EPSILON) {
const DynamicPrintConfig* cfg = &range.second;
m_ranges.emplace_back(t_layer_height_range(last_z, range.first.second), cfg);
last_z = range.first.second;
}
}
// add ranges for extruder changes from custom_tool_changes
for (size_t i = 0; i < custom_tool_changes.size(); i++) {
const DynamicPrintConfig* cfg = &custom_tool_changes[i].second;
coordf_t cur_Z = custom_tool_changes[i].first;
coordf_t next_Z = i == custom_tool_changes.size()-1 ? DBL_MAX : custom_tool_changes[i+1].first;
if (cur_Z > last_z + EPSILON) {
if (i==0)
m_ranges.emplace_back(t_layer_height_range(last_z, cur_Z), nullptr);
m_ranges.emplace_back(t_layer_height_range(cur_Z, next_Z), cfg);
}
else if (next_Z > last_z + EPSILON)
m_ranges.emplace_back(t_layer_height_range(last_z, next_Z), cfg);
}
if (m_ranges.empty())
m_ranges.emplace_back(t_layer_height_range(0, DBL_MAX), nullptr);
else if (m_ranges.back().second == nullptr)
m_ranges.back().first.second = DBL_MAX;
else if (m_ranges.back().first.second != DBL_MAX)
m_ranges.emplace_back(t_layer_height_range(m_ranges.back().first.second, DBL_MAX), nullptr);
}
const DynamicPrintConfig* config(const t_layer_height_range &range) const { const DynamicPrintConfig* config(const t_layer_height_range &range) const {
auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr)); auto it = std::lower_bound(m_ranges.begin(), m_ranges.end(), std::make_pair< t_layer_height_range, const DynamicPrintConfig*>(t_layer_height_range(range.first - EPSILON, range.second - EPSILON), nullptr));
// #ys_FIXME_COLOR // #ys_FIXME_COLOR
@ -733,17 +691,15 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
for (const ModelObject *model_object : m_model.objects) for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::New); model_object_status.emplace(model_object->id(), ModelObjectStatus::New);
} else { } else {
if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) {
// If custom gcode per layer height was changed, we should stop background processing.
update_apply_status(this->invalidate_steps({ psWipeTower, psGCodeExport }));
m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
}
if (model_object_list_equal(m_model, model)) { if (model_object_list_equal(m_model, model)) {
// The object list did not change. // The object list did not change.
for (const ModelObject *model_object : m_model.objects) for (const ModelObject *model_object : m_model.objects)
model_object_status.emplace(model_object->id(), ModelObjectStatus::Old); model_object_status.emplace(model_object->id(), ModelObjectStatus::Old);
// But if custom gcode per layer height was changed
if (m_model.custom_gcode_per_print_z != model.custom_gcode_per_print_z) {
// we should stop background processing
update_apply_status(this->invalidate_step(psGCodeExport));
m_model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
}
} else if (model_object_list_extended(m_model, model)) { } else if (model_object_list_extended(m_model, model)) {
// Add new objects. Their volumes and configs will be synchronized later. // Add new objects. Their volumes and configs will be synchronized later.
update_apply_status(this->invalidate_step(psGCodeExport)); update_apply_status(this->invalidate_step(psGCodeExport));
@ -835,9 +791,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
for (PrintObject *print_object : m_objects) for (PrintObject *print_object : m_objects)
print_object_status.emplace(PrintObjectStatus(print_object)); print_object_status.emplace(PrintObjectStatus(print_object));
std::vector<std::pair<double, DynamicPrintConfig>> custom_tool_changes =
m_model.get_custom_tool_changes(m_default_object_config.layer_height, num_extruders);
// 3) Synchronize ModelObjects & PrintObjects. // 3) Synchronize ModelObjects & PrintObjects.
for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) { for (size_t idx_model_object = 0; idx_model_object < model.objects.size(); ++ idx_model_object) {
ModelObject &model_object = *m_model.objects[idx_model_object]; ModelObject &model_object = *m_model.objects[idx_model_object];
@ -845,9 +798,7 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
assert(it_status != model_object_status.end()); assert(it_status != model_object_status.end());
assert(it_status->status != ModelObjectStatus::Deleted); assert(it_status->status != ModelObjectStatus::Deleted);
const ModelObject& model_object_new = *model.objects[idx_model_object]; const ModelObject& model_object_new = *model.objects[idx_model_object];
// ys_FIXME_COLOR const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges);
// const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges);
const_cast<ModelObjectStatus&>(*it_status).layer_ranges.assign(model_object_new.layer_config_ranges, custom_tool_changes);
if (it_status->status == ModelObjectStatus::New) if (it_status->status == ModelObjectStatus::New)
// PrintObject instances will be added in the next loop. // PrintObject instances will be added in the next loop.
continue; continue;
@ -1015,8 +966,6 @@ Print::ApplyStatus Print::apply(const Model &model, DynamicPrintConfig new_full_
PrintRegionConfig this_region_config; PrintRegionConfig this_region_config;
bool this_region_config_set = false; bool this_region_config_set = false;
for (PrintObject *print_object : m_objects) { for (PrintObject *print_object : m_objects) {
if(m_force_update_print_regions && !custom_tool_changes.empty())
goto print_object_end;
const LayerRanges *layer_ranges; const LayerRanges *layer_ranges;
{ {
auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id())); auto it_status = model_object_status.find(ModelObjectStatus(print_object->model_object()->id()));
@ -1963,8 +1912,8 @@ const WipeTowerData& Print::wipe_tower_data(size_t extruders_cnt, double first_l
// If the wipe tower wasn't created yet, make sure the depth and brim_width members are set to default. // If the wipe tower wasn't created yet, make sure the depth and brim_width members are set to default.
if (! is_step_done(psWipeTower) && extruders_cnt !=0) { if (! is_step_done(psWipeTower) && extruders_cnt !=0) {
float width = m_config.wipe_tower_width; float width = float(m_config.wipe_tower_width);
float brim_spacing = nozzle_diameter * 1.25f - first_layer_height * (1. - M_PI_4); float brim_spacing = float(nozzle_diameter * 1.25f - first_layer_height * (1. - M_PI_4));
const_cast<Print*>(this)->m_wipe_tower_data.depth = (900.f/width) * float(extruders_cnt - 1); const_cast<Print*>(this)->m_wipe_tower_data.depth = (900.f/width) * float(extruders_cnt - 1);
const_cast<Print*>(this)->m_wipe_tower_data.brim_width = 4.5f * brim_spacing; const_cast<Print*>(this)->m_wipe_tower_data.brim_width = 4.5f * brim_spacing;
@ -1990,6 +1939,7 @@ void Print::_make_wipe_tower()
// Let the ToolOrdering class know there will be initial priming extrusions at the start of the print. // Let the ToolOrdering class know there will be initial priming extrusions at the start of the print.
m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int)-1, true); m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int)-1, true);
if (! m_wipe_tower_data.tool_ordering.has_wipe_tower()) if (! m_wipe_tower_data.tool_ordering.has_wipe_tower())
// Don't generate any wipe tower. // Don't generate any wipe tower.
return; return;
@ -2107,13 +2057,6 @@ void Print::_make_wipe_tower()
m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges(); m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
} }
// Returns extruder this eec should be printed with, according to PrintRegion config
int Print::get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion &region)
{
return is_infill(fill.role()) ? std::max<int>(0, (is_solid_infill(fill.entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) - 1) :
std::max<int>(region.config().perimeter_extruder.value - 1, 0);
}
// Generate a recommended G-code output file name based on the format template, default extension, and template parameters // Generate a recommended G-code output file name based on the format template, default extension, and template parameters
// (timestamps, object placeholders derived from the model, current placeholder prameters and print statistics. // (timestamps, object placeholders derived from the model, current placeholder prameters and print statistics.
// Use the final print statistics if available, or just keep the print statistics placeholders if not available yet (before G-code is finalized). // Use the final print statistics if available, or just keep the print statistics placeholders if not available yet (before G-code is finalized).

View File

@ -15,6 +15,8 @@
#include "GCode/ThumbnailData.hpp" #include "GCode/ThumbnailData.hpp"
#endif // ENABLE_THUMBNAIL_GENERATOR #endif // ENABLE_THUMBNAIL_GENERATOR
#include "libslic3r.h"
namespace Slic3r { namespace Slic3r {
class Print; class Print;
@ -50,7 +52,7 @@ public:
// Average diameter of nozzles participating on extruding this region. // Average diameter of nozzles participating on extruding this region.
coordf_t bridging_height_avg(const PrintConfig &print_config) const; coordf_t bridging_height_avg(const PrintConfig &print_config) const;
// Collect extruder indices used to print this region's object. // Collect 0-based extruder indices used to print this region's object.
void collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const; void collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const;
static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig &region_config, std::vector<unsigned int> &object_extruders); static void collect_object_printing_extruders(const PrintConfig &print_config, const PrintRegionConfig &region_config, std::vector<unsigned int> &object_extruders);
@ -116,8 +118,21 @@ public:
size_t total_layer_count() const { return this->layer_count() + this->support_layer_count(); } size_t total_layer_count() const { return this->layer_count() + this->support_layer_count(); }
size_t layer_count() const { return m_layers.size(); } size_t layer_count() const { return m_layers.size(); }
void clear_layers(); void clear_layers();
Layer* get_layer(int idx) { return m_layers[idx]; } const Layer* get_layer(int idx) const { return m_layers[idx]; }
const Layer* get_layer(int idx) const { return m_layers[idx]; } Layer* get_layer(int idx) { return m_layers[idx]; }
// Get a layer exactly at print_z.
const Layer* get_layer_at_printz(coordf_t print_z) const {
auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [print_z](const Layer *layer) { return layer->print_z < print_z; });
return (it == m_layers.end() || (*it)->print_z != print_z) ? nullptr : *it;
}
Layer* get_layer_at_printz(coordf_t print_z) { return const_cast<Layer*>(std::as_const(*this).get_layer_at_printz(print_z)); }
// Get a layer approximately at print_z.
const Layer* get_layer_at_printz(coordf_t print_z, coordf_t epsilon) const {
coordf_t limit = print_z + epsilon;
auto it = Slic3r::lower_bound_by_predicate(m_layers.begin(), m_layers.end(), [limit](const Layer *layer) { return layer->print_z < limit; });
return (it == m_layers.end() || (*it)->print_z < print_z - epsilon) ? nullptr : *it;
}
Layer* get_layer_at_printz(coordf_t print_z, coordf_t epsilon) { return const_cast<Layer*>(std::as_const(*this).get_layer_at_printz(print_z, epsilon)); }
// print_z: top of the layer; slice_z: center of the layer. // print_z: top of the layer; slice_z: center of the layer.
Layer* add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z); Layer* add_layer(int id, coordf_t height, coordf_t print_z, coordf_t slice_z);
@ -345,6 +360,7 @@ public:
const PrintConfig& config() const { return m_config; } const PrintConfig& config() const { return m_config; }
const PrintObjectConfig& default_object_config() const { return m_default_object_config; } const PrintObjectConfig& default_object_config() const { return m_default_object_config; }
const PrintRegionConfig& default_region_config() const { return m_default_region_config; } const PrintRegionConfig& default_region_config() const { return m_default_region_config; }
//FIXME returning const vector to non-const PrintObject*, caller could modify PrintObjects!
const PrintObjectPtrs& objects() const { return m_objects; } const PrintObjectPtrs& objects() const { return m_objects; }
PrintObject* get_object(size_t idx) { return m_objects[idx]; } PrintObject* get_object(size_t idx) { return m_objects[idx]; }
const PrintObject* get_object(size_t idx) const { return m_objects[idx]; } const PrintObject* get_object(size_t idx) const { return m_objects[idx]; }
@ -353,9 +369,6 @@ public:
// If zero, then the print is empty and the print shall not be executed. // If zero, then the print is empty and the print shall not be executed.
unsigned int num_object_instances() const; unsigned int num_object_instances() const;
// Returns extruder this eec should be printed with, according to PrintRegion config:
static int get_extruder(const ExtrusionEntityCollection& fill, const PrintRegion &region);
const ExtrusionEntityCollection& skirt() const { return m_skirt; } const ExtrusionEntityCollection& skirt() const { return m_skirt; }
const ExtrusionEntityCollection& brim() const { return m_brim; } const ExtrusionEntityCollection& brim() const { return m_brim; }
@ -370,9 +383,6 @@ public:
// Accessed by SupportMaterial // Accessed by SupportMaterial
const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; } const PrintRegion* get_region(size_t idx) const { return m_regions[idx]; }
// force update of PrintRegions, when custom_tool_change is not empty and (Re)Slicing is started
void set_force_update_print_regions(bool force_update_print_regions) { m_force_update_print_regions = force_update_print_regions; }
protected: protected:
// methods for handling regions // methods for handling regions
PrintRegion* get_region(size_t idx) { return m_regions[idx]; } PrintRegion* get_region(size_t idx) { return m_regions[idx]; }
@ -415,9 +425,6 @@ private:
// Estimated print time, filament consumed. // Estimated print time, filament consumed.
PrintStatistics m_print_statistics; PrintStatistics m_print_statistics;
// flag used
bool m_force_update_print_regions = false;
// To allow GCode to set the Print's GCodeExport step status. // To allow GCode to set the Print's GCodeExport step status.
friend class GCode; friend class GCode;
// Allow PrintObject to access m_mutex and m_cancel_callback. // Allow PrintObject to access m_mutex and m_cancel_callback.

View File

@ -71,5 +71,8 @@
// Enable a modified version of the toolbar textures where all the icons are separated by 1 pixel // Enable a modified version of the toolbar textures where all the icons are separated by 1 pixel
#define ENABLE_MODIFIED_TOOLBAR_TEXTURES (1 && ENABLE_2_2_0_BETA1) #define ENABLE_MODIFIED_TOOLBAR_TEXTURES (1 && ENABLE_2_2_0_BETA1)
// Enable configurable paths export (fullpath or not) to 3mf and amf
#define ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF (1 && ENABLE_2_2_0_BETA1)
#endif // _technologies_h_ #endif // _technologies_h_

View File

@ -158,6 +158,53 @@ inline std::unique_ptr<T> make_unique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...)); return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
} }
// Variant of std::lower_bound() with compare predicate, but without the key.
// This variant is very useful in case that the T type is large or it does not even have a public constructor.
template<class ForwardIt, class LowerThanKeyPredicate>
ForwardIt lower_bound_by_predicate(ForwardIt first, ForwardIt last, LowerThanKeyPredicate lower_thank_key)
{
ForwardIt it;
typename std::iterator_traits<ForwardIt>::difference_type count, step;
count = std::distance(first, last);
while (count > 0) {
it = first;
step = count / 2;
std::advance(it, step);
if (lower_thank_key(*it)) {
first = ++it;
count -= step + 1;
}
else
count = step;
}
return first;
}
// from https://en.cppreference.com/w/cpp/algorithm/lower_bound
template<class ForwardIt, class T, class Compare=std::less<>>
ForwardIt binary_find(ForwardIt first, ForwardIt last, const T& value, Compare comp={})
{
// Note: BOTH type T and the type after ForwardIt is dereferenced
// must be implicitly convertible to BOTH Type1 and Type2, used in Compare.
// This is stricter than lower_bound requirement (see above)
first = std::lower_bound(first, last, value, comp);
return first != last && !comp(value, *first) ? first : last;
}
// from https://en.cppreference.com/w/cpp/algorithm/lower_bound
template<class ForwardIt, class LowerThanKeyPredicate, class EqualToKeyPredicate>
ForwardIt binary_find_by_predicate(ForwardIt first, ForwardIt last, LowerThanKeyPredicate lower_thank_key, EqualToKeyPredicate equal_to_key)
{
// Note: BOTH type T and the type after ForwardIt is dereferenced
// must be implicitly convertible to BOTH Type1 and Type2, used in Compare.
// This is stricter than lower_bound requirement (see above)
first = lower_bound_by_predicate(first, last, lower_thank_key);
return first != last && equal_to_key(*first) ? first : last;
}
template<typename T> template<typename T>
static inline T sqr(T x) static inline T sqr(T x)
{ {

View File

@ -63,6 +63,7 @@
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <boost/config.hpp> #include <boost/config.hpp>
#include <boost/config/warning_disable.hpp> #include <boost/config/warning_disable.hpp>
#include <boost/container/small_vector.hpp>
#include <boost/date_time/local_time/local_time.hpp> #include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>

View File

@ -341,24 +341,10 @@ void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox)
printf("Unable to create bed grid lines\n"); printf("Unable to create bed grid lines\n");
} }
static const VendorProfile::PrinterModel* system_printer_model(const Preset &preset)
{
const VendorProfile::PrinterModel *out = nullptr;
if (preset.vendor != nullptr) {
auto *printer_model = preset.config.opt<ConfigOptionString>("printer_model");
if (printer_model != nullptr && ! printer_model->value.empty()) {
auto it = std::find_if(preset.vendor->models.begin(), preset.vendor->models.end(), [printer_model](const VendorProfile::PrinterModel &pm) { return pm.id == printer_model->value; });
if (it != preset.vendor->models.end())
out = &(*it);
}
}
return out;
}
static std::string system_print_bed_model(const Preset &preset) static std::string system_print_bed_model(const Preset &preset)
{ {
std::string out; std::string out;
const VendorProfile::PrinterModel *pm = system_printer_model(preset); const VendorProfile::PrinterModel *pm = PresetUtils::system_printer_model(preset);
if (pm != nullptr && ! pm->bed_model.empty()) if (pm != nullptr && ! pm->bed_model.empty())
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_model; out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_model;
return out; return out;
@ -367,7 +353,7 @@ static std::string system_print_bed_model(const Preset &preset)
static std::string system_print_bed_texture(const Preset &preset) static std::string system_print_bed_texture(const Preset &preset)
{ {
std::string out; std::string out;
const VendorProfile::PrinterModel *pm = system_printer_model(preset); const VendorProfile::PrinterModel *pm = PresetUtils::system_printer_model(preset);
if (pm != nullptr && ! pm->bed_texture.empty()) if (pm != nullptr && ! pm->bed_texture.empty())
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_texture; out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_texture;
return out; return out;

View File

@ -877,13 +877,10 @@ bool can_export_to_obj(const GLVolume& volume)
if (!volume.is_active || !volume.is_extrusion_path) if (!volume.is_active || !volume.is_extrusion_path)
return false; return false;
if (volume.indexed_vertex_array.triangle_indices.empty() && (std::min(volume.indexed_vertex_array.triangle_indices_size, volume.tverts_range.second - volume.tverts_range.first) == 0)) bool has_triangles = !volume.indexed_vertex_array.triangle_indices.empty() || (std::min(volume.indexed_vertex_array.triangle_indices_size, volume.tverts_range.second - volume.tverts_range.first) > 0);
return false; bool has_quads = !volume.indexed_vertex_array.quad_indices.empty() || (std::min(volume.indexed_vertex_array.quad_indices_size, volume.qverts_range.second - volume.qverts_range.first) > 0);
if (volume.indexed_vertex_array.quad_indices.empty() && (std::min(volume.indexed_vertex_array.quad_indices_size, volume.qverts_range.second - volume.qverts_range.first) == 0)) return has_triangles || has_quads;
return false;
return true;
} }
bool GLVolumeCollection::has_toolpaths_to_export() const bool GLVolumeCollection::has_toolpaths_to_export() const

View File

@ -61,6 +61,11 @@ void AppConfig::set_defaults()
if (get("preset_update").empty()) if (get("preset_update").empty())
set("preset_update", "1"); set("preset_update", "1");
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
if (get("export_sources_full_pathnames").empty())
set("export_sources_full_pathnames", "0");
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
// remove old 'use_legacy_opengl' parameter from this config, if present // remove old 'use_legacy_opengl' parameter from this config, if present
if (!get("use_legacy_opengl").empty()) if (!get("use_legacy_opengl").empty())
erase("", "use_legacy_opengl"); erase("", "use_legacy_opengl");

View File

@ -95,14 +95,6 @@ void BackgroundSlicingProcess::process_fff()
m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data); m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data);
#endif // ENABLE_THUMBNAIL_GENERATOR #endif // ENABLE_THUMBNAIL_GENERATOR
/* #ys_FIXME_no_exported_codes
if (m_fff_print->model().custom_gcode_per_print_z != GUI::wxGetApp().model().custom_gcode_per_print_z) {
GUI::wxGetApp().model().custom_gcode_per_print_z = m_fff_print->model().custom_gcode_per_print_z;
GUI::show_info(nullptr, _(L("To except of redundant tool manipulation, \n"
"Color change(s) for unused extruder(s) was(were) deleted")), _(L("Info")));
}
*/
if (this->set_step_started(bspsGCodeFinalize)) { if (this->set_step_started(bspsGCodeFinalize)) {
if (! m_export_path.empty()) { if (! m_export_path.empty()) {
//FIXME localize the messages //FIXME localize the messages

View File

@ -132,11 +132,6 @@ public:
// This "finished" flag does not account for the final export of the output file (.gcode or zipped PNGs), // This "finished" flag does not account for the final export of the output file (.gcode or zipped PNGs),
// and it does not account for the OctoPrint scheduling. // and it does not account for the OctoPrint scheduling.
bool finished() const { return m_print->finished(); } bool finished() const { return m_print->finished(); }
void set_force_update_print_regions(bool force_update_print_regions) {
if (m_fff_print)
m_fff_print->set_force_update_print_regions(force_update_print_regions);
}
private: private:
void thread_proc(); void thread_proc();

View File

@ -1,6 +1,7 @@
#include "BitmapCache.hpp" #include "BitmapCache.hpp"
#include "libslic3r/Utils.hpp" #include "libslic3r/Utils.hpp"
#include <boost/filesystem.hpp>
#if ! defined(WIN32) && ! defined(__APPLE__) #if ! defined(WIN32) && ! defined(__APPLE__)
#define BROKEN_ALPHA #define BROKEN_ALPHA
@ -15,7 +16,7 @@
#include "nanosvg/nanosvg.h" #include "nanosvg/nanosvg.h"
#define NANOSVGRAST_IMPLEMENTATION #define NANOSVGRAST_IMPLEMENTATION
#include "nanosvg/nanosvgrast.h" #include "nanosvg/nanosvgrast.h"
#include "GUI_App.hpp" //#include "GUI_App.hpp"
namespace Slic3r { namespace GUI { namespace Slic3r { namespace GUI {
@ -226,7 +227,7 @@ wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned width,
} }
wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_width, unsigned target_height, wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_width, unsigned target_height,
float scale /* = 1.0f */, const bool grayscale/* = false*/) float scale /* = 1.0f */, const bool grayscale/* = false*/, const bool dark_mode/* = false*/)
{ {
std::string bitmap_key = bitmap_name + ( target_height !=0 ? std::string bitmap_key = bitmap_name + ( target_height !=0 ?
"-h" + std::to_string(target_height) : "-h" + std::to_string(target_height) :
@ -234,16 +235,45 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_
+ (scale != 1.0f ? "-s" + std::to_string(scale) : "") + (scale != 1.0f ? "-s" + std::to_string(scale) : "")
+ (grayscale ? "-gs" : ""); + (grayscale ? "-gs" : "");
target_height != 0 ? target_height *= scale : target_width *= scale; /* For the Dark mode of any platform, we should draw icons in respect to OS background
* Note: All standard(regular) icons are collected in "icons" folder,
* SVG-icons, which have "Dark mode" variant, are collected in "icons/white" folder
*/
std::string folder;
if (dark_mode)
{
#ifdef __WXMSW__
folder = "white\\";
#else
folder = "white/";
#endif
auto it = m_map.find(folder + bitmap_key);
if (it != m_map.end())
return it->second;
else {
it = m_map.find(bitmap_key);
if (it != m_map.end())
return it->second;
}
auto it = m_map.find(bitmap_key); if (!boost::filesystem::exists(Slic3r::var(folder + bitmap_name + ".svg")))
if (it != m_map.end()) folder.clear();
return it->second; else
bitmap_key = folder + bitmap_key;
}
else
{
auto it = m_map.find(bitmap_key);
if (it != m_map.end())
return it->second;
}
NSVGimage *image = ::nsvgParseFromFile(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f); NSVGimage *image = ::nsvgParseFromFile(Slic3r::var(folder + bitmap_name + ".svg").c_str(), "px", 96.0f);
if (image == nullptr) if (image == nullptr)
return nullptr; return nullptr;
target_height != 0 ? target_height *= scale : target_width *= scale;
float svg_scale = target_height != 0 ? float svg_scale = target_height != 0 ?
(float)target_height / image->height : target_width != 0 ? (float)target_height / image->height : target_width != 0 ?
(float)target_width / image->width : 1; (float)target_width / image->width : 1;

View File

@ -34,7 +34,7 @@ public:
// Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height/width if nonzero. // Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height/width if nonzero.
wxBitmap* load_png(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false); wxBitmap* load_png(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false);
// Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height/width. // Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height/width.
wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, float scale = 1.0f, const bool grayscale = false); wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, float scale = 1.0f, const bool grayscale = false, const bool dark_mode = false);
static wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency); static wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency);
static wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3]) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE); } static wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3]) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE); }

View File

@ -350,6 +350,21 @@ bool PrinterPicker::any_selected() const
return false; return false;
} }
std::set<std::string> PrinterPicker::get_selected_models() const
{
std::set<std::string> ret_set;
for (const auto& cb : cboxes)
if (cb->GetValue())
ret_set.emplace(cb->model);
for (const auto& cb : cboxes_alt)
if (cb->GetValue())
ret_set.emplace(cb->model);
return ret_set;
}
void PrinterPicker::on_checkbox(const Checkbox *cbox, bool checked) void PrinterPicker::on_checkbox(const Checkbox *cbox, bool checked)
{ {
PrinterPickerEvent evt(EVT_PRINTER_PICK, GetId(), vendor_id, cbox->model, cbox->variant, checked); PrinterPickerEvent evt(EVT_PRINTER_PICK, GetId(), vendor_id, cbox->model, cbox->variant, checked);
@ -500,6 +515,19 @@ bool PagePrinters::any_selected() const
return false; return false;
} }
std::set<std::string> PagePrinters::get_selected_models()
{
std::set<std::string> ret_set;
for (const auto *picker : printer_pickers)
{
std::set<std::string> tmp_models = picker->get_selected_models();
ret_set.insert(tmp_models.begin(), tmp_models.end());
}
return ret_set;
}
void PagePrinters::set_run_reason(ConfigWizard::RunReason run_reason) void PagePrinters::set_run_reason(ConfigWizard::RunReason run_reason)
{ {
if (technology == T_FFF if (technology == T_FFF
@ -765,6 +793,23 @@ PageUpdate::PageUpdate(ConfigWizard *parent)
box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); }); box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); });
} }
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
PageReloadFromDisk::PageReloadFromDisk(ConfigWizard* parent)
: ConfigWizardPage(parent, _(L("Reload from disk")), _(L("Reload from disk")))
, full_pathnames(false)
{
auto* box_pathnames = new wxCheckBox(this, wxID_ANY, _(L("Export full pathnames of models and parts sources into 3mf and amf files")));
box_pathnames->SetValue(wxGetApp().app_config->get("export_sources_full_pathnames") == "1");
append(box_pathnames);
append_text(_(L(
"If enabled, allows the Reload from disk command to automatically find and load the files when invoked.\n"
"If not enabled, the Reload from disk command will ask to select each file using an open file dialog."
)));
box_pathnames->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) { this->full_pathnames = event.IsChecked(); });
}
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
PageMode::PageMode(ConfigWizard *parent) PageMode::PageMode(ConfigWizard *parent)
: ConfigWizardPage(parent, _(L("View mode")), _(L("View mode"))) : ConfigWizardPage(parent, _(L("View mode")), _(L("View mode")))
{ {
@ -1356,6 +1401,9 @@ void ConfigWizard::priv::load_pages()
btn_finish->Enable(any_fff_selected || any_sla_selected); btn_finish->Enable(any_fff_selected || any_sla_selected);
index->add_page(page_update); index->add_page(page_update);
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
index->add_page(page_reload_from_disk);
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
index->add_page(page_mode); index->add_page(page_mode);
index->go_to(former_active); // Will restore the active item/page if possible index->go_to(former_active); // Will restore the active item/page if possible
@ -1580,6 +1628,10 @@ void ConfigWizard::priv::on_printer_pick(PagePrinters *page, const PrinterPicker
preset.is_visible = evt.enable; preset.is_visible = evt.enable;
} }
} }
// if at list one printer is selected but there in no one selected material,
// select materials which is default for selected printer(s)
select_default_materials_if_needed(pair.second.vendor_profile, page->technology, evt.model_id);
} }
if (page->technology & T_FFF) { if (page->technology & T_FFF) {
@ -1589,6 +1641,57 @@ void ConfigWizard::priv::on_printer_pick(PagePrinters *page, const PrinterPicker
} }
} }
void ConfigWizard::priv::select_default_materials_for_printer_model(const std::vector<VendorProfile::PrinterModel>& models, Technology technology, const std::string& model_id)
{
PageMaterials* page_materials = technology & T_FFF ? page_filaments : page_sla_materials;
auto it = std::find_if(models.begin(), models.end(), [model_id](VendorProfile::PrinterModel model) {return model_id == model.id; });
if (it != models.end())
for (const std::string& material : it->default_materials)
appconfig_new.set(page_materials->materials->appconfig_section(), material, "1");
}
void ConfigWizard::priv::select_default_materials_if_needed(VendorProfile* vendor_profile, Technology technology, const std::string& model_id)
{
if ((technology & T_FFF && !any_fff_selected) ||
(technology & T_SLA && !any_sla_selected) ||
check_materials_in_config(technology, false))
return;
select_default_materials_for_printer_model(vendor_profile->models, technology, model_id);
}
void ConfigWizard::priv::selected_default_materials(Technology technology)
{
auto select_default_materials_for_printer_page = [this](PagePrinters * page_printers, Technology technology)
{
std::set<std::string> selected_models = page_printers->get_selected_models();
const std::string vendor_id = page_printers->get_vendor_id();
for (auto& pair : bundles)
{
if (pair.first != vendor_id)
continue;
for (const std::string& model_id : selected_models)
select_default_materials_for_printer_model(pair.second.vendor_profile->models, technology, model_id);
}
};
PagePrinters* page_printers = technology & T_FFF ? page_fff : page_msla;
select_default_materials_for_printer_page(page_printers, technology);
for (const auto& printer : pages_3rdparty)
{
page_printers = technology & T_FFF ? printer.second.first : printer.second.second;
if (page_printers)
select_default_materials_for_printer_page(page_printers, technology);
}
update_materials(technology);
(technology& T_FFF ? page_filaments : page_sla_materials)->reload_presets();
}
void ConfigWizard::priv::on_3rdparty_install(const VendorProfile *vendor, bool install) void ConfigWizard::priv::on_3rdparty_install(const VendorProfile *vendor, bool install)
{ {
auto it = pages_3rdparty.find(vendor->id); auto it = pages_3rdparty.find(vendor->id);
@ -1625,7 +1728,7 @@ bool ConfigWizard::priv::on_bnt_finish()
return check_materials_in_config(T_ANY); return check_materials_in_config(T_ANY);
} }
bool ConfigWizard::priv::check_materials_in_config(Technology technology) bool ConfigWizard::priv::check_materials_in_config(Technology technology, bool show_info_msg)
{ {
const auto exist_preset = [this](const std::string& section, const Materials& materials) const auto exist_preset = [this](const std::string& section, const Materials& materials)
{ {
@ -1640,15 +1743,32 @@ bool ConfigWizard::priv::check_materials_in_config(Technology technology)
return false; return false;
}; };
const auto ask_and_selected_default_materials = [this](wxString message, Technology technology)
{
wxMessageDialog msg(q, message, _(L("Notice")), wxYES_NO);
if (msg.ShowModal() == wxID_YES)
selected_default_materials(technology);
};
if (any_fff_selected && technology & T_FFF && !exist_preset(AppConfig::SECTION_FILAMENTS, filaments)) if (any_fff_selected && technology & T_FFF && !exist_preset(AppConfig::SECTION_FILAMENTS, filaments))
{ {
show_info(q, _(L("You have to select at least one filament for selected printers")), ""); if (show_info_msg)
{
wxString message = _(L("You have to select at least one filament for selected printers")) + "\n\n\t" +
_(L("Do you want to automatic select default filaments?"));
ask_and_selected_default_materials(message, T_FFF);
}
return false; return false;
} }
if (any_sla_selected && technology & T_SLA && !exist_preset(AppConfig::SECTION_MATERIALS, sla_materials)) if (any_sla_selected && technology & T_SLA && !exist_preset(AppConfig::SECTION_MATERIALS, sla_materials))
{ {
show_info(q, _(L("You have to select at least one material for selected printers")), ""); if (show_info_msg)
{
wxString message = _(L("You have to select at least one material for selected printers")) + "\n\n\t" +
_(L("Do you want to automatic select default materials?"));
ask_and_selected_default_materials(message, T_SLA);
}
return false; return false;
} }
@ -1730,6 +1850,11 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
} }
app_config->set("version_check", page_update->version_check ? "1" : "0"); app_config->set("version_check", page_update->version_check ? "1" : "0");
app_config->set("preset_update", page_update->preset_update ? "1" : "0"); app_config->set("preset_update", page_update->preset_update ? "1" : "0");
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
app_config->set("export_sources_full_pathnames", page_reload_from_disk->full_pathnames ? "1" : "0");
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
page_mode->serialize_mode(app_config); page_mode->serialize_mode(app_config);
std::string preferred_model; std::string preferred_model;
@ -1885,6 +2010,9 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
p->add_page(p->page_custom = new PageCustom(this)); p->add_page(p->page_custom = new PageCustom(this));
p->add_page(p->page_update = new PageUpdate(this)); p->add_page(p->page_update = new PageUpdate(this));
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
p->add_page(p->page_reload_from_disk = new PageReloadFromDisk(this));
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
p->add_page(p->page_mode = new PageMode(this)); p->add_page(p->page_mode = new PageMode(this));
p->add_page(p->page_firmware = new PageFirmware(this)); p->add_page(p->page_firmware = new PageFirmware(this));
p->add_page(p->page_bed = new PageBedShape(this)); p->add_page(p->page_bed = new PageBedShape(this));

View File

@ -149,6 +149,7 @@ struct PrinterPicker: wxPanel
void select_all(bool select, bool alternates = false); void select_all(bool select, bool alternates = false);
void select_one(size_t i, bool select); void select_one(size_t i, bool select);
bool any_selected() const; bool any_selected() const;
std::set<std::string> get_selected_models() const ;
int get_width() const { return width; } int get_width() const { return width; }
const std::vector<int>& get_button_indexes() { return m_button_indexes; } const std::vector<int>& get_button_indexes() { return m_button_indexes; }
@ -215,6 +216,9 @@ struct PagePrinters: ConfigWizardPage
void select_all(bool select, bool alternates = false); void select_all(bool select, bool alternates = false);
int get_width() const; int get_width() const;
bool any_selected() const; bool any_selected() const;
std::set<std::string> get_selected_models();
std::string get_vendor_id() const { return printer_pickers.empty() ? "" : printer_pickers[0]->vendor_id; }
virtual void set_run_reason(ConfigWizard::RunReason run_reason) override; virtual void set_run_reason(ConfigWizard::RunReason run_reason) override;
}; };
@ -301,6 +305,17 @@ struct PageUpdate: ConfigWizardPage
PageUpdate(ConfigWizard *parent); PageUpdate(ConfigWizard *parent);
}; };
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
struct PageReloadFromDisk : ConfigWizardPage
{
bool full_pathnames;
PageReloadFromDisk(ConfigWizard* parent);
};
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
struct PageMode: ConfigWizardPage struct PageMode: ConfigWizardPage
{ {
wxRadioButton *radio_simple; wxRadioButton *radio_simple;
@ -455,6 +470,11 @@ struct ConfigWizard::priv
PageMaterials *page_sla_materials = nullptr; PageMaterials *page_sla_materials = nullptr;
PageCustom *page_custom = nullptr; PageCustom *page_custom = nullptr;
PageUpdate *page_update = nullptr; PageUpdate *page_update = nullptr;
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
PageReloadFromDisk *page_reload_from_disk = nullptr;
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
PageMode *page_mode = nullptr; PageMode *page_mode = nullptr;
PageVendors *page_vendors = nullptr; PageVendors *page_vendors = nullptr;
Pages3rdparty pages_3rdparty; Pages3rdparty pages_3rdparty;
@ -487,10 +507,17 @@ struct ConfigWizard::priv
void on_custom_setup(); void on_custom_setup();
void on_printer_pick(PagePrinters *page, const PrinterPickerEvent &evt); void on_printer_pick(PagePrinters *page, const PrinterPickerEvent &evt);
void select_default_materials_for_printer_model(const std::vector<VendorProfile::PrinterModel> &models,
Technology technology,
const std::string & model_id);
void select_default_materials_if_needed(VendorProfile* vendor_profile,
Technology technology,
const std::string &model_id);
void selected_default_materials(Technology technology);
void on_3rdparty_install(const VendorProfile *vendor, bool install); void on_3rdparty_install(const VendorProfile *vendor, bool install);
bool on_bnt_finish(); bool on_bnt_finish();
bool check_materials_in_config(Technology technology); bool check_materials_in_config(Technology technology, bool show_info_msg = true);
void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater); void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);
// #ys_FIXME_alise // #ys_FIXME_alise
void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add); void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add);

View File

@ -886,7 +886,7 @@ void GLGizmosManager::do_render_overlay() const
#else #else
float du = (float)(tex_width - 1) / (3.0f * (float)tex_width); // 3 is the number of possible states if the icons float du = (float)(tex_width - 1) / (3.0f * (float)tex_width); // 3 is the number of possible states if the icons
#endif // ENABLE_GIZMO_ICONS_NON_ACTIVABLE_STATE #endif // ENABLE_GIZMO_ICONS_NON_ACTIVABLE_STATE
float dv = (float)(tex_height - 1) / (float)(selectable_idxs.size() * tex_height); float dv = (float)(tex_height - 1) / (float)(m_gizmos.size() * tex_height);
// tiles in the texture are spaced by 1 pixel // tiles in the texture are spaced by 1 pixel
float u_offset = 1.0f / (float)tex_width; float u_offset = 1.0f / (float)tex_width;

View File

@ -631,13 +631,16 @@ bool Mouse3DController::connect_device()
if (m_device != nullptr) if (m_device != nullptr)
{ {
std::vector<wchar_t> manufacturer(1024, 0); wchar_t buffer[1024];
hid_get_manufacturer_string(m_device, manufacturer.data(), 1024); hid_get_manufacturer_string(m_device, buffer, 1024);
m_device_str = boost::nowide::narrow(manufacturer.data()); m_device_str = boost::nowide::narrow(buffer);
// #3479 seems to show that sometimes an extra whitespace is added, so we remove it
boost::algorithm::trim(m_device_str);
std::vector<wchar_t> product(1024, 0); hid_get_product_string(m_device, buffer, 1024);
hid_get_product_string(m_device, product.data(), 1024); m_device_str += "/" + boost::nowide::narrow(buffer);
m_device_str += "/" + boost::nowide::narrow(product.data()); // #3479 seems to show that sometimes an extra whitespace is added, so we remove it
boost::algorithm::trim(m_device_str);
BOOST_LOG_TRIVIAL(info) << "Connected 3DConnexion device:"; BOOST_LOG_TRIVIAL(info) << "Connected 3DConnexion device:";
BOOST_LOG_TRIVIAL(info) << "Manufacturer/product: " << m_device_str; BOOST_LOG_TRIVIAL(info) << "Manufacturer/product: " << m_device_str;

View File

@ -68,7 +68,7 @@ class Mouse3DController
CustomParameters<double> m_translation_params; CustomParameters<double> m_translation_params;
CustomParameters<float> m_rotation_params; CustomParameters<float> m_rotation_params;
#if ENABLE_3DCONNEXION_Y_AS_ZOOM #if ENABLE_3DCONNEXION_Y_AS_ZOOM
CustomParameters<float> m_zoom_params; CustomParameters<double> m_zoom_params;
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM #endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
// When the 3Dconnexion driver is running the system gets, by default, mouse wheel events when rotations around the X axis are detected. // When the 3Dconnexion driver is running the system gets, by default, mouse wheel events when rotations around the X axis are detected.

View File

@ -3022,7 +3022,6 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
this->update_print_volume_state(); this->update_print_volume_state();
// Apply new config to the possibly running background task. // Apply new config to the possibly running background task.
bool was_running = this->background_process.running(); bool was_running = this->background_process.running();
this->background_process.set_force_update_print_regions(force_validation);
Print::ApplyStatus invalidated = this->background_process.apply(this->q->model(), wxGetApp().preset_bundle->full_config()); Print::ApplyStatus invalidated = this->background_process.apply(this->q->model(), wxGetApp().preset_bundle->full_config());
// Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile. // Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile.
@ -4909,7 +4908,12 @@ void Plater::export_amf()
wxBusyCursor wait; wxBusyCursor wait;
bool export_config = true; bool export_config = true;
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure();
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
bool full_pathnames = wxGetApp().app_config->get("export_sources_full_pathnames") == "1";
if (Slic3r::store_amf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames)) {
#else
if (Slic3r::store_amf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr)) { if (Slic3r::store_amf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr)) {
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
// Success // Success
p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path)); p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path));
} else { } else {
@ -4938,6 +4942,16 @@ void Plater::export_3mf(const boost::filesystem::path& output_path)
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure(); DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure();
const std::string path_u8 = into_u8(path); const std::string path_u8 = into_u8(path);
wxBusyCursor wait; wxBusyCursor wait;
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
bool full_pathnames = wxGetApp().app_config->get("export_sources_full_pathnames") == "1";
#if ENABLE_THUMBNAIL_GENERATOR
ThumbnailData thumbnail_data;
p->generate_thumbnail(thumbnail_data, THUMBNAIL_SIZE_3MF.first, THUMBNAIL_SIZE_3MF.second, false, true, true, true);
if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames, &thumbnail_data)) {
#else
if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames)) {
#endif // ENABLE_THUMBNAIL_GENERATOR
#else
#if ENABLE_THUMBNAIL_GENERATOR #if ENABLE_THUMBNAIL_GENERATOR
ThumbnailData thumbnail_data; ThumbnailData thumbnail_data;
p->generate_thumbnail(thumbnail_data, THUMBNAIL_SIZE_3MF.first, THUMBNAIL_SIZE_3MF.second, false, true, true, true); p->generate_thumbnail(thumbnail_data, THUMBNAIL_SIZE_3MF.first, THUMBNAIL_SIZE_3MF.second, false, true, true, true);
@ -4945,6 +4959,7 @@ void Plater::export_3mf(const boost::filesystem::path& output_path)
#else #else
if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr)) { if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr)) {
#endif // ENABLE_THUMBNAIL_GENERATOR #endif // ENABLE_THUMBNAIL_GENERATOR
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
// Success // Success
p->statusbar()->set_status_text(wxString::Format(_(L("3MF file exported to %s")), path)); p->statusbar()->set_status_text(wxString::Format(_(L("3MF file exported to %s")), path));
p->set_project_filename(path); p->set_project_filename(path);

View File

@ -73,6 +73,16 @@ void PreferencesDialog::build()
option = Option (def, "version_check"); option = Option (def, "version_check");
m_optgroup->append_single_option_line(option); m_optgroup->append_single_option_line(option);
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
// Please keep in sync with ConfigWizard
def.label = L("Export sources full pathnames to 3mf and amf");
def.type = coBool;
def.tooltip = L("If enabled, allows the Reload from disk command to automatically find and load the files when invoked.");
def.set_default_value(new ConfigOptionBool(app_config->get("export_sources_full_pathnames") == "1"));
option = Option(def, "export_sources_full_pathnames");
m_optgroup->append_single_option_line(option);
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
// Please keep in sync with ConfigWizard // Please keep in sync with ConfigWizard
def.label = L("Update built-in Presets automatically"); def.label = L("Update built-in Presets automatically");
def.type = coBool; def.type = coBool;

View File

@ -184,6 +184,14 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
} else { } else {
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Malformed variants field: `%2%`") % id % variants_field; BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Malformed variants field: `%2%`") % id % variants_field;
} }
auto default_materials_field = section.second.get<std::string>("default_materials", "");
if (default_materials_field.empty())
default_materials_field = section.second.get<std::string>("default_filaments", "");
if (Slic3r::unescape_strings_cstyle(default_materials_field, model.default_materials)) {
Slic3r::sort_remove_duplicates(model.default_materials);
} else {
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Malformed default_materials field: `%2%`") % id % default_materials_field;
}
model.bed_model = section.second.get<std::string>("bed_model", ""); model.bed_model = section.second.get<std::string>("bed_model", "");
model.bed_texture = section.second.get<std::string>("bed_texture", ""); model.bed_texture = section.second.get<std::string>("bed_texture", "");
if (! model.id.empty() && ! model.variants.empty()) if (! model.id.empty() && ! model.variants.empty())
@ -1502,4 +1510,20 @@ const Preset* PrinterPresetCollection::find_by_model_id(const std::string &model
return it != cend() ? &*it : nullptr; return it != cend() ? &*it : nullptr;
} }
namespace PresetUtils {
const VendorProfile::PrinterModel* system_printer_model(const Preset &preset)
{
const VendorProfile::PrinterModel *out = nullptr;
if (preset.vendor != nullptr) {
auto *printer_model = preset.config.opt<ConfigOptionString>("printer_model");
if (printer_model != nullptr && ! printer_model->value.empty()) {
auto it = std::find_if(preset.vendor->models.begin(), preset.vendor->models.end(), [printer_model](const VendorProfile::PrinterModel &pm) { return pm.id == printer_model->value; });
if (it != preset.vendor->models.end())
out = &(*it);
}
}
return out;
}
} // namespace PresetUtils
} // namespace Slic3r } // namespace Slic3r

View File

@ -61,6 +61,7 @@ public:
PrinterTechnology technology; PrinterTechnology technology;
std::string family; std::string family;
std::vector<PrinterVariant> variants; std::vector<PrinterVariant> variants;
std::vector<std::string> default_materials;
// Vendor & Printer Model specific print bed model & texture. // Vendor & Printer Model specific print bed model & texture.
std::string bed_model; std::string bed_model;
std::string bed_texture; std::string bed_texture;
@ -563,6 +564,11 @@ public:
const Preset* find_by_model_id(const std::string &model_id) const; const Preset* find_by_model_id(const std::string &model_id) const;
}; };
namespace PresetUtils {
// PrinterModel of a system profile, from which this preset is derived, or null if it is not derived from a system profile.
const VendorProfile::PrinterModel* system_printer_model(const Preset &preset);
} // namespace PresetUtils
} // namespace Slic3r } // namespace Slic3r
#endif /* slic3r_Preset_hpp_ */ #endif /* slic3r_Preset_hpp_ */

View File

@ -13,9 +13,7 @@
#include <wx/numformatter.h> #include <wx/numformatter.h>
#include <wx/colordlg.h> #include <wx/colordlg.h>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
#include <boost/nowide/cstdio.hpp>
#include "BitmapCache.hpp" #include "BitmapCache.hpp"
#include "GUI.hpp" #include "GUI.hpp"
@ -426,28 +424,6 @@ static float get_svg_scale_factor(wxWindow *win)
#endif #endif
} }
// in the Dark mode of any platform, we should draw icons in respect to OS background
static std::string icon_name_respected_to_mode(const std::string& bmp_name_in)
{
#ifdef __WXMSW__
const std::string folder = "white\\";
#else
const std::string folder = "white/";
#endif
std::string bmp_name;
if (Slic3r::GUI::wxGetApp().dark_mode()) {
bmp_name = folder + bmp_name_in;
boost::replace_last(bmp_name, ".png", "");
if (! boost::filesystem::exists(Slic3r::var(bmp_name + ".svg")))
bmp_name.clear();
}
if (bmp_name.empty()) {
bmp_name = bmp_name_in;
boost::replace_last(bmp_name, ".png", "");
}
return bmp_name;
}
// If an icon has horizontal orientation (width > height) call this function with is_horizontal = true // If an icon has horizontal orientation (width > height) call this function with is_horizontal = true
wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in, wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in,
const int px_cnt/* = 16*/, const bool is_horizontal /* = false*/, const bool grayscale/* = false*/) const int px_cnt/* = 16*/, const bool is_horizontal /* = false*/, const bool grayscale/* = false*/)
@ -474,13 +450,13 @@ wxBitmap create_scaled_bitmap(wxWindow *win, const std::string& bmp_name_in,
scale_base = (unsigned int)(em_unit(win) * px_cnt * 0.1f + 0.5f); scale_base = (unsigned int)(em_unit(win) * px_cnt * 0.1f + 0.5f);
// std::string bmp_name = bmp_name_in; std::string bmp_name = bmp_name_in;
// boost::replace_last(bmp_name, ".png", ""); boost::replace_last(bmp_name, ".png", "");
std::string bmp_name = icon_name_respected_to_mode(bmp_name_in); // std::string bmp_name = icon_name_respected_to_mode(bmp_name_in);
// Try loading an SVG first, then PNG if SVG is not found: // Try loading an SVG first, then PNG if SVG is not found:
wxBitmap *bmp = cache.load_svg(bmp_name, width, height, scale_factor, grayscale); wxBitmap *bmp = cache.load_svg(bmp_name, width, height, scale_factor, grayscale, Slic3r::GUI::wxGetApp().dark_mode());
if (bmp == nullptr) { if (bmp == nullptr) {
bmp = cache.load_png(bmp_name, width, height, grayscale); bmp = cache.load_png(bmp_name, width, height, grayscale);
} }

View File

@ -363,10 +363,17 @@ void fix_model_by_win10_sdk_gui(ModelObject &model_object, int volume_idx)
ModelObject *model_object = model.add_object(); ModelObject *model_object = model.add_object();
model_object->add_volume(*volumes[ivolume]); model_object->add_volume(*volumes[ivolume]);
model_object->add_instance(); model_object->add_instance();
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
if (!Slic3r::store_3mf(path_src.string().c_str(), &model, nullptr, false)) {
boost::filesystem::remove(path_src);
throw std::runtime_error(L("Export of a temporary 3mf file failed"));
}
#else
if (! Slic3r::store_3mf(path_src.string().c_str(), &model, nullptr)) { if (! Slic3r::store_3mf(path_src.string().c_str(), &model, nullptr)) {
boost::filesystem::remove(path_src); boost::filesystem::remove(path_src);
throw std::runtime_error(L("Export of a temporary 3mf file failed")); throw std::runtime_error(L("Export of a temporary 3mf file failed"));
} }
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
model.clear_objects(); model.clear_objects();
model.clear_materials(); model.clear_materials();
boost::filesystem::path path_dst = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path(); boost::filesystem::path path_dst = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();

View File

@ -51,7 +51,11 @@ SCENARIO("Export+Import geometry to/from 3mf file cycle", "[3mf]") {
WHEN("model is saved+loaded to/from 3mf file") { WHEN("model is saved+loaded to/from 3mf file") {
// save the model to 3mf file // save the model to 3mf file
std::string test_file = std::string(TEST_DATA_DIR) + "/test_3mf/prusa.3mf"; std::string test_file = std::string(TEST_DATA_DIR) + "/test_3mf/prusa.3mf";
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
store_3mf(test_file.c_str(), &src_model, nullptr, false);
#else
store_3mf(test_file.c_str(), &src_model, nullptr); store_3mf(test_file.c_str(), &src_model, nullptr);
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
// load back the model from the 3mf file // load back the model from the 3mf file
Model dst_model; Model dst_model;