diff --git a/resources/ui_layout/default/printer_fff.ui b/resources/ui_layout/default/printer_fff.ui index f7d8c48deae..36a9a8142d9 100644 --- a/resources/ui_layout/default/printer_fff.ui +++ b/resources/ui_layout/default/printer_fff.ui @@ -57,6 +57,7 @@ group:Thumbnails line:Thumbnail options setting:thumbnails_format setting:thumbnails_with_bed + setting:thumbnails_tag_format setting:thumbnails_end_file end_line group:Advanced diff --git a/resources/ui_layout/default/printer_sla.ui b/resources/ui_layout/default/printer_sla.ui index 6116f0e7b8f..a1e4566b090 100644 --- a/resources/ui_layout/default/printer_sla.ui +++ b/resources/ui_layout/default/printer_sla.ui @@ -53,6 +53,7 @@ group:Thumbnails line:Options setting:thumbnails_with_bed setting:thumbnails_with_support + setting:thumbnails_tag_format end_line group:Print Host upload build_printhost diff --git a/resources/ui_layout/example/printer_fff.ui b/resources/ui_layout/example/printer_fff.ui index 5c016f12231..497cc5659dd 100644 --- a/resources/ui_layout/example/printer_fff.ui +++ b/resources/ui_layout/example/printer_fff.ui @@ -56,6 +56,7 @@ group:Thumbnails line:Thumbnail options setting:thumbnails_format setting:thumbnails_with_bed + setting:thumbnails_tag_format setting:thumbnails_end_file end_line group:Advanced diff --git a/resources/ui_layout/example/printer_sla.ui b/resources/ui_layout/example/printer_sla.ui index 0619cdc1067..6c4b5814fe7 100644 --- a/resources/ui_layout/example/printer_sla.ui +++ b/resources/ui_layout/example/printer_sla.ui @@ -52,6 +52,7 @@ group:Thumbnails line:Options setting:thumbnails_with_bed setting:thumbnails_with_support + setting:thumbnails_tag_format end_line group:Print Host upload build_printhost diff --git a/src/PrusaSlicer.cpp b/src/PrusaSlicer.cpp index 7eedfd923dd..129baabd8fb 100644 --- a/src/PrusaSlicer.cpp +++ b/src/PrusaSlicer.cpp @@ -237,8 +237,8 @@ int CLI::run(int argc, char **argv) // The default bed shape should reflect the default display parameters // and not the fff defaults. - double w = sla_print_config.display_width.get_float(); - double h = sla_print_config.display_height.get_float(); + double w = sla_print_config.display_width.get_float(); + double h = sla_print_config.display_height.get_float(); sla_print_config.bed_shape.values = { Vec2d(0, 0), Vec2d(w, 0), Vec2d(w, h), Vec2d(0, h) }; sla_print_config.apply(m_print_config, true); diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 9dde9b176ef..2d339c0a104 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -59,8 +59,124 @@ bool export_clipper_input_polygons_bin(const char *path, const ClipperLib::Paths namespace ClipperUtils { Points EmptyPathsProvider::s_empty_points; Points SinglePathProvider::s_end; + + + // Clip source polygon to be used as a clipping polygon with a bouding box around the source (to be clipped) + // polygon. Useful as an optimization for expensive ClipperLib operations, for example when clipping source + // polygons one by one with a set of polygons covering the whole layer below. + template + inline void clip_clipper_polygon_with_subject_bbox_templ(const PointsType & src, + const BoundingBox &bbox, + PointsType & out) + { + using PointType = typename PointsType::value_type; + out.clear(); + const size_t cnt = src.size(); + if (cnt < 3) + return; + enum class Side { Left = 1, Right = 2, Top = 4, Bottom = 8 }; + auto sides = [bbox](const PointType &p) { + return int(p.x() < bbox.min.x()) * int(Side::Left) + int(p.x() > bbox.max.x()) * int(Side::Right) + + int(p.y() < bbox.min.y()) * int(Side::Bottom) + int(p.y() > bbox.max.y()) * int(Side::Top); + }; + int sides_prev = sides(src.back()); + int sides_this = sides(src.front()); + const size_t last = cnt - 1; + for (size_t i = 0; i < last; ++i) { + int sides_next = sides(src[i + 1]); + if ( // This point is inside. Take it. + sides_this == 0 || + // Either this point is outside and previous or next is inside, or + // the edge possibly cuts corner of the bounding box. + (sides_prev & sides_this & sides_next) == 0) { + out.emplace_back(src[i]); + sides_prev = sides_this; + } else { + // All the three points (this, prev, next) are outside at the same side. + // Ignore this point. + } + sides_this = sides_next; + } + // Never produce just a single point output polygon. + if (!out.empty()) + if (int sides_next = sides(out.front()); + // The last point is inside. Take it. + sides_this == 0 || + // Either this point is outside and previous or next is inside, or + // the edge possibly cuts corner of the bounding box. + (sides_prev & sides_this & sides_next) == 0) + out.emplace_back(src.back()); + } + void clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox, Points &out) + { + clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out); + } + void clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox, ZPoints &out) + { + clip_clipper_polygon_with_subject_bbox_templ(src, bbox, out); + } + template + [[nodiscard]] PointsType clip_clipper_polygon_with_subject_bbox_templ(const PointsType & src, + const BoundingBox &bbox) + { + PointsType out; + clip_clipper_polygon_with_subject_bbox(src, bbox, out); + return out; + } + [[nodiscard]] Points clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox) + { + return clip_clipper_polygon_with_subject_bbox_templ(src, bbox); + } + [[nodiscard]] ZPoints clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox) + { + return clip_clipper_polygon_with_subject_bbox_templ(src, bbox); + } + void clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox, Polygon &out) + { + clip_clipper_polygon_with_subject_bbox(src.points, bbox, out.points); + } + [[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox) + { + Polygon out; + clip_clipper_polygon_with_subject_bbox(src.points, bbox, out.points); + return out; + } + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const Polygons &src, const BoundingBox &bbox) + { + Polygons out; + out.reserve(src.size()); + for (const Polygon &p : src) out.emplace_back(clip_clipper_polygon_with_subject_bbox(p, bbox)); + out.erase(std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) { return polygon.empty(); }), + out.end()); + return out; + } + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox) + { + Polygons out; + out.reserve(src.num_contours()); + out.emplace_back(clip_clipper_polygon_with_subject_bbox(src.contour, bbox)); + for (const Polygon &p : src.holes) out.emplace_back(clip_clipper_polygon_with_subject_bbox(p, bbox)); + out.erase(std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) { return polygon.empty(); }), + out.end()); + return out; + } + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons &src, const BoundingBox &bbox) + { + Polygons out; + out.reserve(number_polygons(src)); + for (const ExPolygon &p : src) { + Polygons temp = clip_clipper_polygons_with_subject_bbox(p, bbox); + out.insert(out.end(), temp.begin(), temp.end()); + } + + out.erase(std::remove_if(out.begin(), out.end(), [](const Polygon &polygon) { return polygon.empty(); }), + out.end()); + return out; + } } + + static ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree &&polytree) { struct Inner { diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index 35109821c6b..55a566851f5 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -297,7 +297,23 @@ namespace ClipperUtils { const SurfacesPtr &m_surfaces; size_t m_size; }; -} + + using ZPoint = Vec3i32; + using ZPoints = std::vector; + + // Clip source polygon to be used as a clipping polygon with a bouding box around the source (to be clipped) polygon. + // Useful as an optimization for expensive ClipperLib operations, for example when clipping source polygons one by + // one with a set of polygons covering the whole layer below. + void clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox, Points &out); + void clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox, ZPoints &out); + [[nodiscard]] Points clip_clipper_polygon_with_subject_bbox(const Points &src, const BoundingBox &bbox); + [[nodiscard]] ZPoints clip_clipper_polygon_with_subject_bbox(const ZPoints &src, const BoundingBox &bbox); + void clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox, Polygon &out); + [[nodiscard]] Polygon clip_clipper_polygon_with_subject_bbox(const Polygon &src, const BoundingBox &bbox); + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const Polygons &src, const BoundingBox &bbox); + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygon &src, const BoundingBox &bbox); + [[nodiscard]] Polygons clip_clipper_polygons_with_subject_bbox(const ExPolygons &src, const BoundingBox &bbox); + } // Perform union of input polygons using the non-zero rule, convert to ExPolygons. ExPolygons ClipperPaths_to_Slic3rExPolygons(const ClipperLib::Paths &input, bool do_union = false); diff --git a/src/libslic3r/Config.cpp b/src/libslic3r/Config.cpp index 19ed3c2001c..ea0d1216448 100644 --- a/src/libslic3r/Config.cpp +++ b/src/libslic3r/Config.cpp @@ -781,8 +781,8 @@ double ConfigBase::get_computed_value(const t_config_option_key &opt_key, int ex if (extruder_id < 0) { const ConfigOption* opt_extruder_id = nullptr; if ((opt_extruder_id = this->option("extruder")) == nullptr) - if ((opt_extruder_id = this->option("current_extruder")) == nullptr || - opt_extruder_id->get_int() < 0 || opt_extruder_id->get_int() >= vector_opt->size()) { + if ((opt_extruder_id = this->option("current_extruder")) == nullptr + || opt_extruder_id->get_int() < 0 || opt_extruder_id->get_int() >= vector_opt->size()) { std::stringstream ss; ss << "ConfigBase::get_abs_value(): " << opt_key << " need to has the extuder id to get the right value, but it's not available"; throw ConfigurationError(ss.str()); } diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index 3eb8a474347..e0688b7f850 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -396,12 +396,12 @@ class ConfigOption { virtual ConfigOption* clone() const = 0; // Set a value from a ConfigOption. The two options should be compatible. virtual void set(const ConfigOption *option) = 0; - virtual int32_t get_int(int idx = 0) const { throw BadOptionTypeException("Calling ConfigOption::get_int on a non-int ConfigOption"); } - virtual double get_float(int idx = 0) const { throw BadOptionTypeException("Calling ConfigOption::get_float on a non-float ConfigOption"); } - virtual bool get_bool(int idx = 0) const { throw BadOptionTypeException("Calling ConfigOption::get_bool on a non-boolean ConfigOption"); } + virtual int32_t get_int(size_t idx = 0) const { throw BadOptionTypeException("Calling ConfigOption::get_int on a non-int ConfigOption"); } + virtual double get_float(size_t idx = 0) const { throw BadOptionTypeException("Calling ConfigOption::get_float on a non-float ConfigOption"); } + virtual bool get_bool(size_t idx = 0) const { throw BadOptionTypeException("Calling ConfigOption::get_bool on a non-boolean ConfigOption"); } virtual void set_enum_int(int32_t /* val */) { throw BadOptionTypeException("Calling ConfigOption::set_enum_int on a non-enum ConfigOption"); } - virtual boost::any get_any(int idx = 0) const { throw BadOptionTypeException("Calling ConfigOption::get_any on a raw ConfigOption"); } - virtual void set_any(boost::any, int idx = -1) { throw BadOptionTypeException("Calling ConfigOption::set_any on a raw ConfigOption"); } + virtual boost::any get_any(int32_t idx = -1) const { throw BadOptionTypeException("Calling ConfigOption::get_any on a raw ConfigOption"); } + virtual void set_any(boost::any, int32_t idx = -1) { throw BadOptionTypeException("Calling ConfigOption::set_any on a raw ConfigOption"); } virtual bool operator==(const ConfigOption &rhs) const = 0; bool operator!=(const ConfigOption &rhs) const { return ! (*this == rhs); } virtual size_t hash() const throw() = 0; @@ -410,7 +410,7 @@ class ConfigOption { // If this option is nullable, then it may have its value or values set to nil. virtual bool nullable() const { return false; } // A scalar is nil, or all values of a vector are nil if idx < 0. - virtual bool is_nil(int idx = -1) const { return false; } + virtual bool is_nil(int32_t idx = -1) const { return false; } bool is_phony() const { return (flags & FCO_PHONY) != 0; } void set_phony(bool phony) { if (phony) this->flags |= FCO_PHONY; else this->flags &= uint8_t(0xFF ^ FCO_PHONY); } // Is this option overridden by another option? @@ -442,8 +442,8 @@ class ConfigOptionSingle : public ConfigOption { explicit ConfigOptionSingle(T value) : value(value) {} explicit ConfigOptionSingle(T value, bool phony) : ConfigOption(phony), value(value) {} operator T() const { return this->value; } - boost::any get_any(int idx = 0) const override { return boost::any(value); } - void set_any(boost::any anyval, int idx = -1) override { value = boost::any_cast(anyval); } + boost::any get_any(int32_t idx = -1) const override { return boost::any(value); } + void set_any(boost::any anyval, int32_t idx = -1) override { value = boost::any_cast(anyval); } void set(const ConfigOption *rhs) override { @@ -517,7 +517,8 @@ class ConfigOptionVectorBase : public ConfigOption { }; // Value of a vector valued option (bools, ints, floats, strings, points), template -template class ConfigOptionVector : public ConfigOptionVectorBase +template +class ConfigOptionVector : public ConfigOptionVectorBase { private: void set_default_from_values() { @@ -607,8 +608,8 @@ template class ConfigOptionVector : public ConfigOptionVectorBase } T& get_at(size_t i) { return const_cast(std::as_const(*this).get_at(i)); } - boost::any get_any(int idx) const override { return idx < 0 ? boost::any(values) : boost::any(get_at(idx)); } - void set_any(boost::any anyval, int idx = -1) override + boost::any get_any(int32_t idx = -1) const override { return idx < 0 ? boost::any(values) : boost::any(get_at(idx)); } + void set_any(boost::any anyval, int32_t idx = -1) override { if (idx < 0) values = boost::any_cast>(anyval); @@ -739,7 +740,7 @@ class ConfigOptionFloat : public ConfigOptionSingle static ConfigOptionType static_type() { return coFloat; } ConfigOptionType type() const override { return static_type(); } - double get_float(int idx = 0) const override { return this->value; } + double get_float(size_t idx = 0) const override { return this->value; } ConfigOption* clone() const override { return new ConfigOptionFloat(*this); } bool operator==(const ConfigOptionFloat &rhs) const throw() { return this->value == rhs.value; } bool operator< (const ConfigOptionFloat &rhs) const throw() { return this->value < rhs.value; } @@ -794,9 +795,8 @@ class ConfigOptionFloatsTempl : public ConfigOptionVector } // Could a special "nil" value be stored inside the vector, indicating undefined value? bool nullable() const override { return NULLABLE; } - double get_float(int idx = 0) const override { return get_at(idx); } - - bool is_nil(int idx = 0) const override + // A scalar is nil, or all values of a vector are nil. + bool is_nil(int32_t idx = -1) const override { if (idx < 0) { for (double v : this->values) @@ -809,16 +809,13 @@ class ConfigOptionFloatsTempl : public ConfigOptionVector (std::isnan(this->values.front()) || NIL_VALUE() == this->values.front()); } } + double get_float(size_t idx = 0) const override { return get_at(idx); } - static inline bool is_nil(const boost::any &to_check) - { + static inline bool is_nil(const boost::any &to_check) { return std::isnan(boost::any_cast(to_check)) || boost::any_cast(to_check) == NIL_VALUE(); } // don't use it to compare, use is_nil() to check. - static inline boost::any create_nil() - { - return boost::any(NIL_VALUE()); - } + static inline boost::any create_nil() { return boost::any(NIL_VALUE()); } std::string serialize() const override { @@ -874,9 +871,8 @@ class ConfigOptionFloatsTempl : public ConfigOptionVector protected: // Special "nil" value to be stored into the vector if this->supports_nil(). - // try to not use nan. It's a weird value, and other types don't use it. - static double NIL_VALUE() { return std::numeric_limits::quiet_NaN(); } // std::numeric_limits::max(); } - + //please use is_nil & create_nil, to better support nan + static double NIL_VALUE() { return std::numeric_limits::quiet_NaN(); } void serialize_single_value(std::ostringstream &ss, const double v) const { if (std::isfinite(v)) ss << v; @@ -931,8 +927,8 @@ class ConfigOptionInt : public ConfigOptionSingle static ConfigOptionType static_type() { return coInt; } ConfigOptionType type() const override { return static_type(); } - int32_t get_int(int idx = 0) const override { return this->value; } - double get_float(int idx = 0) const override { return this->value; } + int32_t get_int(size_t idx = 0) const override { return this->value; } + double get_float(size_t idx = 0) const override { return this->value; } ConfigOption* clone() const override { return new ConfigOptionInt(*this); } bool operator==(const ConfigOptionInt &rhs) const throw() { return this->value == rhs.value; } @@ -983,10 +979,8 @@ class ConfigOptionIntsTempl : public ConfigOptionVector bool nullable() const override { return NULLABLE; } // Special "nil" value to be stored into the vector if this->supports_nil(). static int32_t NIL_VALUE() { return std::numeric_limits::max(); } - int32_t get_int(int idx= 0) const override { return get_at(idx); } - double get_float(int idx = 0) const override { return get_at(idx); } - - bool is_nil(int idx = 0) const override + // A scalar is nil, or all values of a vector are nil. + bool is_nil(int32_t idx = -1) const override { if (idx < 0) { for (int32_t v : this->values) @@ -999,6 +993,8 @@ class ConfigOptionIntsTempl : public ConfigOptionVector NIL_VALUE() == this->values.front(); } } + int32_t get_int(size_t idx = 0) const override { return get_at(idx); } + double get_float(size_t idx = 0) const override { return get_at(idx); } std::string serialize() const override { @@ -1111,7 +1107,7 @@ class ConfigOptionStrings : public ConfigOptionVector ConfigOptionStrings& operator=(const ConfigOption *opt) { this->set(opt); return *this; } bool operator==(const ConfigOptionStrings &rhs) const throw() { return this->values == rhs.values; } bool operator< (const ConfigOptionStrings &rhs) const throw() { return this->values < rhs.values; } - bool is_nil(int) const override { return false; } + bool is_nil(int32_t idx = 0) const override { return false; } std::string serialize() const override { @@ -1200,7 +1196,7 @@ class ConfigOptionPercentsTempl : public ConfigOptionFloatsTempl if (&v != &this->values.front()) ss << ","; this->serialize_single_value(ss, v); - if (!(std::isnan(v) || v == ConfigOptionFloatsTempl::NIL_VALUE())) + if (! (std::isnan(v) || v == ConfigOptionFloatsTempl::NIL_VALUE())) ss << "%"; } std::string str = ss.str(); @@ -1214,7 +1210,7 @@ class ConfigOptionPercentsTempl : public ConfigOptionFloatsTempl for (const double v : this->values) { std::ostringstream ss; this->serialize_single_value(ss, v); - if (!(std::isnan(v) || v == ConfigOptionFloatsTempl::NIL_VALUE())) + if (! (std::isnan(v) || v == ConfigOptionFloatsTempl::NIL_VALUE())) ss << "%"; vv.push_back(ss.str()); } @@ -1248,30 +1244,22 @@ class ConfigOptionFloatOrPercent : public ConfigOptionPercent { if (rhs.type() != this->type()) throw ConfigurationError("ConfigOptionFloatOrPercent: Comparing incompatible types"); - assert(dynamic_cast(&rhs)); - return *this == *static_cast(&rhs); + assert(dynamic_cast(&rhs)); + return *this == *static_cast(&rhs); } bool operator==(const ConfigOptionFloatOrPercent &rhs) const throw() - { - return this->value == rhs.value && this->percent == rhs.percent; - } - size_t hash() const throw() override - { - size_t seed = std::hash{}(this->value); - return this->percent ? seed ^ 0x9e3779b9 : seed; - } - bool operator<(const ConfigOptionFloatOrPercent &rhs) const throw() - { - return this->value < rhs.value || (this->value == rhs.value && int(this->percent) < int(rhs.percent)); - } - - double get_abs_value(double ratio_over) const - { - return this->percent ? (ratio_over * this->value / 100) : this->value; - } + { return this->value == rhs.value && this->percent == rhs.percent; } + size_t hash() const throw() override + { size_t seed = std::hash{}(this->value); return this->percent ? seed ^ 0x9e3779b9 : seed; } + bool operator< (const ConfigOptionFloatOrPercent &rhs) const throw() + { return this->value < rhs.value || (this->value == rhs.value && int(this->percent) < int(rhs.percent)); } + + double get_abs_value(double ratio_over) const + { return this->percent ? (ratio_over * this->value / 100) : this->value; } + double get_float(size_t idx = 0) const override { return get_abs_value(1.); } // special case for get/set any: use a FloatOrPercent like for FloatsOrPercents, to have the is_percent - boost::any get_any(int idx = 0) const override { return boost::any(FloatOrPercent{value, percent}); } - void set_any(boost::any anyval, int idx = -1) override + boost::any get_any(int32_t idx = 0) const override { return boost::any(FloatOrPercent{value, percent}); } + void set_any(boost::any anyval, int32_t idx = -1) override { auto fl_or_per = boost::any_cast(anyval); this->value = fl_or_per.value; @@ -1333,28 +1321,33 @@ class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVectorsupports_nil(). - static FloatOrPercent NIL_VALUE() { return FloatOrPercent{ std::numeric_limits::max(), false }; } - double get_abs_value(size_t i, double ratio_over) const { - if (this->is_nil(i)) return 0; - const FloatOrPercent& data = this->get_at(i); - if (data.percent) return ratio_over * data.value / 100; - return data.value; - } - - bool is_nil(int idx = 0) const override + // A scalar is nil, or all values of a vector are nil. + bool is_nil(int32_t idx = -1) const override { if (idx < 0) { for (const FloatOrPercent &v : this->values) - if (v != NIL_VALUE()) + if (! std::isnan(v.value) || v != NIL_VALUE()) return false; return true; } else { - return idx < values.size() ? NIL_VALUE() == this->values[idx] : - values.empty() ? NIL_VALUE() == this->default_value : - NIL_VALUE() == this->values.front(); + return idx < values.size() ? (std::isnan(this->values[idx].value) || NIL_VALUE() == this->values[idx]) : + values.empty() ? (std::isnan(this->default_value.value) || NIL_VALUE() == this->default_value) : + (std::isnan(this->values.front().value) || NIL_VALUE() == this->values.front()); } } + double get_abs_value(size_t i, double ratio_over) const { + if (this->is_nil(i)) return 0; + const FloatOrPercent& data = this->get_at(i); + if (data.percent) return ratio_over * data.value / 100; + return data.value; + } + double get_float(size_t idx = 0) const override { return get_abs_value(idx, 1.); } + + static inline bool is_nil(const boost::any &to_check) { + return std::isnan(boost::any_cast(to_check).value) || boost::any_cast(to_check).value == NIL_VALUE().value; + } + // don't use it to compare, use is_nil() to check. + static inline boost::any create_nil() { return boost::any(NIL_VALUE()); } std::string serialize() const override { @@ -1410,6 +1403,9 @@ class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVectorsupports_nil(). + static FloatOrPercent NIL_VALUE() { return FloatOrPercent{ std::numeric_limits::max(), false }; } + void serialize_single_value(std::ostringstream &ss, const FloatOrPercent &v) const { if (std::isfinite(v.value)) { ss << v.value; @@ -1523,7 +1519,7 @@ class ConfigOptionPoints : public ConfigOptionVector bool operator==(const ConfigOptionPoints &rhs) const throw() { return this->values == rhs.values; } bool operator< (const ConfigOptionPoints &rhs) const throw() { return std::lexicographical_compare(this->values.begin(), this->values.end(), rhs.values.begin(), rhs.values.end(), [](const auto &l, const auto &r){ return l < r; }); } - bool is_nil(int) const override { return false; } + bool is_nil(int32_t idx = 0) const override { return false; } std::string serialize() const override { @@ -1632,9 +1628,9 @@ class ConfigOptionBool : public ConfigOptionSingle static ConfigOptionType static_type() { return coBool; } ConfigOptionType type() const override { return static_type(); } - bool get_bool(int idx = 0) const override { return this->value; } - int32_t get_int(int idx = 0) const override { return this->value?1:0; } - double get_float(int idx = 0) const override { return this->value?1.:0.; } + bool get_bool(size_t idx = 0) const override { return this->value; } + int32_t get_int(size_t idx = 0) const override { return this->value ? 1 : 0; } + double get_float(size_t idx = 0) const override { return this->value ? 1. : 0.; } ConfigOption* clone() const override { return new ConfigOptionBool(*this); } ConfigOptionBool& operator=(const ConfigOption *opt) { this->set(opt); return *this; } bool operator==(const ConfigOptionBool &rhs) const throw() { return this->value == rhs.value; } @@ -1686,27 +1682,8 @@ class ConfigOptionBoolsTempl : public ConfigOptionVector bool nullable() const override { return NULLABLE; } // Special "nil" value to be stored into the vector if this->supports_nil(). static unsigned char NIL_VALUE() { return std::numeric_limits::max(); } - bool get_bool(int idx = 0) const override { return ConfigOptionVector::get_at(idx) != 0; } - int32_t get_int(int idx = 0) const override { return ConfigOptionVector::get_at(idx) != 0 ? 1 : 0; } - double get_float(int idx = 0) const override { return ConfigOptionVector::get_at(idx) != 0 ? 1. : 0.; } - - // I commented it, it shouldn't do anything wrong, as if(uint) == if(uint!=0) - //bool& get_at(size_t i) { - // assert(! this->values.empty()); - // if (this->values.empty()) { - // values.push_back(default_value); - // } - // return *reinterpret_cast(&((i < this->values.size()) ? this->values[i] : this->values.front())); - //} - - //FIXME this smells, the parent class has the method declared returning (unsigned char&). - // I commented it, it shouldn't do anything wrong, as if(uint) == if(uint!=0) - // Please use get_bool(i) - //bool get_at(size_t i) const { - // return ((i < this->values.size()) ? this->values[i] : (this->values.empty() ? default_value != 0 : this->values.front() != 0)); - //} - - bool is_nil(int idx = 0) const override + // A scalar is nil, or all values of a vector are nil. + bool is_nil(int32_t idx = -1) const override { if (idx < 0) { for (uint8_t v : this->values) @@ -1719,6 +1696,9 @@ class ConfigOptionBoolsTempl : public ConfigOptionVector NIL_VALUE() == this->values.front(); } } + bool get_bool(size_t idx = 0) const override { return ConfigOptionVector::get_at(idx) != 0; } + int32_t get_int(size_t idx = 0) const override { return ConfigOptionVector::get_at(idx) != 0 ? 1 : 0; } + double get_float(size_t idx = 0) const override { return ConfigOptionVector::get_at(idx) != 0 ? 1. : 0.; } std::string serialize() const override { @@ -1814,11 +1794,11 @@ class ConfigOptionEnum : public ConfigOptionSingle ConfigOptionEnum& operator=(const ConfigOption *opt) { this->set(opt); return *this; } bool operator==(const ConfigOptionEnum &rhs) const throw() { return this->value == rhs.value; } bool operator< (const ConfigOptionEnum &rhs) const throw() { return int(this->value) < int(rhs.value); } - int32_t get_int(int idx = 0) const override { return (int32_t)this->value; } + int32_t get_int(size_t idx = 0) const override { return int32_t(this->value); } void set_enum_int(int32_t val) override { this->value = T(val); } // special case for get/set any: use a int like for ConfigOptionEnumGeneric, to simplify - boost::any get_any(int idx = 0) const override { return boost::any(get_int(idx)); } - void set_any(boost::any anyval, int idx = -1) override { set_enum_int(boost::any_cast(anyval)); } + boost::any get_any(int32_t idx = -1) const override { return boost::any(get_int()); } + void set_any(boost::any anyval, int32_t idx = -1) override { set_enum_int(boost::any_cast(anyval)); } bool operator==(const ConfigOption &rhs) const override { @@ -2581,5 +2561,4 @@ class StaticConfig : public virtual ConfigBase } - #endif diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index a6fff4aae49..08daf3c2222 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -211,7 +211,7 @@ void Fill::fill_surface_extrusion(const Surface *surface, const FillParams ¶ good_role, used_flow, used_flow.scaled_width() / 8, - true); + !params.monotonic); // compute the path of the nozzle -> extruded volume for (const ExtrusionEntity* entity : entities) { extruded_volume += entity->total_volume(); @@ -219,6 +219,8 @@ void Fill::fill_surface_extrusion(const Surface *surface, const FillParams ¶ //append (move so the pointers are reused, and won't need to be deleted) all_new_paths->append(std::move(entities)); } + if(params.monotonic) + all_new_paths->set_can_sort_reverse(false, false); thick_polylines.clear(); @@ -305,7 +307,8 @@ void Fill::fill_surface_extrusion(const Surface *surface, const FillParams ¶ good_role, params.flow.mm3_per_mm()* params.flow_mult * mult_flow, (float)(params.flow.width()* params.flow_mult * mult_flow), - (float)params.flow.height()); + (float)params.flow.height(), + !params.monotonic); } } catch (InfillFailedException&) { } diff --git a/src/libslic3r/Fill/FillRectilinear.cpp b/src/libslic3r/Fill/FillRectilinear.cpp index 74e74a70fe5..b171db95122 100644 --- a/src/libslic3r/Fill/FillRectilinear.cpp +++ b/src/libslic3r/Fill/FillRectilinear.cpp @@ -3532,7 +3532,8 @@ FillRectilinearWGapFill::fill_surface_extrusion(const Surface *surface, const Fi good_role, params.flow.mm3_per_mm() * params.flow_mult * flow_mult_exact_volume, params.flow.width() * params.flow_mult * float(flow_mult_exact_volume), - params.flow.height()); + params.flow.height(), + !is_monotonic()); coll_nosort->append(ExtrusionEntitiesPtr{ eec }); diff --git a/src/libslic3r/Fill/FillRectilinear.hpp b/src/libslic3r/Fill/FillRectilinear.hpp index d9151dab8b0..dae36dc6ca7 100644 --- a/src/libslic3r/Fill/FillRectilinear.hpp +++ b/src/libslic3r/Fill/FillRectilinear.hpp @@ -50,6 +50,12 @@ class FillMonotonic : public FillRectilinear public: Fill* clone() const override { return new FillMonotonic(*this); } ~FillMonotonic() override = default; + //apply monotonic + void fill_surface_extrusion(const Surface *surface, const FillParams ¶ms, ExtrusionEntitiesPtr &out) const override { + FillParams monotonic_params = params; + monotonic_params.monotonic = true; + FillRectilinear::fill_surface_extrusion(surface, monotonic_params, out); + } Polylines fill_surface(const Surface* surface, const FillParams& params) const override; bool no_sort() const override { return true; } }; diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index dd3fdefcedd..ebe23a0dce7 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1377,13 +1377,15 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene const ConfigOptionBool* thumbnails_end_file = print.full_print_config().option("thumbnails_end_file"); if(!thumbnails_end_file || !thumbnails_end_file->value) { const ConfigOptionBool* thumbnails_with_bed = print.full_print_config().option("thumbnails_with_bed"); + const ConfigOptionBool* thumbnails_tag_with_format = print.full_print_config().option("thumbnails_tag_format"); const ConfigOptionEnum* thumbnails_format = print.full_print_config().option>("thumbnails_format"); // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". // If "thumbnails_format" is not defined, export to PNG. GCodeThumbnails::export_thumbnails_to_file(thumbnail_cb, print.full_print_config().option("thumbnails")->values, - thumbnails_with_bed == nullptr ? false : thumbnails_with_bed->value, + thumbnails_with_bed ? thumbnails_with_bed->value : false, thumbnails_format ? thumbnails_format->value : GCodeThumbnailsFormat::PNG, + thumbnails_tag_with_format ? thumbnails_tag_with_format->value : false, [&file](const char* sz) { file.write(sz); }, [&print]() { print.throw_if_canceled(); }); } @@ -2085,13 +2087,15 @@ void GCode::_do_export(Print& print_mod, GCodeOutputStream &file, ThumbnailsGene //print thumbnails at the end instead of the start if requested if (thumbnails_end_file && thumbnails_end_file->value) { const ConfigOptionBool* thumbnails_with_bed = print.full_print_config().option("thumbnails_with_bed"); + const ConfigOptionBool* thumbnails_tag_with_format = print.full_print_config().option("thumbnails_tag_format"); const ConfigOptionEnum* thumbnails_format = print.full_print_config().option>("thumbnails_format"); // Unit tests or command line slicing may not define "thumbnails" or "thumbnails_format". // If "thumbnails_format" is not defined, export to PNG. GCodeThumbnails::export_thumbnails_to_file(thumbnail_cb, print.full_print_config().option("thumbnails")->values, - thumbnails_with_bed==nullptr? false:thumbnails_with_bed->value, + thumbnails_with_bed ? thumbnails_with_bed->value : false, thumbnails_format ? thumbnails_format->value : GCodeThumbnailsFormat::PNG, + thumbnails_tag_with_format ? thumbnails_tag_with_format->value: false, [&file](const char* sz) { file.write(sz); }, [&print]() { print.throw_if_canceled(); }); } @@ -5436,10 +5440,10 @@ double_t GCode::_compute_speed_mm_per_sec(const ExtrusionPath& path, double spee void GCode::cooldown_marker_init() { - if (!_cooldown_marker_speed[ExtrusionRole::erExternalPerimeter].empty()) { - std::string allow_speed_change = ";CM_extrude_speed;_EXTRUDE_SET_SPEED"; + if (_cooldown_marker_speed[ExtrusionRole::erExternalPerimeter].empty()) { + std::string allow_speed_change = ";_EXTRUDE_SET_SPEED"; //only change speed on external perimeter (and similar) speed if really necessary. - std::string maybe_allow_speed_change = ";CM_extrude_speed_external;_EXTRUDE_SET_SPEED_MAYBE"; + std::string maybe_allow_speed_change = ";_EXTRUDE_SET_SPEED_MAYBE"; _cooldown_marker_speed[erNone] = ""; _cooldown_marker_speed[erPerimeter] = allow_speed_change; _cooldown_marker_speed[erExternalPerimeter] = maybe_allow_speed_change; diff --git a/src/libslic3r/GCode/CoolingBuffer.cpp b/src/libslic3r/GCode/CoolingBuffer.cpp index 07d4e02c467..c19f3d60b79 100644 --- a/src/libslic3r/GCode/CoolingBuffer.cpp +++ b/src/libslic3r/GCode/CoolingBuffer.cpp @@ -45,6 +45,7 @@ void CoolingBuffer::reset(const Vec3d &position) struct CoolingLine { enum Type : uint32_t { + TYPE_NONE = 0, //first 5 bits are for the extrusiontype (not a flag) TYPE_SET_TOOL = 1 << 7, @@ -401,6 +402,28 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: // Index of an existing CoolingLine of the current adjustment, which holds the feedrate setting command // for a sequence of extrusion moves. size_t active_speed_modifier = size_t(-1); + // type to add to each next G1 (just for adjustable for now) + size_t current_stamp = CoolingLine::TYPE_NONE; + + auto finalize_sm = [&](bool ignore_empty) { + if (active_speed_modifier != size_t(-1)) { + assert(active_speed_modifier < adjustment->lines.size()); + CoolingLine &sm = adjustment->lines[active_speed_modifier]; + // There should be at least some extrusion move inside the adjustment block. + // However if the block has no extrusion (which is wrong), fix it for the cooling buffer to work. + //FIXME: Pressure equalizer add EXTRUDE_SET_SPEED_TAG withotu removing the previous one at the line before. + assert(ignore_empty || sm.length > 0); + assert(ignore_empty || sm.time > 0); + if (sm.time <= 0) { + // Likely a zero length extrusion, it should not be emitted, however the zero extrusions should + // not confuse firmware either. Prohibit time adjustment of a block of zero length extrusions by + // the cooling buffer. + sm.type &= ~CoolingLine::TYPE_ADJUSTABLE; + // But the start / end comment shall be removed. + sm.type |= CoolingLine::TYPE_ADJUSTABLE_EMPTY; + } + } + }; for (; *line_start != 0; line_start = line_end) { @@ -459,11 +482,15 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: bool wipe = boost::contains(sline, ";_WIPE"); if (wipe) line.type |= CoolingLine::TYPE_WIPE; - if (boost::contains(sline, ";_EXTRUDE_SET_SPEED") && ! wipe) { - line.type |= CoolingLine::TYPE_ADJUSTABLE; + if (boost::contains(sline, ";_EXTRUDE_SET_SPEED") && !wipe) { + finalize_sm(true); active_speed_modifier = adjustment->lines.size(); - if (boost::contains(sline, ";_EXTRUDE_SET_SPEED_MAYBE")) + line.type |= CoolingLine::TYPE_ADJUSTABLE; + current_stamp |= CoolingLine::TYPE_ADJUSTABLE; + if (boost::contains(sline, ";_EXTRUDE_SET_SPEED_MAYBE")) { line.type |= CoolingLine::TYPE_ADJUSTABLE_MAYBE; + current_stamp |= CoolingLine::TYPE_ADJUSTABLE_MAYBE; + } } if ((line.type & CoolingLine::TYPE_G92) == 0) { // G0 or G1. Calculate the duration. @@ -494,6 +521,12 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: line.length = std::abs(dif[3]); } line.feedrate = new_pos[4]; + if (line.feedrate > 0.f && line.length > 0.f) { + assert((line.type & CoolingLine::TYPE_ADJUSTABLE) == 0); + // there can be no active_speed_modifier in custom gcode. + assert(active_speed_modifier != size_t(-1) || current_stamp == CoolingLine::TYPE_NONE); + line.type |= current_stamp; + } assert((line.type & CoolingLine::TYPE_ADJUSTABLE) == 0 || line.feedrate > 0.f); if (line.length > 0) { assert(line.feedrate > 0); @@ -530,22 +563,9 @@ std::vector CoolingBuffer::parse_layer_gcode(const std:: } else if (boost::starts_with(sline, ";_EXTRUDE_END")) { // Closing a block of non-zero length extrusion moves. line.type = CoolingLine::TYPE_EXTRUDE_END; - if (active_speed_modifier != size_t(-1)) { - assert(active_speed_modifier < adjustment->lines.size()); - CoolingLine &sm = adjustment->lines[active_speed_modifier]; - // There should be at least some extrusion move inside the adjustment block. - // However if the block has no extrusion (which is wrong), fix it for the cooling buffer to work. - assert(sm.length > 0); - assert(sm.time > 0); - if (sm.time <= 0) { - // Likely a zero length extrusion, it should not be emitted, however the zero extrusions should not confuse firmware either. - // Prohibit time adjustment of a block of zero length extrusions by the cooling buffer. - sm.type &= ~CoolingLine::TYPE_ADJUSTABLE; - // But the start / end comment shall be removed. - sm.type |= CoolingLine::TYPE_ADJUSTABLE_EMPTY; - } - } + finalize_sm(false); active_speed_modifier = size_t(-1); + current_stamp = CoolingLine::TYPE_NONE; } else if (boost::starts_with(sline, ";_TOOLCHANGE")) { //not using m_toolchange_prefix anymore because there is no use case for it, there is always a _TOOLCHANGE for when a fan change is needed. int prefix = 13; @@ -1029,8 +1049,6 @@ std::string CoolingBuffer::apply_layer_cooldown( } else if (line->type & CoolingLine::TYPE_EXTRUDE_END) { assert(extrude_tree.size() > 0); if (extrude_tree.size() > 0) { - new_gcode.append(std::string(";TYPE_EXTRUDE remove type ") + - ExtrusionEntity::role_to_string(extrude_tree.back()) + std::string("\n")); extrude_tree.pop_back(); fan_need_set = true; } diff --git a/src/libslic3r/GCode/PressureEqualizer.cpp b/src/libslic3r/GCode/PressureEqualizer.cpp index da8f833138d..fd3a8998914 100644 --- a/src/libslic3r/GCode/PressureEqualizer.cpp +++ b/src/libslic3r/GCode/PressureEqualizer.cpp @@ -17,7 +17,7 @@ namespace Slic3r { static const std::string EXTRUSION_ROLE_TAG = ";_EXTRUSION_ROLE:"; static const std::string EXTRUDE_END_TAG = ";_EXTRUDE_END"; static const std::string EXTRUDE_SET_SPEED_TAG = ";_EXTRUDE_SET_SPEED"; -static const std::string EXTERNAL_PERIMETER_TAG = ";_EXTERNAL_PERIMETER"; +//static const std::string EXTERNAL_PERIMETER_TAG = ";_EXTERNAL_PERIMETER"; // Maximum segment length to split a long segment if the initial and the final flow rate differ. // Smaller value means a smoother transition between two different flow rates. @@ -685,7 +685,7 @@ inline bool is_just_line_with_extrude_set_speed_tag(const std::string &line) break; else return false; -} + } parse_float(p_line, line_end - p_line); eatws(p_line); p_line += EXTRUDE_SET_SPEED_TAG.length(); @@ -700,16 +700,19 @@ void PressureEqualizer::push_line_to_output(const size_t line_idx, const float n output_buffer.begin() + int(this->output_buffer_length) + 1); if (is_just_line_with_extrude_set_speed_tag(prev_line_str)) this->output_buffer_length = this->output_buffer_prev_length; // Remove the last line because it only sets the speed for an empty block of g-code lines, so it is useless. - else - push_to_output(EXTRUDE_END_TAG.data(), EXTRUDE_END_TAG.length(), true); - } else - push_to_output(EXTRUDE_END_TAG.data(), EXTRUDE_END_TAG.length(), true); + //else + //push_to_output(EXTRUDE_END_TAG.data(), EXTRUDE_END_TAG.length(), true); // you don't need to extrude_end a EXTRUDE_SET_SPEED_TAG anymore, only for _EXTRUDETYPE_ + } else { + std::cout<<"\n"; + } + //else push_to_output(EXTRUDE_END_TAG.data(), EXTRUDE_END_TAG.length(), true); // you don't need to extrude_end a EXTRUDE_SET_SPEED_TAG anymore, only for _EXTRUDETYPE_ GCodeG1Formatter feedrate_formatter(m_gcode_precision_xyz, m_gcode_precision_e); feedrate_formatter.emit_f(new_feedrate); feedrate_formatter.emit_string(std::string(EXTRUDE_SET_SPEED_TAG.data(), EXTRUDE_SET_SPEED_TAG.length())); - if (line.extrusion_role == erExternalPerimeter) - feedrate_formatter.emit_string(std::string(EXTERNAL_PERIMETER_TAG.data(), EXTERNAL_PERIMETER_TAG.length())); + //you don't need to re-emit that. and now it's _EXTRUDETYPE_{code} anyway + //if (line.extrusion_role == erExternalPerimeter) + // feedrate_formatter.emit_string(std::string(EXTERNAL_PERIMETER_TAG.data(), EXTERNAL_PERIMETER_TAG.length())); push_to_output(feedrate_formatter); GCodeG1Formatter extrusion_formatter(m_gcode_precision_xyz, m_gcode_precision_e); diff --git a/src/libslic3r/GCode/Thumbnails.cpp b/src/libslic3r/GCode/Thumbnails.cpp index 02b5e4f02d4..cfdcfff016e 100644 --- a/src/libslic3r/GCode/Thumbnails.cpp +++ b/src/libslic3r/GCode/Thumbnails.cpp @@ -12,7 +12,7 @@ using namespace std::literals; struct CompressedPNG : CompressedImageBuffer { ~CompressedPNG() override { if (data) mz_free(data); } - std::string_view tag() const override { return "thumbnail"sv; } + std::string_view tag() const override { return "thumbnail_PNG"sv; } }; struct CompressedJPG : CompressedImageBuffer diff --git a/src/libslic3r/GCode/Thumbnails.hpp b/src/libslic3r/GCode/Thumbnails.hpp index 45565d0647c..2b04415c9eb 100644 --- a/src/libslic3r/GCode/Thumbnails.hpp +++ b/src/libslic3r/GCode/Thumbnails.hpp @@ -13,6 +13,8 @@ namespace Slic3r::GCodeThumbnails { +constexpr std::string_view EMPTY_TAG = "thumbnail"; + struct CompressedImageBuffer { void *data { nullptr }; @@ -24,7 +26,13 @@ struct CompressedImageBuffer std::unique_ptr compress_thumbnail(const ThumbnailData &data, GCodeThumbnailsFormat format); template -inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, const std::vector &sizes, bool with_bed, GCodeThumbnailsFormat format, WriteToOutput output, ThrowIfCanceledCallback throw_if_canceled) +inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, + const std::vector & sizes, + bool with_bed, + GCodeThumbnailsFormat format, + bool with_tag_format, + WriteToOutput output, + ThrowIfCanceledCallback throw_if_canceled) { // Write thumbnails using base64 encoding if (thumbnail_cb != nullptr) { @@ -43,7 +51,9 @@ inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, auto compressed = compress_thumbnail(data, format); if (compressed->data && compressed->size) { if (format == GCodeThumbnailsFormat::BIQU) { - output((boost::format("\n;\n; %s begin %dx%d %d\n") % compressed->tag() % data.width % data.height % (compressed->size - 1)).str().c_str()); + output((boost::format("\n;\n; %s begin %dx%d %d\n") + % (with_tag_format ? compressed->tag() : EMPTY_TAG) + % data.width % data.height % (compressed->size - 1)).str().c_str()); //print size in hex std::stringstream ss; ss << std::setfill('0') << std::hex; @@ -60,7 +70,9 @@ inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, encoded.resize(boost::beast::detail::base64::encoded_size(compressed->size)); encoded.resize(boost::beast::detail::base64::encode((void*)encoded.data(), (const void*)compressed->data, compressed->size)); - output((boost::format("\n;\n; %s begin %dx%d %d\n") % compressed->tag() % data.width % data.height % encoded.size()).str().c_str()); + output((boost::format("\n;\n; %s begin %dx%d %d\n") + % (with_tag_format ? compressed->tag() : EMPTY_TAG) + % data.width % data.height % encoded.size()).str().c_str()); while (encoded.size() > max_row_length) { output((boost::format("; %s\n") % encoded.substr(0, max_row_length)).str().c_str()); encoded = encoded.substr(max_row_length); @@ -69,7 +81,7 @@ inline void export_thumbnails_to_file(ThumbnailsGeneratorCallback &thumbnail_cb, if (encoded.size() > 0) output((boost::format("; %s\n") % encoded).str().c_str()); } - output((boost::format("; %s end\n;\n") % compressed->tag()).str().c_str()); + output((boost::format("; %s end\n;\n") % (with_tag_format ? compressed->tag() : EMPTY_TAG)).str().c_str()); } throw_if_canceled(); } diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 00771703452..8500e91764b 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -200,10 +200,82 @@ ProcessSurfaceResult PerimeterGenerator::process_arachne(int& loop_number, const } } - Polygons last_p = to_polygons(last); + // only_one_perimeter_top, from orca + std::vector out_shell; + if (loop_number > 0 && this->config->only_one_perimeter_top && !surface.has_mod_bridge() && upper_slices != nullptr) { + // Check if current layer has surfaces that are not covered by upper layer (i.e., top surfaces) + ExPolygons non_top_polygons; + ExPolygons fill_clip; + + //has to set the outer polygon to the centerline of the external perimeter + split_top_surfaces(lower_slices, upper_slices, offset_ex(last, -this->ext_perimeter_spacing/2), result.top_fills, non_top_polygons, result.fill_clip); + + if (result.top_fills.empty()) { + // No top surfaces, no special handling needed + } else { + // First we slice the outer shell + const Polygons last_p = to_polygons(last); + Arachne::WallToolPaths wallToolPaths(last_p, this->get_ext_perimeter_spacing(),this->get_ext_perimeter_width(), + this->get_perimeter_spacing(), this->get_perimeter_width(), 1, coord_t(0), + this->layer->height, *this->object_config, *this->print_config); + out_shell = wallToolPaths.getToolPaths(); + // Make sure infill not overlap with wall + // offset the InnerContour as arachne use bounds and not centerline + result.top_fills = intersection_ex(result.top_fills, offset_ex(wallToolPaths.getInnerContour(), this->ext_perimeter_spacing/2)); + + if (!result.top_fills.empty()) { + // Then get the inner part that needs more walls + // reduce the not-top fill to the bound for arachne (as arachne doesn't use the centerline but the boundary) + // note: you can also diff_ex(offset_ex(result.top_fills, this->perimeter_spacing / 2), wallToolPaths.getInnerContour()); this should have similar results + last = intersection_ex(offset_ex(non_top_polygons, -this->perimeter_spacing / 2), wallToolPaths.getInnerContour()); + //{ + // static int i = 0; + // i++; + // std::stringstream stri; + // stri << this->layer->id() << "_M_" << i << "_only_one_peri" + // << ".svg"; + // SVG svg(stri.str()); + // //svg.draw(to_polylines(old_last), "green"); + // //svg.draw(to_polylines(offset_ex(old_last, -this->ext_perimeter_spacing / 2)), "lime"); + // //svg.draw(to_polylines(old_top), "blue"); + // svg.draw(to_polylines(result.top_fills), "cyan"); + // svg.draw(to_polylines(result.fill_clip), "pink"); + // svg.draw(to_polylines(wallToolPaths.getInnerContour()), "orange"); + // svg.draw(to_polylines(non_top_polygons), "red"); + // svg.draw(to_polylines(last), "brown"); + // svg.Close(); + //} + loop_number = 0; + } else { + // Give up the outer shell because we don't have any meaningful top surface + out_shell.clear(); + } + } + } - Arachne::WallToolPaths wallToolPaths(last_p, this->get_ext_perimeter_spacing(), this->get_ext_perimeter_width(), this->get_perimeter_spacing(), this->get_perimeter_width(), coord_t(loop_number + 1), 0, this->layer->height, *this->object_config, *this->print_config); + const Polygons last_p = to_polygons(last); + Arachne::WallToolPaths wallToolPaths(last_p, this->get_ext_perimeter_spacing(), this->get_ext_perimeter_width(), + this->get_perimeter_spacing(), this->get_perimeter_width(), loop_number + 1, coord_t(0), + this->layer->height, *this->object_config, *this->print_config); std::vector perimeters = wallToolPaths.getToolPaths(); + + // only_one_perimeter_top, from orca + if (!out_shell.empty()) { + // Combine outer shells + size_t inset_offset = 0; + for (auto &p : out_shell) { + for (auto &l : p) { + if (l.inset_idx + 1 > inset_offset) { + inset_offset = l.inset_idx + 1; + } + } + } + for (auto &p : perimeters) { + for (auto &l : p) { l.inset_idx += inset_offset; } + } + perimeters.insert(perimeters.begin(), out_shell.begin(), out_shell.end()); + } + loop_number = int(perimeters.size()) - 1; #ifdef ARACHNE_DEBUG @@ -372,6 +444,143 @@ ProcessSurfaceResult PerimeterGenerator::process_arachne(int& loop_number, const return result; } +void PerimeterGenerator::split_top_surfaces(const ExPolygons *lower_slices, + const ExPolygons *upper_slices, + const ExPolygons &orig_polygons, + ExPolygons & top_fills, + ExPolygons & non_top_polygons, + ExPolygons & fill_clip) +{ + // other perimeters + coord_t perimeter_width = this->perimeter_flow.scaled_width(); + coord_t perimeter_spacing = this->perimeter_flow.scaled_spacing(); + + // external perimeters + coord_t ext_perimeter_width = this->ext_perimeter_flow.scaled_width(); + coord_t ext_perimeter_spacing = this->ext_perimeter_flow.scaled_spacing(); + + double fill_nozzle_diameter = this->solid_infill_flow.nozzle_diameter(); + + bool has_gap_fill = this->config->gap_fill_enabled && + this->object_config->perimeter_generator.value == PerimeterGeneratorType::Classic; + + // split the polygons with top/not_top + // get the offset from solid surface anchor* + const int32_t peri_count = this->config->perimeters.value; + const double max_perimeters_width = unscaled(double(ext_perimeter_width + perimeter_spacing * int(peri_count - int(1)))); + coord_t offset_top_surface = scale_t(config->external_infill_margin.get_abs_value(peri_count == 0 ? 0. : max_perimeters_width)); + // if possible, try to not push the extra perimeters inside the sparse infill + if (offset_top_surface > 0.9 * (peri_count <= 1 ? 0. : (perimeter_spacing * (peri_count - 1)))) + offset_top_surface -= coord_t(0.9 * (peri_count <= 1 ? 0. : (perimeter_spacing * (peri_count - 1)))); + else + offset_top_surface = 0; + // don't takes into account too thin areas + // skip if the exposed area is smaller than "min_width_top_surface" + coordf_t min_width_top_surface = std::max(coordf_t(ext_perimeter_spacing / 2 + 10), + scale_d(this->config->min_width_top_surface.get_abs_value(unscaled(perimeter_width)))); + + Polygons grown_upper_slices; + if (!this->config->only_one_perimeter_top_other_algo.value) { + grown_upper_slices = offset(*upper_slices, min_width_top_surface); + } else { + ExPolygons grown_accumulator; + // make thin upper surfaces disapear with -+offset_top_surface + // do offset2 per island, to avoid big blob merging + // remove polygon too thin (but don't mess with holes) + for (const ExPolygon &expoly_to_grow : *this->upper_slices) { + // only offset the contour, as it can merge holes + Polygons contour = offset2(ExPolygons{ExPolygon{expoly_to_grow.contour}}, -offset_top_surface, + offset_top_surface + min_width_top_surface + + (this->mill_extra_size > SCALED_EPSILON ? (double) mill_extra_size : 0)); + if (!contour.empty()) { + if (expoly_to_grow.holes.empty()) { + for (Polygon &p : contour) grown_accumulator.push_back(ExPolygon{p}); + } else { + Polygons holes = expoly_to_grow.holes; + for (Polygon &h : holes) h.reverse(); + holes = offset(holes, + -min_width_top_surface - + ((this->mill_extra_size > SCALED_EPSILON) ? (double) mill_extra_size : 0)); + for (ExPolygon p : diff_ex(contour, holes)) grown_accumulator.push_back(p); + } + } + } + grown_upper_slices = union_(grown_accumulator); + } + + // get boungding box of last + BoundingBox last_box = get_extents(orig_polygons); + last_box.offset(SCALED_EPSILON); + + // get the Polygons upper the polygon this layer + Polygons upper_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(grown_upper_slices, last_box); + + // set the clip to a virtual "second perimeter" + fill_clip = offset_ex(orig_polygons, -coordf_t(ext_perimeter_spacing)); + // Check whether surface be bridge or not + ExPolygons bridge_checker; + if (lower_slices != nullptr) { + // BBS: get the Polygons below the polygon this layer + Polygons lower_polygons_series_clipped = ClipperUtils::clip_clipper_polygons_with_subject_bbox(*lower_slices, last_box); + coordf_t bridge_offset = std::max(coordf_t(ext_perimeter_spacing), coordf_t(perimeter_width)); + // SoftFever: improve bridging + const coordf_t bridge_margin = scale_d(this->config->bridged_infill_margin.get_abs_value(unscaled(perimeter_width))); + bridge_checker = offset_ex(diff_ex(orig_polygons, lower_polygons_series_clipped, ApplySafetyOffset::Yes), + 1.5 * bridge_offset + bridge_margin + perimeter_spacing / 2); + } + ExPolygons delete_bridge = diff_ex(orig_polygons, bridge_checker, ApplySafetyOffset::Yes); + // get the real top surface + ExPolygons top_polygons; + if (this->mill_extra_size < SCALED_EPSILON) { + top_polygons = diff_ex(delete_bridge, upper_polygons_series_clipped, ApplySafetyOffset::Yes); + } else if (this->unmillable.empty()) { + top_polygons = diff_ex(delete_bridge, offset_ex(upper_polygons_series_clipped, (double) mill_extra_size), ApplySafetyOffset::Yes); + } else { + top_polygons = diff_ex(delete_bridge, + diff_ex(offset_ex(upper_polygons_series_clipped, (double) mill_extra_size), + unmillable, ApplySafetyOffset::Yes)); + } + // save the top area for gap fill, or something. Made by BB/orca, but no comment. + ExPolygons temp_gap = diff_ex(top_polygons, fill_clip); + // get the not-top surface, from the "real top" but enlarged by external_infill_margin (and the + // min_width_top_surface we removed a bit before) + // also remove the ext_perimeter_spacing/2 width because we are faking the external perimeter, and we will remove ext_perimeter_spacing2 + ExPolygons inner_polygons = diff_ex(orig_polygons, + offset_ex(top_polygons, offset_top_surface + min_width_top_surface - double(ext_perimeter_spacing / 2)), + ApplySafetyOffset::Yes); + // get the enlarged top surface, by using inner_polygons instead of upper_slices, and clip it for it to be exactly + // the polygons to fill. + top_polygons = diff_ex(fill_clip, inner_polygons, ApplySafetyOffset::Yes); + // increase by half peri the inner space to fill the frontier between last and stored. + top_fills = union_ex(top_fills, top_polygons); + // set the clip to the external wall but go back inside by infill_extrusion_width/2 to be sure the extrusion won't + // go outside even with a 100% overlap. + double infill_spacing_unscaled = this->config->infill_extrusion_width.get_abs_value(fill_nozzle_diameter); + if (infill_spacing_unscaled == 0) + infill_spacing_unscaled = Flow::auto_extrusion_width(frInfill, fill_nozzle_diameter); + fill_clip = offset_ex(orig_polygons, double(ext_perimeter_spacing / 2) - scale_(infill_spacing_unscaled / 2)); + + non_top_polygons = intersection_ex(inner_polygons, orig_polygons); + // Made by BB/orca, but no comment. Plz test it and report the usefullness. + if (has_gap_fill) + non_top_polygons = union_ex(non_top_polygons, temp_gap); + + //if (!top_fills.empty() && !non_top_polygons.empty()) { + // static int i = 0; + // i++; + // std::stringstream stri; + // stri << this->layer->id() << "_1_" << i << "_only_one_peri" + // << ".svg"; + // SVG svg(stri.str()); + // svg.draw(to_polylines(top_fills), "green"); + // svg.draw(to_polylines(inner_polygons), "yellow"); + // svg.draw(to_polylines(top_polygons), "cyan"); + // svg.draw(to_polylines(orig_polygons), "orange"); + // svg.draw(to_polylines(non_top_polygons), "red"); + // svg.Close(); + //} +} + void PerimeterGenerator::process() { // other perimeters @@ -1219,125 +1428,12 @@ ProcessSurfaceResult PerimeterGenerator::process_classic(int& loop_number, const for(ExPolygon& exp : next_onion) exp.simplify((resolution < SCALED_EPSILON ? SCALED_EPSILON : resolution), &last); - //store surface for top infill if only_one_perimeter_top + // store surface for top infill if only_one_perimeter_top if (perimeter_idx == 0 && (config->only_one_perimeter_top && this->upper_slices != NULL)) { - if (this->config->only_one_perimeter_top_other_algo) { - //split the polygons with top/not_top - //get the offset from solid surface anchor - coord_t offset_top_surface = scale_(config->external_infill_margin.get_abs_value( - config->perimeters.value == 0 ? 0. : unscaled(double(ext_perimeter_width + perimeter_spacing * int(int(config->perimeters.value) - int(1)))))); - // if possible, try to not push the extra perimeters inside the sparse infill - if (offset_top_surface > 0.9 * (config->perimeters.value <= 1 ? 0. : (perimeter_spacing * (config->perimeters.value - 1)))) - offset_top_surface -= coord_t(0.9 * (config->perimeters.value <= 1 ? 0. : (perimeter_spacing * (config->perimeters.value - 1)))); - else offset_top_surface = 0; - //don't takes into account too thin areas - double min_width_top_surface = std::max(double(ext_perimeter_spacing / 2 + 10), scale_d(this->config->min_width_top_surface.get_abs_value(unscaled(perimeter_width)))); - //make thin upper surfaces disapear with -+offset_top_surface - ExPolygons grown_upper_slices; - //do offset2 per island, to avoid big blob merging - //remove polygon too thin (but don't mess with holes) - for (const ExPolygon& expoly_to_grow : *this->upper_slices) { - //only offset the contour, as it can merge holes - Polygons contour = offset2(ExPolygons{ ExPolygon{expoly_to_grow.contour} }, -offset_top_surface, offset_top_surface + min_width_top_surface + (this->mill_extra_size > SCALED_EPSILON ? (double)mill_extra_size : 0)); - if (!contour.empty()) { - if (expoly_to_grow.holes.empty()) { - for (Polygon& p : contour) - grown_upper_slices.push_back(ExPolygon{ p }); - } else { - Polygons holes = expoly_to_grow.holes; - for (Polygon& h : holes) - h.reverse(); - holes = offset(holes, -min_width_top_surface - ((this->mill_extra_size > SCALED_EPSILON) ? (double)mill_extra_size : 0)); - for (ExPolygon p : diff_ex(contour, holes)) - grown_upper_slices.push_back(p); - } - } - } - grown_upper_slices = union_ex(grown_upper_slices); - //set the clip to a virtual "second perimeter" - results.fill_clip = offset_ex(last, -double(ext_perimeter_spacing)); - auto fill_clip_old = results.fill_clip; - // get the real top surface - const ExPolygons top_grown_polygons = (!(this->mill_extra_size > SCALED_EPSILON)) - ? diff_ex(last, grown_upper_slices, ApplySafetyOffset::Yes) - : (unmillable.empty()) - ? diff_ex(last, grown_upper_slices, ApplySafetyOffset::Yes) - : diff_ex(last, diff_ex(grown_upper_slices, unmillable, ApplySafetyOffset::Yes)); - - //get the not-top surface, from the "real top" but enlarged by external_infill_margin (and the min_width_top_surface we removed a bit before) - const ExPolygons inner_polygons = diff_ex(last, - offset_ex(top_grown_polygons, offset_top_surface + min_width_top_surface - //also remove the ext_perimeter_spacing/2 width because we are faking the external periemter, and we will remove ext_perimeter_spacing2 - - double(ext_perimeter_spacing / 2)), ApplySafetyOffset::Yes); - // get the enlarged top surface, by using inner_polygons instead of upper_slices, and clip it for it to be exactly the polygons to fill. - const ExPolygons top_polygons = diff_ex(results.fill_clip, inner_polygons, ApplySafetyOffset::Yes); - // increase by half peri the inner space to fill the frontier between last and stored. - results.top_fills = union_ex(results.top_fills, top_polygons); - //set the clip to the external wall but go back inside by infill_extrusion_width/2 to be sure the extrusion won't go outside even with a 100% overlap. - double infill_spacing_unscaled = this->config->infill_extrusion_width.get_abs_value(this->solid_infill_flow.nozzle_diameter()); - if (infill_spacing_unscaled == 0) infill_spacing_unscaled = Flow::auto_extrusion_width(frInfill, this->solid_infill_flow.nozzle_diameter()); - results.fill_clip = offset_ex(last, double(ext_perimeter_spacing / 2) - scale_d(infill_spacing_unscaled / 2)); - last = intersection_ex(inner_polygons, last); - //{ - // static int isazfn = 0; - // std::stringstream stri; - // stri << this->layer->id() << "_" << perimeter_idx << "_"<< isazfn++ <<"_only_one_peri"<< ".svg"; - // SVG svg(stri.str()); - // svg.draw(to_polylines(oldLast), "orange"); - // svg.draw(to_polylines(fill_clip), "purple"); - // svg.draw(to_polylines(inner_polygons), "yellow"); - // svg.draw(to_polylines(top_polygons), "cyan"); - // svg.draw(to_polylines(last), "red"); - // svg.draw(to_polylines(fill_clip_old), "green"); - // svg.Close(); - //} - } else { - - //split the polygons with top/not_top - //get the offset from solid surface anchor - coord_t offset_top_surface = scale_(config->external_infill_margin.get_abs_value( - config->perimeters.value == 0 ? 0. : unscaled(double(ext_perimeter_width + perimeter_spacing * int(int(config->perimeters.value) - int(1)))))); - // if possible, try to not push the extra perimeters inside the sparse infill - if (offset_top_surface > 0.9 * (config->perimeters.value <= 1 ? 0. : (perimeter_spacing * (config->perimeters.value - 1)))) - offset_top_surface -= coord_t(0.9 * (config->perimeters.value <= 1 ? 0. : (perimeter_spacing * (config->perimeters.value - 1)))); - else offset_top_surface = 0; - //don't takes into account too thin areas - double min_width_top_surface = std::max(double(ext_perimeter_spacing / 2 + 10), scale_d(this->config->min_width_top_surface.get_abs_value(unscaled(perimeter_width)))); - ExPolygons grown_upper_slices = offset_ex(*this->upper_slices, min_width_top_surface); - //set the clip to a virtual "second perimeter" - results.fill_clip = offset_ex(last, -double(ext_perimeter_spacing)); - // get the real top surface - ExPolygons top_polygons = (!(this->mill_extra_size > SCALED_EPSILON)) - ? diff_ex(last, grown_upper_slices, ApplySafetyOffset::Yes) - : (unmillable.empty()) - ? diff_ex(last, offset_ex(grown_upper_slices, (double)mill_extra_size), ApplySafetyOffset::Yes) - : diff_ex(last, diff_ex(offset_ex(grown_upper_slices, (double)mill_extra_size), unmillable, ApplySafetyOffset::Yes)); - - - //get the not-top surface, from the "real top" but enlarged by external_infill_margin (and the min_width_top_surface we removed a bit before) - ExPolygons inner_polygons = diff_ex(last, offset_ex(top_polygons, offset_top_surface + min_width_top_surface - //also remove the ext_perimeter_spacing/2 width because we are faking the external perimeter, and we will remove ext_perimeter_spacing2 - - double(ext_perimeter_spacing / 2)), ApplySafetyOffset::Yes); - // get the enlarged top surface, by using inner_polygons instead of upper_slices, and clip it for it to be exactly the polygons to fill. - top_polygons = diff_ex(results.fill_clip, inner_polygons, ApplySafetyOffset::Yes); - // increase by half peri the inner space to fill the frontier between last and stored. - results.top_fills = union_ex(results.top_fills, top_polygons); - //set the clip to the external wall but go back inside by infill_extrusion_width/2 to be sure the extrusion won't go outside even with a 100% overlap. - results.fill_clip = offset_ex(last, double(ext_perimeter_spacing / 2) - this->config->infill_extrusion_width.get_abs_value(this->solid_infill_flow.nozzle_diameter()) / 2); - //ExPolygons oldLast = last; - last = intersection_ex(inner_polygons, last); - //{ - // std::stringstream stri; - // stri << this->layer->id() << "_1_"<< perimeter_idx <<"_only_one_peri"<< ".svg"; - // SVG svg(stri.str()); - // svg.draw(to_polylines(top_fills), "green"); - // svg.draw(to_polylines(inner_polygons), "yellow"); - // svg.draw(to_polylines(top_polygons), "cyan"); - // svg.draw(to_polylines(oldLast), "orange"); - // svg.draw(to_polylines(last), "red"); - // svg.Close(); - //} - } + ExPolygons next; + split_top_surfaces(this->lower_slices, this->upper_slices, last, results.top_fills, next, + results.fill_clip); + last = next; } } diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp index f2c85cbb2b2..192d4691a91 100644 --- a/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -172,7 +172,13 @@ class PerimeterGenerator { ExtrusionLoop _extrude_and_cut_loop(const PerimeterGeneratorLoop& loop, const Point entryPoint, const Line& direction = Line(Point(0, 0), Point(0, 0)), bool enforce_loop = false) const; // sub-function of _traverse_and_join_loops, find the good splot to cut a loop to be able to join it with an other one PerimeterIntersectionPoint _get_nearest_point(const PerimeterGeneratorLoops &children, ExtrusionLoop &myPolylines, const coord_t dist_cut, const coord_t max_dist) const; - + // for one_peri_on_top + void split_top_surfaces(const ExPolygons *lower_slices, + const ExPolygons *upper_slices, + const ExPolygons &orig_polygons, + ExPolygons & top_fills, + ExPolygons & non_top_polygons, + ExPolygons & fill_clip); }; diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 92c25b503e0..bd32837407b 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -939,7 +939,7 @@ namespace client case coFloat: output.set_d(opt.opt->get_float()); break; case coInt: output.set_i(opt.opt->get_int()); break; case coString: output.set_s(static_cast(opt.opt)->value); break; - case coPercent: output.set_d(opt.opt->get_float()); break; + case coPercent: output.set_d(opt.opt->get_float()); break; case coPoint: output.set_s(opt.opt->serialize()); break; case coBool: output.set_b(opt.opt->get_bool()); break; case coFloatOrPercent: @@ -969,7 +969,7 @@ namespace client v *= val; break; // if (opt_parent->type() == coFloat || opt_parent->type() == coFloatOrPercent) { - // v *= opt_parent->getFloat(); + // v *= opt_parent->get_float(); // if (opt_parent->type() == coFloat || ! static_cast(opt_parent)->percent) // break; // v *= 0.01; // percent to ratio @@ -985,7 +985,7 @@ namespace client case coInts: opt_def = print_config_def.get(opt_key); if (opt_def->is_vector_extruder) { - output.set_i(int(((ConfigOptionVectorBase *) opt.opt)->get_float(int(ctx->current_extruder_id)))); + output.set_i(int(((ConfigOptionVectorBase*)opt.opt)->get_float(int(ctx->current_extruder_id)))); break; } else ctx->throw_exception("Unknown scalar variable type", opt.it_range); @@ -993,7 +993,7 @@ namespace client case coPercents: vector_opt = static_cast(opt.opt); if (vector_opt->is_extruder_size()) { - output.set_d(((ConfigOptionVectorBase *) opt.opt)->get_float(int(ctx->current_extruder_id))); + output.set_d(((ConfigOptionVectorBase*)opt.opt)->get_float(int(ctx->current_extruder_id))); break; } else ctx->throw_exception("Unknown scalar variable type", opt.it_range); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index df9e639bc84..62f53fd6727 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -849,6 +849,7 @@ static std::vector s_Preset_printer_options { "thumbnails_custom_color", "thumbnails_end_file", "thumbnails_format", + "thumbnails_tag_format", "thumbnails_with_bed", "wipe_advanced", "wipe_advanced_nozzle_melted_volume", @@ -960,6 +961,7 @@ static std::vector s_Preset_sla_printer_options { "thumbnails_color", "thumbnails_custom_color", "thumbnails_with_bed", + "thumbnails_tag_format", "thumbnails_with_support", }; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 75e588ede42..dbc6f89f469 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -187,6 +187,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver& /* ne "thumbnails_custom_color", "thumbnails_end_file", "thumbnails_format", + "thumbnails_tag_format", "thumbnails_with_bed", "time_estimation_compensation", "time_cost", diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 7d577f64f0a..585f5a0be7f 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -92,7 +92,7 @@ static inline void layer_height_ranges_copy_configs(t_layer_config_ranges &lr_ds assert(std::abs(kvp_dst.first.first - kvp_src.first.first ) <= EPSILON); assert(std::abs(kvp_dst.first.second - kvp_src.first.second) <= EPSILON); // Layer heights are allowed do differ in case the layer height table is being overriden by the smooth profile. - // assert(std::abs(kvp_dst.second.option("layer_height")->getFloat() - kvp_src.second.option("layer_height")->getFloat()) <= EPSILON); + // assert(std::abs(kvp_dst.second.option("layer_height")->get_float() - kvp_src.second.option("layer_height")->get_float()) <= EPSILON); kvp_dst.second = kvp_src.second; } } diff --git a/src/libslic3r/PrintBase.cpp b/src/libslic3r/PrintBase.cpp index 035fc3334a2..29bdc1301d0 100644 --- a/src/libslic3r/PrintBase.cpp +++ b/src/libslic3r/PrintBase.cpp @@ -76,8 +76,7 @@ std::string PrintBase::output_filename(const std::string &format, const std::str cfg.set_key_value("input_filename_base", new ConfigOptionString(filename_base)); } try { - uint16_t extruder_initial = config_override->option("initial_extruder") != nullptr && - config_override->option("initial_extruder")->type() == coInt ? config_override->option("initial_extruder")->get_int() : 0; + uint16_t extruder_initial = config_override->option("initial_extruder") != nullptr && config_override->option("initial_extruder")->type() == coInt ? config_override->option("initial_extruder")->get_int() : 0; boost::filesystem::path filepath = format.empty() ? cfg.opt_string("input_filename_base") + default_ext : this->placeholder_parser().process(format, extruder_initial, &cfg); diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 22eb705b34f..42a6e1246a0 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -293,7 +293,7 @@ static const t_config_enum_values s_keys_map_GCodeThumbnailsFormat = { { "PNG", int(GCodeThumbnailsFormat::PNG) }, { "JPG", int(GCodeThumbnailsFormat::JPG) }, { "QOI", int(GCodeThumbnailsFormat::QOI) }, - { "BIQU", int(GCodeThumbnailsFormat::BIQU) } + { "BIQU",int(GCodeThumbnailsFormat::BIQU) }, }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(GCodeThumbnailsFormat) @@ -422,6 +422,13 @@ void PrintConfigDef::init_common_params() def->enum_labels.push_back("Biqu"); def->set_default_value(new ConfigOptionEnum(GCodeThumbnailsFormat::PNG)); + def = this->add("thumbnails_tag_format", coBool); + def->label = L("Write the thumbnail type in gcode."); + def->tooltip = L("instead of writing 'thumbnails' as tag in the gcode, it will write 'thumbnails_PNG', thumbnails_JPG', 'thumbnail_QOI', etc.." + "\n Some firmware needs it to know how to decode the thumbnail, some others don't support it."); + def->mode = comExpert | comSuSi; + def->set_default_value(new ConfigOptionBool(false)); + def = this->add("thumbnails_with_support", coBool); def->label = L("Support on thumbnail"); def->tooltip = L("Show the supports (and pads) on the thumbnail picture."); @@ -1452,7 +1459,8 @@ void PrintConfigDef::init_fff_params() def->label = L("Default"); def->full_label = L("Default infill margin"); def->category = OptionCategory::infill; - def->tooltip = L("This parameter grows the top/bottom/solid layers by the specified mm to anchor them into the sparse infill and support the perimeters above. Put 0 to deactivate it. Can be a % of the width of the perimeters."); + def->tooltip = L("This parameter grows the top/bottom/solid layers by the specified mm to anchor them into the sparse infill and support the perimeters above." + " Put 0 to deactivate it. Can be a % of the width of the perimeters."); def->sidetext = L("mm or %"); def->ratio_over = "perimeter_extrusion_width"; def->min = 0; @@ -2295,9 +2303,9 @@ void PrintConfigDef::init_fff_params() def->mode = comExpert | comSuSi; def->set_default_value(new ConfigOptionFloat(0)); - def = this->add("fill_angle_template", coFloats); - def->label = L("Fill angle pattern"); - def->full_label = L("Fill angle pattern"); + def = this->add("fill_angle_template", coFloats); + def->label = L("Fill angle template"); + def->full_label = L("Fill angle template"); def->category = OptionCategory::infill; def->tooltip = L("This define the succetion of infill angle. When defined, it replaces the fill_angle" ", and there won't be any extra 90° for each layer added, but the fill_angle_increment will still be used." @@ -4230,8 +4238,9 @@ void PrintConfigDef::init_fff_params() def->label = L("Post-processing scripts"); def->category = OptionCategory::customgcode; def->tooltip = L("If you want to process the output G-code through custom scripts, " - "just list their absolute paths here. Separate multiple scripts with a semicolon. " - "Scripts will be passed the absolute path to the G-code file as the first argument, " + "just list their absolute paths here." + "\nSeparate multiple scripts with a semicolon or a line return.\n!! please use '\;' here if you want a not-line-separation ';'!!" + "\nScripts will be passed the absolute path to the G-code file as the first argument, " "and they can access the Slic3r config settings by reading environment variables." "\nThe script, if passed as a relative path, will also be searched from the slic3r directory, " "the slic3r configuration directory and the user directory."); @@ -6467,14 +6476,14 @@ void PrintConfigDef::init_fff_params() } case coPercents: { ConfigOptionPercentsNullable *opt = new ConfigOptionPercentsNullable(it_opt->second.default_value.get()->get_float()); - opt->set_any(ConfigOptionFloatsNullable::create_nil(), 0); + opt->set_any(ConfigOptionPercentsNullable::create_nil(), 0); def->set_default_value(opt); break; } case coFloatsOrPercents: { ConfigOptionFloatsOrPercentsNullable*opt = new ConfigOptionFloatsOrPercentsNullable( static_cast(it_opt->second.default_value.get())->get_at(0)); - opt->set_at(ConfigOptionFloatsOrPercentsNullable::NIL_VALUE(), 0); + opt->set_any(ConfigOptionFloatsOrPercentsNullable::create_nil(), 0); def->set_default_value(opt); break; } @@ -7756,6 +7765,11 @@ std::map PrintConfigDef::from_prusa(t_config_option_key if (value != "1") output["default_fan_speed"] = "0"; } + if ("thumbnails_format" == opt_key) { + // by default, no thumbnails_tag_format for png output + if (value == "PNG") + output["thumbnails_tag_format"] = "0"; + } return output; } @@ -7989,6 +8003,7 @@ std::unordered_set prusa_export_to_remove_keys = { "thumbnails_color", "thumbnails_custom_color", "thumbnails_end_file", +"thumbnails_tag_format", "thumbnails_with_bed", "thumbnails_with_support", "time_cost", diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 2fd8d4d6150..ed471275f96 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -1237,6 +1237,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE( ((ConfigOptionBool, thumbnails_custom_color)) ((ConfigOptionBool, thumbnails_end_file)) ((ConfigOptionEnum, thumbnails_format)) + ((ConfigOptionBool, thumbnails_tag_format)) ((ConfigOptionBool, thumbnails_with_bed)) ((ConfigOptionPercent, time_estimation_compensation)) ((ConfigOptionFloat, time_cost)) @@ -1483,6 +1484,7 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionPoints, thumbnails)) ((ConfigOptionString, thumbnails_color)) ((ConfigOptionBool, thumbnails_custom_color)) + ((ConfigOptionBool, thumbnails_tag_format)) ((ConfigOptionBool, thumbnails_with_bed)) ((ConfigOptionBool, thumbnails_with_support)) ((ConfigOptionFloat, z_rotate)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index da8375dd980..27928a2cfd3 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -2541,12 +2541,13 @@ static constexpr const std::initializer_list keys_extrud // 2) Copy the rest of the values. for (auto it = in.cbegin(); it != in.cend(); ++it) if (it->first != key_extruder) - if (ConfigOptionInt *my_opt = out.option(it->first, false); my_opt != nullptr) { + if (ConfigOption* my_opt = out.option(it->first, false); my_opt != nullptr) { if (one_of(it->first, keys_extruders)) { + assert(dynamic_cast(my_opt)); // Ignore "default" extruders. - int extruder = it->second->get_int(); + int extruder = static_cast(it->second.get())->value; if (extruder > 0) - my_opt->value = (extruder); + static_cast(my_opt)->value = (extruder); } else my_opt->set(it->second.get()); } diff --git a/src/libslic3r/SLAPrint.cpp b/src/libslic3r/SLAPrint.cpp index d7b53a4325e..22c2739f532 100644 --- a/src/libslic3r/SLAPrint.cpp +++ b/src/libslic3r/SLAPrint.cpp @@ -863,6 +863,7 @@ bool SLAPrint::invalidate_state_by_config_options(const std::vectoroption("skirts")->get_int() > 0 && - print_config->option("skirt_height")->get_int() > 0) { + if (print_config->option("skirts")->get_int() > 0 && print_config->option("skirt_height")->get_int() > 0) { new_print_config.set_key_value("complete_objects_one_skirt", new ConfigOptionBool(true)); } diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 4e7b0e51838..a0a03ba6de7 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -357,12 +357,12 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) "thin_perimeters", "overhangs_reverse", "perimeter_round_corners"}) toggle_field(el, have_perimeters && !have_arachne); - toggle_field("only_one_perimeter_top", have_perimeters); // with arachne, it will only do it for the last layer + toggle_field("only_one_perimeter_top", have_perimeters); toggle_field("only_one_perimeter_first_layer", config->opt_int("perimeters") > 1); toggle_field("overhangs_width", config->option("overhangs_width_speed")->value > 0); toggle_field("overhangs_reverse_threshold", have_perimeters && config->opt_bool("overhangs_reverse")); toggle_field("overhangs_speed_enforce", have_perimeters && !config->opt_bool("perimeter_loop")); - toggle_field("min_width_top_surface", have_perimeters && config->opt_bool("only_one_perimeter_top") && !have_arachne); + toggle_field("min_width_top_surface", have_perimeters && config->opt_bool("only_one_perimeter_top")); toggle_field("thin_perimeters_all", have_perimeters && config->option("thin_perimeters")->get_float() != 0 && !have_arachne); bool have_thin_wall = !have_arachne && have_perimeters; toggle_field("thin_walls", have_thin_wall); @@ -427,7 +427,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) "infill_extrusion_spacing", "infill_extrusion_change_odd_layers", "infill_speed" }) toggle_field(el, have_infill || has_solid_infill); - toggle_field("fill_angle", (have_infill || has_solid_infill) && ((ConfigOptionVectorBase*)config->option("fill_angle_template"))->size() != 0); + toggle_field("fill_angle", (have_infill || has_solid_infill) && ((ConfigOptionVectorBase*)config->option("fill_angle_template"))->size() == 0); toggle_field("top_solid_min_thickness", ! has_spiral_vase && has_top_solid_infill); toggle_field("bottom_solid_min_thickness", ! has_spiral_vase && has_bottom_solid_infill); diff --git a/src/slic3r/GUI/DoubleSlider.cpp b/src/slic3r/GUI/DoubleSlider.cpp index f3735fc3397..9a1a7781998 100644 --- a/src/slic3r/GUI/DoubleSlider.cpp +++ b/src/slic3r/GUI/DoubleSlider.cpp @@ -1108,7 +1108,7 @@ void Control::Ruler::update(wxWindow* win, const std::vector& values, do int pow = -2; int step = 0; - auto end_it = std::find(values.begin() + 1, values.end(), values.front()); + auto end_it = std::find(values.begin() + 2, values.end(), values.front()); while (pow < 3) { for (int istep : {1, 2, 5}) { @@ -1119,11 +1119,11 @@ void Control::Ruler::update(wxWindow* win, const std::vector& values, do break; int tick = val_it - values.begin(); - // find next tick with istep - val *= 2; - val_it = std::lower_bound(values.begin(), end_it, val - epsilon()); - // count of short ticks between ticks - int short_ticks_cnt = val_it == values.end() ? tick : val_it - values.begin() - tick; + // find next tick with istep + val *= 2; + val_it = std::lower_bound(values.begin(), end_it, val - epsilon()); + // count of short ticks between ticks + int short_ticks_cnt = val_it == values.end() ? tick : val_it - values.begin() - tick; if (lround(short_ticks_cnt * scroll_step) > pixels_per_sm) { step = istep; @@ -1156,12 +1156,13 @@ void Control::draw_ruler(wxDC& dc) wxColour old_clr = dc.GetTextForeground(); dc.SetTextForeground(GREY_PEN.GetColour()); - if (m_ruler.long_step < 0) + if (m_ruler.long_step < 0) { for (size_t tick = 1; tick < m_values.size(); tick++) { wxCoord pos = get_position_from_value(tick); draw_ticks_pair(dc, pos, mid, 5); draw_tick_text(dc, wxPoint(mid, pos), tick); - } else { + } + } else { auto draw_short_ticks = [this, mid](wxDC& dc, double& current_tick, int max_tick) { while (current_tick < max_tick) { wxCoord pos = get_position_from_value(lround(current_tick)); @@ -1189,8 +1190,9 @@ void Control::draw_ruler(wxDC& dc) if (m_values[tick] < value) break; // short ticks from the last tick to the end of current sequence - assert(! std::isnan(short_tick)); - draw_short_ticks(dc, short_tick, tick); + //note: first sequence can be empty. + if(!std::isnan(short_tick)); + draw_short_ticks(dc, short_tick, tick); if (sequence < m_ruler.count() - 1) sequence++; } short_tick = tick; @@ -1214,6 +1216,7 @@ void Control::draw_ruler(wxDC& dc) prev_y_pos = pos; } + assert(!std::isnan(short_tick)); draw_short_ticks(dc, short_tick, tick); if (value == m_ruler.max_values[sequence]) { @@ -1223,6 +1226,7 @@ void Control::draw_ruler(wxDC& dc) } } // short ticks from the last tick to the end + assert(!std::isnan(short_tick)); draw_short_ticks(dc, short_tick, m_max_value); } diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index e86455ad91f..5fd5ef73a7c 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -105,7 +105,6 @@ std::pair get_strings_points(const wxString &str, double min, double return {invalid_val, out_of_range_val}; } - Field::~Field() { if (m_on_kill_focus) @@ -410,10 +409,23 @@ std::pair any_to_wxstring(const boost::any &value, const ConfigO case coStrings: { if (opt_idx < 0) { // custom for strings, as we don't need the serialized form, the normal one with ';' in-between is enough + // (use '\n' for multi-line opt) ConfigOptionStrings reader; reader.set_any(value, opt_idx); std::string good_str; - for (std::string s : reader.values) good_str += s + ";"; + for (std::string &s : reader.values) { + //ensure the separator isn't inside, not escaped. + if (s.find((opt.multiline ? '\n' : ';')) != std::string::npos) { + if (opt.multiline) { + //if multiline, all \n are escaped (again) + boost::replace_all(s, "\\n", "\\\\n"); + boost::replace_all(s, "\n", "\\n"); + } + // all ";" are escaped + boost::replace_all(s, ";","\\;"); + } + good_str.append(s).append((opt.multiline ? "\n" : ";")); + } if (!good_str.empty()) good_str.pop_back(); text_value = good_str; @@ -430,8 +442,7 @@ std::pair any_to_wxstring(const boost::any &value, const ConfigO deserialize(ConfigOptionFloatsOrPercents{}); break; } - if (opt.nullable && - boost::any_cast(value) == ConfigOptionFloatsOrPercentsNullable::NIL_VALUE()) { + if (opt.nullable && ConfigOptionFloatsOrPercentsNullable::is_nil(value)) { text_value = na_value(); has_nil = true; break; @@ -656,17 +667,36 @@ void TextField::get_value_by_opt_type(wxString &str, const bool check_value /* = } case coStrings: if (m_opt_idx < 0) { - ConfigOptionStrings reader; //don't remove spaces and things like that - try { - reader.deserialize(str.ToStdString()); - } catch (std::exception) {} + //don't use reader.deserialize(str.ToStdString()); as the current string isn't escaped. + std::string str_to_split = str.ToStdString(); + std::vector strings; + // ensure no in-string ';' to not mess up the split + boost::replace_all(str_to_split, "\\;", "@$@"); + //split + boost::split(strings, str_to_split, boost::is_any_of("\n;")); + //restore extra ';' and '\n' + for (std::string &line : strings) { + boost::replace_all(line, "@$@", ";"); + if (this->m_opt.multiline) { + boost::replace_all(line, "\\n", "\n"); + boost::replace_all(line, "\\\n", "\\n"); + } + } + // recreate field string std::string good_str; - for (std::string s : reader.values) good_str += s + ";"; + for (std::string s : strings) { + boost::replace_all(s, ";", "\\;"); + if (this->m_opt.multiline) { + boost::replace_all(s, "\\n", "\n"); + boost::replace_all(s, "\\\n", "\\n"); + } + good_str += s + (this->m_opt.multiline ? "\n" : ";"); + } if (!good_str.empty()) good_str.pop_back(); - need_update = (str.ToStdString() != good_str); - m_value = reader.values; + need_update = (str.ToStdString() != good_str); // mostly true, even when not needed + m_value = strings; break; } case coString: m_value = std::string(str.ToUTF8().data()); break; @@ -732,7 +762,7 @@ void TextField::get_value_by_opt_type(wxString &str, const bool check_value /* = str.Replace("m", "", true); if (m_opt.nullable && str == NIL_STR_VALUE) { - m_value = ConfigOptionFloatsOrPercentsNullable::NIL_VALUE(); + m_value = ConfigOptionFloatsOrPercentsNullable::create_nil(); break; } else if (!str.ToDouble(&val)) { if (!check_value) { @@ -740,7 +770,7 @@ void TextField::get_value_by_opt_type(wxString &str, const bool check_value /* = break; } show_error(m_parent, _(L("Invalid numeric input."))); - set_value(FloatOrPercent{val, is_percent}, true); + set_any_value(FloatOrPercent{val, is_percent}, true); } else { //convert m_value into str to compare FloatOrPercent val_from_m_value = m_value.empty() ? FloatOrPercent{0, false} : @@ -758,7 +788,7 @@ void TextField::get_value_by_opt_type(wxString &str, const bool check_value /* = show_error(m_parent, _(L("Input value is out of range"))); if (m_opt.min > val) val = m_opt.min; - set_value(FloatOrPercent{val, is_percent}, true); + set_any_value(FloatOrPercent{val, is_percent}, true); } else if (m_value.empty() || str != str_from_m_value) { // empty of not equal -> need check bool not_ok = (m_opt.sidetext.rfind("mm/s") != std::string::npos && val > m_opt.max); @@ -806,10 +836,10 @@ void TextField::get_value_by_opt_type(wxString &str, const bool check_value /* = str += "%"; is_percent = true; m_last_validated_value = str; - set_value(FloatOrPercent{val, is_percent}, false /*true*/); + set_any_value(FloatOrPercent{val, is_percent}, false /*true*/); str = m_last_validated_value; } else - set_value(FloatOrPercent{val, is_percent}, false); // it's no needed but can be helpful, when inputted value + set_any_value(FloatOrPercent{val, is_percent}, false); // it's no needed but can be helpful, when inputted value // contained "," instead of "." m_last_validated_value = str; } @@ -919,7 +949,7 @@ void TextCtrl::BUILD() { assert(m_opt.default_value->is_vector()); m_last_meaningful_value = any_to_wxstring(static_cast(m_opt.default_value.get())->get_default_value(), m_opt, m_opt_idx).first; } else { - m_last_meaningful_value = text_value; + m_last_meaningful_value = text_value; } assert(m_last_meaningful_value != na_value()); @@ -1034,7 +1064,7 @@ bool TextCtrl::value_was_changed() double old_val = boost::any_cast(val); if ((std::isnan(new_val) || ConfigOptionFloats::is_nil(m_value)) && (std::isnan(old_val) || ConfigOptionFloats::is_nil(val))) - return false; + return false; } case coPercent: case coFloat: { @@ -1053,10 +1083,7 @@ bool TextCtrl::value_was_changed() boost::any_cast>(val); } if (m_opt.nullable) { - FloatOrPercent new_val = boost::any_cast(m_value); - FloatOrPercent old_val = boost::any_cast(val); - if ((std::isnan(new_val.value) || new_val == ConfigOptionFloatsOrPercents::NIL_VALUE()) && - (std::isnan(old_val.value) || old_val == ConfigOptionFloatsOrPercents::NIL_VALUE())) + if (ConfigOptionFloatsOrPercents::is_nil(m_value) &&ConfigOptionFloatsOrPercents::is_nil(val)) return false; } case coFloatOrPercent: @@ -1102,7 +1129,7 @@ void TextCtrl::propagate_value() m_last_meaningful_value = dynamic_cast(window)->GetValue(); } -void TextCtrl::set_value(const boost::any& value, bool change_event/* = false*/) { +void TextCtrl::set_any_value(const boost::any& value, bool change_event/* = false*/) { //can be: //case coFloat: //case coFloats: @@ -1232,7 +1259,7 @@ void CheckBox::BUILD() { //set value (need the window for the set_value) if (m_opt.is_script && !m_opt.default_script_value.empty()) - set_value(m_opt.default_script_value, false); + set_any_value(m_opt.default_script_value, false); else set_widget_value(check_value); @@ -1261,7 +1288,7 @@ void CheckBox::set_widget_value(bool new_val) #endif } -void CheckBox::set_value(const boost::any &value, bool change_event) +void CheckBox::set_any_value(const boost::any &value, bool change_event) { //can be coBool and coBools (with idx) m_disable_change_event = !change_event; @@ -1322,7 +1349,7 @@ boost::any& CheckBox::get_value() if (m_opt.type == coBool) m_value = static_cast(value); else - m_value = m_is_na_val ? ConfigOptionBoolsNullable::NIL_VALUE() : static_cast(value); + m_value = m_is_na_val ? ConfigOptionBoolsNullable::NIL_VALUE() : static_cast(value); return m_value; } @@ -1738,7 +1765,7 @@ int32_t Choice::idx_from_enum_value(int32_t val) { return 0; } -void Choice::set_value(const boost::any &value, bool change_event) +void Choice::set_any_value(const boost::any &value, bool change_event) { // can be // GUIType::select_open @@ -1758,7 +1785,7 @@ void Choice::set_value(const boost::any &value, bool change_event) case coPercent: case coFloatOrPercent: case coString: - case coStrings: { + case coStrings: { auto [/*wxString*/ text_value, /*bool*/ has_nil] = any_to_wxstring(value, m_opt, m_opt_idx); size_t idx = 0; @@ -2009,7 +2036,7 @@ void ColourPicker::set_undef_value(wxColourPickerCtrl* field) btn->SetBitmapLabel(bmp); } -void ColourPicker::set_value(const boost::any &value, bool change_event) +void ColourPicker::set_any_value(const boost::any &value, bool change_event) { // can be ConfigOptionDef::GUIType::color m_disable_change_event = !change_event; @@ -2184,7 +2211,7 @@ void PointCtrl::set_vec2d_value(const Vec2d& value, bool change_event) m_disable_change_event = false; } -void PointCtrl::set_value(const boost::any &value, bool change_event) +void PointCtrl::set_any_value(const boost::any &value, bool change_event) { // can be coPoint and coPoints (with idx) assert(m_opt.type == coPoint || (m_opt.type == coPoints && m_opt_idx >= 0)); @@ -2198,7 +2225,7 @@ boost::any& PointCtrl::get_value() if (!x_textctrl->GetValue().ToDouble(&x) || !y_textctrl->GetValue().ToDouble(&y)) { - set_value(m_value.empty() ? Vec2d(0.0, 0.0) : m_value, true); + set_any_value(m_value.empty() ? Vec2d(0.0, 0.0) : m_value, true); show_error(m_parent, _L("Invalid numeric input.")); } else @@ -2301,7 +2328,7 @@ void SliderCtrl::BUILD() m_sizer = dynamic_cast(temp); } -void SliderCtrl::set_value(const boost::any &value, bool change_event) +void SliderCtrl::set_any_value(const boost::any &value, bool change_event) { // only with ConfigOptionDef::GUIType::slider: & coFloat or coInt assert(m_opt.gui_type == ConfigOptionDef::GUIType::slider && (m_opt.type == coFloat || m_opt.type == coInt)); @@ -2332,5 +2359,6 @@ boost::any &SliderCtrl::get_value() return m_value; } + } // GUI } // Slic3r diff --git a/src/slic3r/GUI/Field.hpp b/src/slic3r/GUI/Field.hpp index 574cda881e2..db0c6aafc34 100644 --- a/src/slic3r/GUI/Field.hpp +++ b/src/slic3r/GUI/Field.hpp @@ -120,7 +120,7 @@ class Field { /// Sets a value for this control. /// subclasses should overload with a specific version /// Postcondition: Method does not fire the on_change event. - virtual void set_value(const boost::any &value, bool change_event) = 0; + virtual void set_any_value(const boost::any &value, bool change_event) = 0; virtual void set_last_meaningful_value() {} virtual void set_na_value() {} @@ -300,7 +300,7 @@ class TextCtrl : public TextField { wxWindow* window {nullptr}; void set_text_value(const std::string &value, bool change_event = false) override; - void set_value(const boost::any& value, bool change_event = false) override; + void set_any_value(const boost::any& value, bool change_event = false) override; void set_last_meaningful_value() override; void set_na_value() override; @@ -333,7 +333,7 @@ class CheckBox : public Field { dynamic_cast(window)->SetValue(value); m_disable_change_event = false; } - void set_value(const boost::any &value, bool change_event = false) override; + void set_any_value(const boost::any &value, bool change_event = false) override; void set_last_meaningful_value() override; void set_na_value() override; boost::any& get_value() override; @@ -367,7 +367,7 @@ class SpinCtrl : public Field { dynamic_cast(window)->SetValue(value); m_disable_change_event = false; } - void set_value(const boost::any& value, bool change_event = false) override { + void set_any_value(const boost::any &value, bool change_event = false) override { m_disable_change_event = !change_event; tmp_value = boost::any_cast(value); m_value = value; @@ -416,7 +416,7 @@ class Choice : public TextField void set_selection(); void set_text_value(const std::string &value, bool change_event = false); - void set_value(const boost::any &value, bool change_event = false) override; + void set_any_value(const boost::any &value, bool change_event = false) override; void set_values(const std::vector &values); void set_values(const wxArrayString &values); boost::any& get_value() override; @@ -447,7 +447,7 @@ class ColourPicker : public Field { dynamic_cast(window)->SetColour(value); m_disable_change_event = false; } - void set_value(const boost::any &value, bool change_event = false) override; + void set_any_value(const boost::any &value, bool change_event = false) override; boost::any& get_value() override; void msw_rescale() override; void sys_color_changed() override; @@ -473,7 +473,7 @@ class PointCtrl : public Field { // Propagate value from field to the OptionGroupe and Config after kill_focus/ENTER void propagate_value(wxTextCtrl* win); void set_vec2d_value(const Vec2d& value, bool change_event = false); - void set_value(const boost::any &value, bool change_event = false) override; + void set_any_value(const boost::any &value, bool change_event = false) override; boost::any& get_value() override; void msw_rescale() override; @@ -505,7 +505,7 @@ class StaticText : public Field { dynamic_cast(window)->SetLabel(wxString::FromUTF8(value.data())); m_disable_change_event = false; } - void set_value(const boost::any& value, bool change_event = false) override { + void set_any_value(const boost::any& value, bool change_event = false) override { m_disable_change_event = !change_event; dynamic_cast(window)->SetLabel(boost::any_cast(value)); m_disable_change_event = false; @@ -536,7 +536,7 @@ class SliderCtrl : public Field { void BUILD() override; void set_int_value(const int value, bool change_event = false); - void set_value(const boost::any& value, bool change_event = false) override; + void set_any_value(const boost::any& value, bool change_event = false) override; boost::any& get_value() override; void enable() override { diff --git a/src/slic3r/GUI/GUI.hpp b/src/slic3r/GUI/GUI.hpp index 62ba9ab8a37..430826d9d20 100644 --- a/src/slic3r/GUI/GUI.hpp +++ b/src/slic3r/GUI/GUI.hpp @@ -36,9 +36,6 @@ extern AppConfig* get_app_config(); extern void add_menus(wxMenuBar *menu, int event_preferences_changed, int event_language_change); -// Change option value in config -//void change_opt_value(DynamicConfig& config, const t_config_option_key& opt_key, const boost::any& value, int opt_index = 0); - // If monospaced_font is true, the error message is displayed using html
tags, // so that the code formatting will be preserved. This is useful for reporting errors from the placeholder parser. void show_error(wxWindow* parent, const wxString& message, bool monospaced_font = false); diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index 51dd05d57ed..e42f1e33c9b 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -1005,7 +1005,6 @@ std::pair ConfigOptionsGroup::get_custom_ctrl_with_blinki void ConfigOptionsGroup::change_opt_value(const t_config_option_key& opt_key, const boost::any& value, int opt_index /*= 0*/) { - //Slic3r::GUI::change_opt_value(const_cast(*m_config), opt_key, value, opt_index); const_cast(*m_config).option(opt_key)->set_any(value, opt_index); if (m_modelconfig) m_modelconfig->touch(); diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index da388b6867a..b40440d4ab2 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -157,7 +157,7 @@ class OptionsGroup { } bool set_value(const t_config_option_key& id, const boost::any& value, bool change_event = false) { if (m_fields.find(id) == m_fields.end()) return false; - m_fields.at(id)->set_value(value, change_event); + m_fields.at(id)->set_any_value(value, change_event); return true; } boost::any get_value(const t_config_option_key& id) { @@ -309,10 +309,6 @@ class ConfigOptionsGroup: public OptionsGroup { void msw_rescale(); void sys_color_changed(); void refresh(); - //call optionconfig->get_any() - //boost::any config_value(const std::string& opt_key, int opt_index, bool deserialize); - // return option value from config - //boost::any get_config_value(const DynamicConfig& config, const std::string& opt_key, int opt_index = -1); Field* get_fieldc(const t_config_option_key& opt_key, int opt_index); std::pair get_custom_ctrl_with_blinking_ptr(const t_config_option_key& opt_key, int opt_index/* = -1*/); diff --git a/src/slic3r/GUI/PhysicalPrinterDialog.cpp b/src/slic3r/GUI/PhysicalPrinterDialog.cpp index 9a4105017c6..7ca307ff9da 100644 --- a/src/slic3r/GUI/PhysicalPrinterDialog.cpp +++ b/src/slic3r/GUI/PhysicalPrinterDialog.cpp @@ -308,9 +308,9 @@ void PhysicalPrinterDialog::update_printers() auto value_idx = std::find(slugs.begin(), slugs.end(), m_config->opt("printhost_port")->value); if ((val.empty() || (any_string_type.type() == val.type() && boost::any_cast(val) == "")) && !slugs.empty() && value_idx == slugs.end()) { m_config->option("printhost_port")->set_any(slugs[0]); // change_opt_value(*m_config, "printhost_port", slugs[0]); - choice->set_value(slugs[0], false); + choice->set_text_value(slugs[0], false); } else if (value_idx != slugs.end()) { - choice->set_value(m_config->option("printhost_port")->value, false); + choice->set_any_value(m_config->option("printhost_port")->get_any(), false); } rs->enable(); } @@ -683,7 +683,7 @@ void PhysicalPrinterDialog::update_host_type(bool printer_change) Field* ht = m_optgroup->get_field("host_type"); Choice* choice = dynamic_cast(ht); auto set_to_choice_and_config = [this, choice](PrintHostType type) { - choice->set_value(static_cast(type)); + choice->set_any_value(static_cast(type)); m_config->set_key_value("host_type", new ConfigOptionEnum(type)); }; if ((printer_change && all_presets_are_from_mk3_family) || (!had_all_mk3 && all_presets_are_from_mk3_family)) @@ -691,7 +691,7 @@ void PhysicalPrinterDialog::update_host_type(bool printer_change) else if ((printer_change && !all_presets_are_from_mk3_family) || (!all_presets_are_from_mk3_family && m_config->option>("host_type")->value == htPrusaLink)) set_to_choice_and_config(htOctoPrint); else - choice->set_value(m_config->option("host_type")->get_int()); + choice->set_any_value(m_config->option("host_type")->get_int()); had_all_mk3 = all_presets_are_from_mk3_family; } diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index ee0eb2c2f10..043fc282cc4 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -658,7 +658,7 @@ void PreferencesDialog::build(size_t selected_tab) if (is_editor) { // set Field for notify_release to its value to activate the object boost::any val = s_keys_map_NotifyReleaseMode.at(app_config->get("notify_release")); - m_optgroups_gui.back()->get_field("notify_release")->set_value(val, false); + m_optgroups_gui.back()->get_field("notify_release")->set_any_value(val, false); } //create layout options diff --git a/src/slic3r/GUI/ScriptExecutor.cpp b/src/slic3r/GUI/ScriptExecutor.cpp index 2ffd4ccf9f2..17070466a06 100644 --- a/src/slic3r/GUI/ScriptExecutor.cpp +++ b/src/slic3r/GUI/ScriptExecutor.cpp @@ -870,14 +870,14 @@ void ScriptContainer::call_script_function_set(const ConfigOptionDef& def, const for (const auto& data : to_update) { Tab* tab = wxGetApp().get_tab(data.first); for (auto opt_key : data.second.keys()) { - tab->on_value_change(opt_key, data.second.option(opt_key)->get_any()); + tab->on_value_change(opt_key, data.second.option(opt_key)->get_any(-1)); } } // refresh the field if needed if (m_need_refresh && m_tab) { Field* f = m_tab->get_field(def.opt_key); if (f != nullptr) { - f->set_value(call_script_function_get_value(def), false); + f->set_any_value(call_script_function_get_value(def), false); } } } @@ -925,14 +925,14 @@ bool ScriptContainer::call_script_function_reset(const ConfigOptionDef& def) for (const auto& data : to_update) { Tab* tab = wxGetApp().get_tab(data.first); for (auto opt_key : data.second.keys()) { - tab->on_value_change(opt_key, data.second.option(opt_key)->get_any()); + tab->on_value_change(opt_key, data.second.option(opt_key)->get_any(-1)); } } // refresh the field if needed if (m_need_refresh && m_tab) { Field* f = m_tab->get_field(def.opt_key); if (f != nullptr) { - f->set_value(call_script_function_get_value(def), false); + f->set_any_value(call_script_function_get_value(def), false); } } return true; diff --git a/src/slic3r/GUI/Search.cpp b/src/slic3r/GUI/Search.cpp index 5b81566ecf8..1260d22545c 100644 --- a/src/slic3r/GUI/Search.cpp +++ b/src/slic3r/GUI/Search.cpp @@ -36,21 +36,18 @@ namespace Search { static char marker_by_type(Preset::Type type, PrinterTechnology pt) { - switch(type) { - case Preset::TYPE_FREQUENT_FFF: - case Preset::TYPE_FREQUENT_SLA: - case Preset::TYPE_FFF_PRINT: - case Preset::TYPE_SLA_PRINT: - return ImGui::PrintIconMarker; - case Preset::TYPE_FFF_FILAMENT: + if (Preset::TYPE_FFF_FILAMENT == type) { return ImGui::FilamentIconMarker; - case Preset::TYPE_SLA_MATERIAL: + } else if (Preset::TYPE_SLA_MATERIAL == type) { return ImGui::MaterialIconMarker; - case Preset::TYPE_PRINTER: + } else if ((Preset::TYPE_PRINTER & type) == Preset::TYPE_PRINTER) { return pt == ptSLA ? ImGui::PrinterSlaIconMarker : ImGui::PrinterIconMarker; - default: + } else if ((Preset::TYPE_PRINT1 & type) == Preset::TYPE_PRINT1 || + (Preset::TYPE_FREQUENT & type) == Preset::TYPE_FREQUENT) { return ImGui::PrintIconMarker; - } + } + assert(false); + return ImGui::PrintIconMarker; } std::string Option::opt_key_with_idx() const diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index fe90ddb5368..a6c7b580ed8 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -569,25 +569,27 @@ void Tab::update_label_colours() m_phony_label_clr = wxGetApp().get_label_clr_phony(); //update options "decoration" - for (const std::pair &opt : m_options_list) - { + for (const auto &opt : m_options_list) { + const std::string &opt_key = opt.first; + const int & opt_idx = opt.second.first; + const int & opt_status = opt.second.second; const wxColour *color = &m_sys_label_clr; // value isn't equal to system value - if ((opt.second & osSystemValue) == 0) { + if ((opt_status & osSystemValue) == 0) { // value is equal to last saved - if ((opt.second & osInitValue) != 0) + if ((opt_status & osInitValue) != 0) color = &m_default_label_clr; // value is modified else color = &m_modified_label_clr; } - if ((opt.second & osCurrentPhony) != 0) + if ((opt_status & osCurrentPhony) != 0) color = &m_phony_label_clr; else { - if ((opt.second & osInitPhony) != 0) + if ((opt_status & osInitPhony) != 0) color = &m_modified_label_clr; - else if ((opt.second & osSystemPhony) != 0) + else if ((opt_status & osSystemPhony) != 0) color = &m_default_label_clr; } @@ -631,12 +633,14 @@ void Tab::decorate() { Field* field = nullptr; wxColour* colored_label_clr = nullptr; - + const std::string &opt_key = opt.first; + const int & opt_idx = opt.second.first; + const int & opt_status = opt.second.second; if (OptionsGroup::is_option_without_field(opt.first)) colored_label_clr = (m_colored_Label_colors.find(opt.first) == m_colored_Label_colors.end()) ? nullptr : &m_colored_Label_colors.at(opt.first); if (!colored_label_clr) { - field = get_field(opt.first); + field = get_field(opt_key, opt_idx); if (!field) continue; } @@ -652,18 +656,18 @@ void Tab::decorate() const wxString* tt = &m_tt_value_revert; // value isn't equal to system value - if ((opt.second & osSystemValue) == 0) { + if ((opt_status & osSystemValue) == 0) { is_nonsys_value = true; sys_icon = m_bmp_non_system; sys_tt = m_tt_non_system; // value is equal to last saved - if ((opt.second & osInitValue) != 0) + if ((opt_status & osInitValue) != 0) color = &m_default_label_clr; // value is modified else color = &m_modified_label_clr; } - if ((opt.second & osInitValue) != 0) + if ((opt_status & osInitValue) != 0) { is_modified_value = false; icon = &m_bmp_white_bullet; @@ -671,12 +675,12 @@ void Tab::decorate() } //color for phony things - if ((opt.second & osCurrentPhony) != 0) + if ((opt_status & osCurrentPhony) != 0) color = &m_phony_label_clr; else { - if ((opt.second & osInitPhony) != 0) + if ((opt_status & osInitPhony) != 0) color = &m_modified_label_clr; - else if ((opt.second & osSystemPhony) != 0) + else if ((opt_status & osSystemPhony) != 0) color = &m_default_label_clr; } @@ -714,8 +718,8 @@ void Tab::decorate() for (const std::string &dep : field->m_opt.depends_on) { const auto& it = m_options_list.find(dep); if (it != m_options_list.end()) { - is_not_sys |= ((it->second & osSystemValue) == 0); - is_not_initial |= ((it->second & osInitValue) == 0); + is_not_sys |= ((it->second.second & osSystemValue) == 0); + is_not_initial |= ((it->second.second & osInitValue) == 0); } } @@ -773,7 +777,7 @@ void Tab::update_changed_ui() } for (auto& it : m_options_list) - it.second = m_opt_status_value; + it.second.second = m_opt_status_value; dirty_options.insert(dirty_options.end(), m_options_dirty.begin(), m_options_dirty.end()); m_options_dirty.clear(); @@ -784,21 +788,21 @@ void Tab::update_changed_ui() for (auto& opt_key : m_presets->get_edited_preset().config.keys()) { if (edited_preset.config.option(opt_key)->is_phony()) //ensure that osCurrentPhony is in the bitmask - m_options_list[opt_key] |= osCurrentPhony; + m_options_list[opt_key].second |= osCurrentPhony; if (selected_preset.config.option(opt_key) && selected_preset.config.option(opt_key)->is_phony()) - m_options_list[opt_key] |= osInitPhony; + m_options_list[opt_key].second |= osInitPhony; if (system_preset && system_preset->config.option(opt_key) && system_preset->config.option(opt_key)->is_phony()) - m_options_list[opt_key] |= osSystemPhony; + m_options_list[opt_key].second |= osSystemPhony; } //don't let option that were phony be resetable. for (auto opt_key : dirty_options) - if( (m_options_list[opt_key] & osInitPhony) == 0) + if ((m_options_list[opt_key].second & osInitPhony) == 0) //ensure that osInitValue is not in the bitmask - m_options_list[opt_key] &= ~osInitValue; + m_options_list[opt_key].second &= ~osInitValue; for (auto opt_key : nonsys_options) - if ((m_options_list[opt_key] & osSystemPhony) == 0) - m_options_list[opt_key] &= ~osSystemValue; + if ((m_options_list[opt_key].second & osSystemPhony) == 0) + m_options_list[opt_key].second &= ~osSystemValue; decorate(); @@ -814,15 +818,15 @@ void Tab::init_options_list() m_options_list.clear(); for (const std::string& opt_key : m_config->keys()) - m_options_list.emplace(opt_key, m_opt_status_value); + m_options_list.emplace(opt_key, std::pair(-1, m_opt_status_value)); } template -void add_correct_opts_to_options_list(const std::string &opt_key, std::map& map, Tab *tab, const int& value) +void add_correct_opts_to_options_list(const std::string &opt_key, std::map>& map, Tab *tab, const int& value) { T *opt_cur = static_cast(tab->get_config()->option(opt_key)); for (size_t i = 0; i < opt_cur->values.size(); i++) - map.emplace(opt_key + "#" + std::to_string(i), value); + map.emplace(opt_key /* + "#" + std::to_string(i)*/, std::pair{i, value}); } void TabPrinter::init_options_list() @@ -833,7 +837,7 @@ void TabPrinter::init_options_list() for (const std::string& opt_key : m_config->keys()) { if (opt_key == "bed_shape") { - m_options_list.emplace(opt_key, m_opt_status_value); + m_options_list.emplace(opt_key, std::pair{-1, m_opt_status_value}); continue; } switch (m_config->option(opt_key)->type()) @@ -845,11 +849,11 @@ void TabPrinter::init_options_list() case coPercents:add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; case coFloatsOrPercents:add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; case coPoints: add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; - default: m_options_list.emplace(opt_key, m_opt_status_value); break; + default: m_options_list.emplace(opt_key, std::pair{-1, m_opt_status_value}); break; } } if (m_printer_technology == ptFFF) - m_options_list.emplace("extruders_count", m_opt_status_value); + m_options_list.emplace("extruders_count", std::pair{-1, m_opt_status_value}); } void TabPrinter::msw_rescale() @@ -862,6 +866,15 @@ void TabPrinter::msw_rescale() Layout(); } +void TabFilament::init_options_list() +{ + if (!m_options_list.empty()) + m_options_list.clear(); + + for (const std::string &opt_key : m_config->keys()) + m_options_list.emplace(opt_key, std::pair(0, m_opt_status_value)); +} + void TabSLAMaterial::init_options_list() { if (!m_options_list.empty()) @@ -870,7 +883,7 @@ void TabSLAMaterial::init_options_list() for (const std::string& opt_key : m_config->keys()) { if (opt_key == "compatible_prints" || opt_key == "compatible_printers") { - m_options_list.emplace(opt_key, m_opt_status_value); + m_options_list.emplace(opt_key, std::pair{0, m_opt_status_value}); continue; } switch (m_config->option(opt_key)->type()) @@ -882,7 +895,7 @@ void TabSLAMaterial::init_options_list() case coPercents:add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; case coFloatsOrPercents:add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; case coPoints: add_correct_opts_to_options_list(opt_key, m_options_list, this, m_opt_status_value); break; - default: m_options_list.emplace(opt_key, m_opt_status_value); break; + default: m_options_list.emplace(opt_key, std::pair{0, m_opt_status_value}); break; } } } @@ -893,8 +906,8 @@ void Tab::get_sys_and_mod_flags(const std::string& opt_key, bool& sys_page, bool if (opt == m_options_list.end()) return; - if (sys_page) sys_page = (opt->second & osSystemValue) != 0; - modified_page |= (opt->second & osInitValue) == 0; + if (sys_page) sys_page = (opt->second.second & osSystemValue) != 0; + modified_page |= (opt->second.second & osInitValue) == 0; } void Tab::update_changed_tree_ui() @@ -995,28 +1008,28 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/) for (auto group : m_active_page->m_optgroups) { if (group->title == "Capabilities") { - if ((m_options_list["extruders_count"] & os) == 0) + if ((m_options_list["extruders_count"].second & os) == 0) to_sys ? group->back_to_sys_value("extruders_count") : group->back_to_initial_value("extruders_count"); } if (group->title == "Size and coordinates") { - if ((m_options_list["bed_shape"] & os) == 0) { + if ((m_options_list["bed_shape"].second & os) == 0) { to_sys ? group->back_to_sys_value("bed_shape") : group->back_to_initial_value("bed_shape"); load_key_value("bed_shape", true/*some value*/, true); } } if (group->title == "Toolchange parameters with single extruder MM printers") { - if ((m_options_list["filament_ramming_parameters"] & os) == 0) + if ((m_options_list["filament_ramming_parameters"].second & os) == 0) to_sys ? group->back_to_sys_value("filament_ramming_parameters") : group->back_to_initial_value("filament_ramming_parameters"); } if (group->title == "G-code Substitutions") { - if ((m_options_list["gcode_substitutions"] & os) == 0) { + if ((m_options_list["gcode_substitutions"].second & os) == 0) { to_sys ? group->back_to_sys_value("gcode_substitutions") : group->back_to_initial_value("gcode_substitutions"); load_key_value("gcode_substitutions", true/*some value*/, true); } } if (group->title == "Profile dependencies") { // "compatible_printers" option doesn't exists in Printer Settimgs Tab - if (type() != Preset::TYPE_PRINTER && (m_options_list["compatible_printers"] & os) == 0) { + if (type() != Preset::TYPE_PRINTER && (m_options_list["compatible_printers"].second & os) == 0) { to_sys ? group->back_to_sys_value("compatible_printers") : group->back_to_initial_value("compatible_printers"); load_key_value("compatible_printers", true/*some value*/, true); @@ -1025,7 +1038,8 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/) is_empty ? m_compatible_printers.btn->Disable() : m_compatible_printers.btn->Enable(); } // "compatible_prints" option exists only in Filament Settimgs and Materials Tabs - if ((type() == Preset::TYPE_FFF_FILAMENT || type() == Preset::TYPE_SLA_MATERIAL) && (m_options_list["compatible_prints"] & os) == 0) { + if ((type() == Preset::TYPE_FFF_FILAMENT || type() == Preset::TYPE_SLA_MATERIAL) && + (m_options_list["compatible_prints"].second & os) == 0) { to_sys ? group->back_to_sys_value("compatible_prints") : group->back_to_initial_value("compatible_prints"); load_key_value("compatible_prints", true/*some value*/, true); @@ -1036,7 +1050,7 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/) } for (const auto &kvp : group->opt_map()) { const std::string& opt_key = kvp.first; - if ((m_options_list[opt_key] & os) == 0) + if ((m_options_list[opt_key].second & os) == 0) to_sys ? group->back_to_sys_value(opt_key) : group->back_to_initial_value(opt_key); } } @@ -1311,7 +1325,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) if (field) { boost::any script_val = this->m_script_exec.call_script_function_get_value(field->m_opt); if (!script_val.empty()) - field->set_value(script_val, false); + field->set_any_value(script_val, false); } } { // also check freq changed params @@ -1319,7 +1333,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) if (field) { boost::any script_val = this->m_script_exec.call_script_function_get_value(field->m_opt); if (!script_val.empty()) - field->set_value(script_val, false); + field->set_any_value(script_val, false); } } } @@ -1330,7 +1344,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) Field* field = og_freq_chng_params->get_field(opt_key); if (field) { boost::any val = m_config->option(opt_key)->get_any(field->m_opt_idx); - field->set_value(val, false); + field->set_any_value(val, false); } @@ -2484,6 +2498,7 @@ std::vector Tab::create_pages(std::string setting_type_nam this->m_vector_managers.push_back(manager); manager->set_cb_edited([this]() { update_dirty(); + toggle_options(); wxGetApp().mainframe->on_config_changed(m_config); // invalidate print }); current_line = current_group->create_single_option_line(opt_key); diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index 739c31f02dc..c3bf7b6dc16 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -321,8 +321,10 @@ class Tab: public wxPanel osInitPhony = 8, osCurrentPhony = 16, }; - std::map m_options_list; - std::map m_options_script; + // map> + std::map> m_options_list; + // map (script can't be vector) + std::map m_options_script; std::vector m_options_dirty; int m_opt_status_value = 0; @@ -550,6 +552,7 @@ class TabFilament : public Tab void toggle_options() override; void update() override; void clear_pages() override; + void init_options_list() override; PrinterTechnology get_printer_technology() const override { return ptFFF; } };