diff options
Diffstat (limited to 'src')
91 files changed, 2031 insertions, 2275 deletions
diff --git a/src/admesh/stl.h b/src/admesh/stl.h index 9b1146f8d..117090dd8 100644 --- a/src/admesh/stl.h +++ b/src/admesh/stl.h @@ -247,7 +247,7 @@ extern void its_transform(indexed_triangle_set &its, T *trafo3x4) template<typename T> inline void its_transform(indexed_triangle_set &its, const Eigen::Transform<T, 3, Eigen::Affine, Eigen::DontAlign>& t) { - const Eigen::Matrix<double, 3, 3, Eigen::DontAlign> r = t.matrix().template block<3, 3>(0, 0); + //const Eigen::Matrix<double, 3, 3, Eigen::DontAlign> r = t.matrix().template block<3, 3>(0, 0); for (stl_vertex &v : its.vertices) v = (t * v.template cast<T>()).template cast<float>().eval(); } diff --git a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp index 4ef1fe71d..1a341d691 100644 --- a/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp +++ b/src/libnest2d/include/libnest2d/placers/nfpplacer.hpp @@ -546,7 +546,12 @@ public: inline explicit _NofitPolyPlacer(const BinType& bin): Base(bin), - norm_(std::sqrt(sl::area(bin))) {} + norm_(std::sqrt(sl::area(bin))) + { + // In order to not have items out of bin, it will be shrinked by an + // very little empiric offset value. + // sl::offset(bin_, 1e-5 * norm_); + } _NofitPolyPlacer(const _NofitPolyPlacer&) = default; _NofitPolyPlacer& operator=(const _NofitPolyPlacer&) = default; @@ -1179,7 +1184,14 @@ private: } void setInitialPosition(Item& item) { - Box&& bb = item.boundingBox(); + auto sh = item.rawShape(); + sl::translate(sh, item.translation()); + sl::rotate(sh, item.rotation()); + + Box bb = sl::boundingBox(sh); + bb.minCorner() += item.translation(); + bb.maxCorner() += item.translation(); + Vertex ci, cb; auto bbin = sl::boundingBox(bin_); diff --git a/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp b/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp index f904210aa..30ee7b627 100644 --- a/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp +++ b/src/libnest2d/include/libnest2d/selections/djd_heuristic.hpp @@ -550,16 +550,7 @@ public: return ret; }; - // Safety test: try to pack each item into an empty bin. If it fails - // then it should be removed from the not_packed list - { auto it = store_.begin(); - while (it != store_.end() && !this->stopcond_()) { - Placer p(bin); p.configure(pconfig); - if(!p.pack(*it, rem(it, store_))) { - it = store_.erase(it); - } else it++; - } - } + this->template remove_unpackable_items<Placer>(store_, bin, pconfig); int acounter = int(store_.size()); std::atomic_flag flg = ATOMIC_FLAG_INIT; diff --git a/src/libnest2d/include/libnest2d/selections/filler.hpp b/src/libnest2d/include/libnest2d/selections/filler.hpp index 19c44bfaa..3b6527d3e 100644 --- a/src/libnest2d/include/libnest2d/selections/filler.hpp +++ b/src/libnest2d/include/libnest2d/selections/filler.hpp @@ -30,7 +30,8 @@ public: TBin&& bin, PConfig&& pconfig = PConfig()) { - + using Placer = PlacementStrategyLike<TPlacer>; + store_.clear(); auto total = last-first; store_.reserve(total); @@ -57,10 +58,12 @@ public: auto sortfunc = [](Item& i1, Item& i2) { return i1.area() > i2.area(); }; - + + this->template remove_unpackable_items<Placer>(store_, bin, pconfig); + std::sort(store_.begin(), store_.end(), sortfunc); - PlacementStrategyLike<TPlacer> placer(bin); + Placer placer(bin); placer.configure(pconfig); auto it = store_.begin(); diff --git a/src/libnest2d/include/libnest2d/selections/firstfit.hpp b/src/libnest2d/include/libnest2d/selections/firstfit.hpp index 5585e565d..373e8b618 100644 --- a/src/libnest2d/include/libnest2d/selections/firstfit.hpp +++ b/src/libnest2d/include/libnest2d/selections/firstfit.hpp @@ -77,17 +77,8 @@ public: }; auto& cancelled = this->stopcond_; - - // Safety test: try to pack each item into an empty bin. If it fails - // then it should be removed from the list - { auto it = store_.begin(); - while (it != store_.end() && !cancelled()) { - Placer p(bin); p.configure(pconfig); - if(!p.pack(*it)) { - it = store_.erase(it); - } else it++; - } - } + + this->template remove_unpackable_items<Placer>(store_, bin, pconfig); auto it = store_.begin(); diff --git a/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp b/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp index 9ae92fe29..4bb2e72af 100644 --- a/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp +++ b/src/libnest2d/include/libnest2d/selections/selection_boilerplate.hpp @@ -25,6 +25,22 @@ public: inline void clear() { packed_bins_.clear(); } protected: + + template<class Placer, class Container, class Bin, class PCfg> + void remove_unpackable_items(Container &c, const Bin &bin, const PCfg& pcfg) + { + // Safety test: try to pack each item into an empty bin. If it fails + // then it should be removed from the list + auto it = c.begin(); + while (it != c.end() && !stopcond_()) { + Placer p{bin}; + p.configure(pcfg); + const Item& itm = *it; + Item cpy{itm}; + if (!p.pack(cpy)) it = c.erase(it); + else it++; + } + } PackGroup packed_bins_; ProgressFunction progress_ = [](unsigned){}; diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 34e07302a..52168c929 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -350,6 +350,12 @@ public: m_pck.configure(m_pconf); } + AutoArranger(const TBin & bin, + std::function<void(unsigned)> progressind, + std::function<bool(void)> stopcond) + : AutoArranger{bin, 0 /* no min distance */, progressind, stopcond} + {} + template<class It> inline void operator()(It from, It to) { m_rtree.clear(); m_item_count += size_t(to - from); @@ -553,13 +559,21 @@ BedShapeHint &BedShapeHint::operator=(const BedShapeHint &cpy) return *this; } +template<class Bin> void remove_large_items(std::vector<Item> &items, Bin &&bin) +{ + auto it = items.begin(); + while (it != items.end()) + sl::isInside(it->transformedShape(), bin) ? + ++it : it = items.erase(it); +} + template<class BinT> // Arrange for arbitrary bin type void _arrange( std::vector<Item> & shapes, std::vector<Item> & excludes, const BinT & bin, coord_t minobjd, - std::function<void(unsigned)> prind, + std::function<void(unsigned)> progressfn, std::function<bool()> stopfn) { // Integer ceiling the min distance from the bed perimeters @@ -569,16 +583,13 @@ void _arrange( auto corrected_bin = bin; sl::offset(corrected_bin, md); - AutoArranger<BinT> arranger{corrected_bin, 0, prind, stopfn}; + AutoArranger<BinT> arranger{corrected_bin, progressfn, stopfn}; auto infl = coord_t(std::ceil(minobjd / 2.0)); for (Item& itm : shapes) itm.inflate(infl); for (Item& itm : excludes) itm.inflate(infl); - auto it = excludes.begin(); - while (it != excludes.end()) - sl::isInside(it->transformedShape(), corrected_bin) ? - ++it : it = excludes.erase(it); + remove_large_items(excludes, corrected_bin); // If there is something on the plate if (!excludes.empty()) arranger.preload(excludes); @@ -674,7 +685,7 @@ void arrange(ArrangePolygons & arrangables, _arrange(items, fixeditems, Box::infinite(), min_obj_dist, pri, cfn); break; } - }; + } for(size_t i = 0; i < items.size(); ++i) { clppr::IntPoint tr = items[i].translation(); diff --git a/src/libslic3r/Config.hpp b/src/libslic3r/Config.hpp index ff5563226..c349ad3e1 100644 --- a/src/libslic3r/Config.hpp +++ b/src/libslic3r/Config.hpp @@ -117,9 +117,9 @@ public: 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 int getInt() const { throw std::runtime_error("Calling ConfigOption::getInt on a non-int ConfigOption"); return 0; } - virtual double getFloat() const { throw std::runtime_error("Calling ConfigOption::getFloat on a non-float ConfigOption"); return 0; } - virtual bool getBool() const { throw std::runtime_error("Calling ConfigOption::getBool on a non-boolean ConfigOption"); return 0; } + virtual int getInt() const { throw std::runtime_error("Calling ConfigOption::getInt on a non-int ConfigOption"); } + virtual double getFloat() const { throw std::runtime_error("Calling ConfigOption::getFloat on a non-float ConfigOption"); } + virtual bool getBool() const { throw std::runtime_error("Calling ConfigOption::getBool on a non-boolean ConfigOption"); } virtual void setInt(int /* val */) { throw std::runtime_error("Calling ConfigOption::setInt on a non-int ConfigOption"); } virtual bool operator==(const ConfigOption &rhs) const = 0; bool operator!=(const ConfigOption &rhs) const { return ! (*this == rhs); } @@ -204,6 +204,12 @@ public: // Is the value nil? That should only be possible if this->nullable(). virtual bool is_nil(size_t idx) const = 0; + // We just overloaded and hid two base class virtual methods. + // Let's show it was intentional (warnings). + using ConfigOption::set; + using ConfigOption::is_nil; + + protected: // Used to verify type compatibility when assigning to / from a scalar ConfigOption. ConfigOptionType scalar_type() const { return static_cast<ConfigOptionType>(this->type() - coVectorType); } @@ -544,7 +550,7 @@ public: static ConfigOptionType static_type() { return coInt; } ConfigOptionType type() const override { return static_type(); } int getInt() const override { return this->value; } - void setInt(int val) { this->value = val; } + void setInt(int val) override { this->value = val; } ConfigOption* clone() const override { return new ConfigOptionInt(*this); } bool operator==(const ConfigOptionInt &rhs) const { return this->value == rhs.value; } @@ -702,7 +708,7 @@ public: ConfigOption* clone() const override { return new ConfigOptionStrings(*this); } ConfigOptionStrings& operator=(const ConfigOption *opt) { this->set(opt); return *this; } bool operator==(const ConfigOptionStrings &rhs) const { return this->values == rhs.values; } - bool is_nil(size_t idx) const override { return false; } + bool is_nil(size_t) const override { return false; } std::string serialize() const override { @@ -917,7 +923,7 @@ public: ConfigOption* clone() const override { return new ConfigOptionPoints(*this); } ConfigOptionPoints& operator=(const ConfigOption *opt) { this->set(opt); return *this; } bool operator==(const ConfigOptionPoints &rhs) const { return this->values == rhs.values; } - bool is_nil(size_t idx) const override { return false; } + bool is_nil(size_t) const override { return false; } std::string serialize() const override { @@ -1502,7 +1508,7 @@ protected: // Both opt_key and value may be modified by handle_legacy(). // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy(). // handle_legacy() is called internally by set_deserialize(). - virtual void handle_legacy(t_config_option_key &opt_key, std::string &value) const {} + virtual void handle_legacy(t_config_option_key &/*opt_key*/, std::string &/*value*/) const {} public: // Non-virtual methods: @@ -1576,7 +1582,7 @@ public: DynamicConfig() {} DynamicConfig(const DynamicConfig& other) { *this = other; } DynamicConfig(DynamicConfig&& other) : options(std::move(other.options)) { other.options.clear(); } - virtual ~DynamicConfig() { clear(); } + virtual ~DynamicConfig() override { clear(); } // Copy a content of one DynamicConfig to another DynamicConfig. // If rhs.def() is not null, then it has to be equal to this->def(). @@ -1697,14 +1703,14 @@ public: const std::string& opt_string(const t_config_option_key &opt_key, unsigned int idx) const { return const_cast<DynamicConfig*>(this)->opt_string(opt_key, idx); } double& opt_float(const t_config_option_key &opt_key) { return this->option<ConfigOptionFloat>(opt_key)->value; } - const double opt_float(const t_config_option_key &opt_key) const { return dynamic_cast<const ConfigOptionFloat*>(this->option(opt_key))->value; } + const double& opt_float(const t_config_option_key &opt_key) const { return dynamic_cast<const ConfigOptionFloat*>(this->option(opt_key))->value; } double& opt_float(const t_config_option_key &opt_key, unsigned int idx) { return this->option<ConfigOptionFloats>(opt_key)->get_at(idx); } - const double opt_float(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast<const ConfigOptionFloats*>(this->option(opt_key))->get_at(idx); } + const double& opt_float(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast<const ConfigOptionFloats*>(this->option(opt_key))->get_at(idx); } int& opt_int(const t_config_option_key &opt_key) { return this->option<ConfigOptionInt>(opt_key)->value; } - const int opt_int(const t_config_option_key &opt_key) const { return dynamic_cast<const ConfigOptionInt*>(this->option(opt_key))->value; } + int opt_int(const t_config_option_key &opt_key) const { return dynamic_cast<const ConfigOptionInt*>(this->option(opt_key))->value; } int& opt_int(const t_config_option_key &opt_key, unsigned int idx) { return this->option<ConfigOptionInts>(opt_key)->get_at(idx); } - const int opt_int(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast<const ConfigOptionInts*>(this->option(opt_key))->get_at(idx); } + int opt_int(const t_config_option_key &opt_key, unsigned int idx) const { return dynamic_cast<const ConfigOptionInts*>(this->option(opt_key))->get_at(idx); } template<typename ENUM> ENUM opt_enum(const t_config_option_key &opt_key) const { return (ENUM)dynamic_cast<const ConfigOptionEnumGeneric*>(this->option(opt_key))->value; } diff --git a/src/libslic3r/ExtrusionEntity.cpp b/src/libslic3r/ExtrusionEntity.cpp index 92f0d3669..7f57b78af 100644 --- a/src/libslic3r/ExtrusionEntity.cpp +++ b/src/libslic3r/ExtrusionEntity.cpp @@ -8,46 +8,39 @@ #include <limits> #include <sstream> +#define L(s) (s) + namespace Slic3r { -void -ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const +void ExtrusionPath::intersect_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const { this->_inflate_collection(intersection_pl(this->polyline, collection), retval); } -void -ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const +void ExtrusionPath::subtract_expolygons(const ExPolygonCollection &collection, ExtrusionEntityCollection* retval) const { this->_inflate_collection(diff_pl(this->polyline, collection), retval); } -void -ExtrusionPath::clip_end(double distance) +void ExtrusionPath::clip_end(double distance) { this->polyline.clip_end(distance); } -void -ExtrusionPath::simplify(double tolerance) +void ExtrusionPath::simplify(double tolerance) { this->polyline.simplify(tolerance); } -double -ExtrusionPath::length() const +double ExtrusionPath::length() const { return this->polyline.length(); } -void -ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const +void ExtrusionPath::_inflate_collection(const Polylines &polylines, ExtrusionEntityCollection* collection) const { - for (Polylines::const_iterator it = polylines.begin(); it != polylines.end(); ++it) { - ExtrusionPath* path = this->clone(); - path->polyline = *it; - collection->entities.push_back(path); - } + for (const Polyline &polyline : polylines) + collection->entities.emplace_back(new ExtrusionPath(polyline, *this)); } void ExtrusionPath::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const @@ -65,36 +58,36 @@ void ExtrusionPath::polygons_covered_by_spacing(Polygons &out, const float scale void ExtrusionMultiPath::reverse() { - for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path) - path->reverse(); + for (ExtrusionPath &path : this->paths) + path.reverse(); std::reverse(this->paths.begin(), this->paths.end()); } double ExtrusionMultiPath::length() const { double len = 0; - for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) - len += path->polyline.length(); + for (const ExtrusionPath &path : this->paths) + len += path.polyline.length(); return len; } void ExtrusionMultiPath::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const { - for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) - path->polygons_covered_by_width(out, scaled_epsilon); + for (const ExtrusionPath &path : this->paths) + path.polygons_covered_by_width(out, scaled_epsilon); } void ExtrusionMultiPath::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const { - for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) - path->polygons_covered_by_spacing(out, scaled_epsilon); + for (const ExtrusionPath &path : this->paths) + path.polygons_covered_by_spacing(out, scaled_epsilon); } double ExtrusionMultiPath::min_mm3_per_mm() const { double min_mm3_per_mm = std::numeric_limits<double>::max(); - for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) - min_mm3_per_mm = std::min(min_mm3_per_mm, path->mm3_per_mm); + for (const ExtrusionPath &path : this->paths) + min_mm3_per_mm = std::min(min_mm3_per_mm, path.mm3_per_mm); return min_mm3_per_mm; } @@ -119,52 +112,46 @@ Polyline ExtrusionMultiPath::as_polyline() const return out; } -bool -ExtrusionLoop::make_clockwise() +bool ExtrusionLoop::make_clockwise() { bool was_ccw = this->polygon().is_counter_clockwise(); if (was_ccw) this->reverse(); return was_ccw; } -bool -ExtrusionLoop::make_counter_clockwise() +bool ExtrusionLoop::make_counter_clockwise() { bool was_cw = this->polygon().is_clockwise(); if (was_cw) this->reverse(); return was_cw; } -void -ExtrusionLoop::reverse() +void ExtrusionLoop::reverse() { - for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path) - path->reverse(); + for (ExtrusionPath &path : this->paths) + path.reverse(); std::reverse(this->paths.begin(), this->paths.end()); } -Polygon -ExtrusionLoop::polygon() const +Polygon ExtrusionLoop::polygon() const { Polygon polygon; - for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) { + for (const ExtrusionPath &path : this->paths) { // for each polyline, append all points except the last one (because it coincides with the first one of the next polyline) - polygon.points.insert(polygon.points.end(), path->polyline.points.begin(), path->polyline.points.end()-1); + polygon.points.insert(polygon.points.end(), path.polyline.points.begin(), path.polyline.points.end()-1); } return polygon; } -double -ExtrusionLoop::length() const +double ExtrusionLoop::length() const { double len = 0; - for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) - len += path->polyline.length(); + for (const ExtrusionPath &path : this->paths) + len += path.polyline.length(); return len; } -bool -ExtrusionLoop::split_at_vertex(const Point &point) +bool ExtrusionLoop::split_at_vertex(const Point &point) { for (ExtrusionPaths::iterator path = this->paths.begin(); path != this->paths.end(); ++path) { int idx = path->polyline.find_point(point); @@ -218,18 +205,18 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang) Point p_non_overhang; size_t path_idx_non_overhang = 0; double min_non_overhang = std::numeric_limits<double>::max(); - for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) { - Point p_tmp = point.projection_onto(path->polyline); + for (const ExtrusionPath &path : this->paths) { + Point p_tmp = point.projection_onto(path.polyline); double dist = (p_tmp - point).cast<double>().norm(); if (dist < min) { p = p_tmp; min = dist; - path_idx = path - this->paths.begin(); + path_idx = &path - &this->paths.front(); } - if (prefer_non_overhang && ! is_bridge(path->role()) && dist < min_non_overhang) { + if (prefer_non_overhang && ! is_bridge(path.role()) && dist < min_non_overhang) { p_non_overhang = p_tmp; min_non_overhang = dist; - path_idx_non_overhang = path - this->paths.begin(); + path_idx_non_overhang = &path - &this->paths.front(); } } if (prefer_non_overhang && min_non_overhang != std::numeric_limits<double>::max()) { @@ -265,8 +252,7 @@ void ExtrusionLoop::split_at(const Point &point, bool prefer_non_overhang) this->split_at_vertex(p); } -void -ExtrusionLoop::clip_end(double distance, ExtrusionPaths* paths) const +void ExtrusionLoop::clip_end(double distance, ExtrusionPaths* paths) const { *paths = this->paths; @@ -283,15 +269,14 @@ ExtrusionLoop::clip_end(double distance, ExtrusionPaths* paths) const } } -bool -ExtrusionLoop::has_overhang_point(const Point &point) const +bool ExtrusionLoop::has_overhang_point(const Point &point) const { - for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) { - int pos = path->polyline.find_point(point); + for (const ExtrusionPath &path : this->paths) { + int pos = path.polyline.find_point(point); if (pos != -1) { // point belongs to this path // we consider it overhang only if it's not an endpoint - return (is_bridge(path->role()) && pos > 0 && pos != (int)(path->polyline.points.size())-1); + return (is_bridge(path.role()) && pos > 0 && pos != (int)(path.polyline.points.size())-1); } } return false; @@ -299,23 +284,46 @@ ExtrusionLoop::has_overhang_point(const Point &point) const void ExtrusionLoop::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const { - for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) - path->polygons_covered_by_width(out, scaled_epsilon); + for (const ExtrusionPath &path : this->paths) + path.polygons_covered_by_width(out, scaled_epsilon); } void ExtrusionLoop::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const { - for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) - path->polygons_covered_by_spacing(out, scaled_epsilon); + for (const ExtrusionPath &path : this->paths) + path.polygons_covered_by_spacing(out, scaled_epsilon); } -double -ExtrusionLoop::min_mm3_per_mm() const +double ExtrusionLoop::min_mm3_per_mm() const { double min_mm3_per_mm = std::numeric_limits<double>::max(); - for (ExtrusionPaths::const_iterator path = this->paths.begin(); path != this->paths.end(); ++path) - min_mm3_per_mm = std::min(min_mm3_per_mm, path->mm3_per_mm); + for (const ExtrusionPath &path : this->paths) + min_mm3_per_mm = std::min(min_mm3_per_mm, path.mm3_per_mm); return min_mm3_per_mm; } + +std::string ExtrusionEntity::role_to_string(ExtrusionRole role) +{ + switch (role) { + case erNone : return L("None"); + case erPerimeter : return L("Perimeter"); + case erExternalPerimeter : return L("External perimeter"); + case erOverhangPerimeter : return L("Overhang perimeter"); + case erInternalInfill : return L("Internal infill"); + case erSolidInfill : return L("Solid infill"); + case erTopSolidInfill : return L("Top solid infill"); + case erBridgeInfill : return L("Bridge infill"); + case erGapFill : return L("Gap fill"); + case erSkirt : return L("Skirt"); + case erSupportMaterial : return L("Support material"); + case erSupportMaterialInterface : return L("Support material interface"); + case erWipeTower : return L("Wipe tower"); + case erCustom : return L("Custom"); + case erMixed : return L("Mixed"); + default : assert(false); + } + return ""; +} + } diff --git a/src/libslic3r/ExtrusionEntity.hpp b/src/libslic3r/ExtrusionEntity.hpp index b32943b7d..ce52ae152 100644 --- a/src/libslic3r/ExtrusionEntity.hpp +++ b/src/libslic3r/ExtrusionEntity.hpp @@ -11,7 +11,7 @@ class ExPolygonCollection; class ExtrusionEntityCollection; class Extruder; -/* Each ExtrusionRole value identifies a distinct set of { extruder, speed } */ +// Each ExtrusionRole value identifies a distinct set of { extruder, speed } enum ExtrusionRole { erNone, erPerimeter, @@ -29,9 +29,17 @@ enum ExtrusionRole { erCustom, // Extrusion role for a collection with multiple extrusion roles. erMixed, - erCount, + erCount }; +// Special flags describing loop +enum ExtrusionLoopRole { + elrDefault, + elrContourInternalPerimeter, + elrSkirt, +}; + + inline bool is_perimeter(ExtrusionRole role) { return role == erPerimeter @@ -59,13 +67,6 @@ inline bool is_bridge(ExtrusionRole role) { || role == erOverhangPerimeter; } -/* Special flags describing loop */ -enum ExtrusionLoopRole { - elrDefault, - elrContourInternalPerimeter, - elrSkirt, -}; - class ExtrusionEntity { public: @@ -74,7 +75,9 @@ public: virtual bool is_loop() const { return false; } virtual bool can_reverse() const { return true; } virtual ExtrusionEntity* clone() const = 0; - virtual ~ExtrusionEntity() {}; + // Create a new object, initialize it with this object using the move semantics. + virtual ExtrusionEntity* clone_move() = 0; + virtual ~ExtrusionEntity() {} virtual void reverse() = 0; virtual Point first_point() const = 0; virtual Point last_point() const = 0; @@ -96,6 +99,8 @@ public: virtual Polylines as_polylines() const { Polylines dst; this->collect_polylines(dst); return dst; } virtual double length() const = 0; virtual double total_volume() const = 0; + + static std::string role_to_string(ExtrusionRole role); }; typedef std::vector<ExtrusionEntity*> ExtrusionEntitiesPtr; @@ -108,26 +113,30 @@ public: double mm3_per_mm; // Width of the extrusion, used for visualization purposes. float width; - // Height of the extrusion, used for visualization purposed. + // Height of the extrusion, used for visualization purposes. float height; - // Feedrate of the extrusion, used for visualization purposed. + // Feedrate of the extrusion, used for visualization purposes. float feedrate; - // Id of the extruder, used for visualization purposed. + // Id of the extruder, used for visualization purposes. unsigned int extruder_id; - // Id of the color, used for visualization purposed in the color printing case. + // Id of the color, used for visualization purposes in the color printing case. unsigned int cp_color_id; - ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), feedrate(0.0f), extruder_id(0), cp_color_id(0), m_role(role) {}; - ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), feedrate(0.0f), extruder_id(0), cp_color_id(0), m_role(role) {}; + ExtrusionPath(ExtrusionRole role) : mm3_per_mm(-1), width(-1), height(-1), feedrate(0.0f), extruder_id(0), cp_color_id(0), m_role(role) {} + ExtrusionPath(ExtrusionRole role, double mm3_per_mm, float width, float height) : mm3_per_mm(mm3_per_mm), width(width), height(height), feedrate(0.0f), extruder_id(0), cp_color_id(0), m_role(role) {} ExtrusionPath(const ExtrusionPath &rhs) : polyline(rhs.polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} - ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} + ExtrusionPath(const Polyline &polyline, const ExtrusionPath &rhs) : polyline(polyline), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} + ExtrusionPath(ExtrusionPath &&rhs) : polyline(std::move(rhs.polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} + ExtrusionPath(Polyline &&polyline, const ExtrusionPath &rhs) : polyline(std::move(polyline)), mm3_per_mm(rhs.mm3_per_mm), width(rhs.width), height(rhs.height), feedrate(rhs.feedrate), extruder_id(rhs.extruder_id), cp_color_id(rhs.cp_color_id), m_role(rhs.m_role) {} // ExtrusionPath(ExtrusionRole role, const Flow &flow) : m_role(role), mm3_per_mm(flow.mm3_per_mm()), width(flow.width), height(flow.height), feedrate(0.0f), extruder_id(0) {}; - ExtrusionPath& operator=(const ExtrusionPath &rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->cp_color_id = rhs.cp_color_id, this->polyline = rhs.polyline; return *this; } - ExtrusionPath& operator=(ExtrusionPath &&rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate, this->extruder_id = rhs.extruder_id, this->cp_color_id = rhs.cp_color_id, this->polyline = std::move(rhs.polyline); return *this; } + ExtrusionPath& operator=(const ExtrusionPath &rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate; this->extruder_id = rhs.extruder_id; this->cp_color_id = rhs.cp_color_id; this->polyline = rhs.polyline; return *this; } + ExtrusionPath& operator=(ExtrusionPath &&rhs) { m_role = rhs.m_role; this->mm3_per_mm = rhs.mm3_per_mm; this->width = rhs.width; this->height = rhs.height; this->feedrate = rhs.feedrate; this->extruder_id = rhs.extruder_id; this->cp_color_id = rhs.cp_color_id; this->polyline = std::move(rhs.polyline); return *this; } - ExtrusionPath* clone() const { return new ExtrusionPath (*this); } - void reverse() { this->polyline.reverse(); } + ExtrusionEntity* clone() const override { return new ExtrusionPath(*this); } + // Create a new object, initialize it with this object using the move semantics. + ExtrusionEntity* clone_move() override { return new ExtrusionPath(std::move(*this)); } + void reverse() override { this->polyline.reverse(); } Point first_point() const override { return this->polyline.points.front(); } Point last_point() const override { return this->polyline.points.back(); } size_t size() const { return this->polyline.size(); } @@ -145,18 +154,18 @@ public: ExtrusionRole role() const override { return m_role; } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. - void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const; + void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override; // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion spacing. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. // Useful to calculate area of an infill, which has been really filled in by a 100% rectilinear infill. - void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const; + void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const override; Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const { Polygons out; this->polygons_covered_by_width(out, scaled_epsilon); return out; } Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const { Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; } // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. - double min_mm3_per_mm() const { return this->mm3_per_mm; } - Polyline as_polyline() const { return this->polyline; } + double min_mm3_per_mm() const override { return this->mm3_per_mm; } + Polyline as_polyline() const override { return this->polyline; } void collect_polylines(Polylines &dst) const override { if (! this->polyline.empty()) dst.emplace_back(this->polyline); } double total_volume() const override { return mm3_per_mm * unscale<double>(length()); } @@ -174,37 +183,39 @@ class ExtrusionMultiPath : public ExtrusionEntity public: ExtrusionPaths paths; - ExtrusionMultiPath() {}; + ExtrusionMultiPath() {} ExtrusionMultiPath(const ExtrusionMultiPath &rhs) : paths(rhs.paths) {} ExtrusionMultiPath(ExtrusionMultiPath &&rhs) : paths(std::move(rhs.paths)) {} - ExtrusionMultiPath(const ExtrusionPaths &paths) : paths(paths) {}; + ExtrusionMultiPath(const ExtrusionPaths &paths) : paths(paths) {} ExtrusionMultiPath(const ExtrusionPath &path) { this->paths.push_back(path); } ExtrusionMultiPath& operator=(const ExtrusionMultiPath &rhs) { this->paths = rhs.paths; return *this; } ExtrusionMultiPath& operator=(ExtrusionMultiPath &&rhs) { this->paths = std::move(rhs.paths); return *this; } - bool is_loop() const { return false; } - bool can_reverse() const { return true; } - ExtrusionMultiPath* clone() const { return new ExtrusionMultiPath(*this); } - void reverse(); + bool is_loop() const override { return false; } + bool can_reverse() const override { return true; } + ExtrusionEntity* clone() const override { return new ExtrusionMultiPath(*this); } + // Create a new object, initialize it with this object using the move semantics. + ExtrusionEntity* clone_move() override { return new ExtrusionMultiPath(std::move(*this)); } + void reverse() override; Point first_point() const override { return this->paths.front().polyline.points.front(); } Point last_point() const override { return this->paths.back().polyline.points.back(); } double length() const override; ExtrusionRole role() const override { return this->paths.empty() ? erNone : this->paths.front().role(); } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. - void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const; + void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override; // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion spacing. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. // Useful to calculate area of an infill, which has been really filled in by a 100% rectilinear infill. - void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const; + void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const override; Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const { Polygons out; this->polygons_covered_by_width(out, scaled_epsilon); return out; } Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const { Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; } // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. - double min_mm3_per_mm() const; - Polyline as_polyline() const; + double min_mm3_per_mm() const override; + Polyline as_polyline() const override; void collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); } double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; } }; @@ -215,19 +226,21 @@ class ExtrusionLoop : public ExtrusionEntity public: ExtrusionPaths paths; - ExtrusionLoop(ExtrusionLoopRole role = elrDefault) : m_loop_role(role) {}; - ExtrusionLoop(const ExtrusionPaths &paths, ExtrusionLoopRole role = elrDefault) : paths(paths), m_loop_role(role) {}; - ExtrusionLoop(ExtrusionPaths &&paths, ExtrusionLoopRole role = elrDefault) : paths(std::move(paths)), m_loop_role(role) {}; + ExtrusionLoop(ExtrusionLoopRole role = elrDefault) : m_loop_role(role) {} + ExtrusionLoop(const ExtrusionPaths &paths, ExtrusionLoopRole role = elrDefault) : paths(paths), m_loop_role(role) {} + ExtrusionLoop(ExtrusionPaths &&paths, ExtrusionLoopRole role = elrDefault) : paths(std::move(paths)), m_loop_role(role) {} ExtrusionLoop(const ExtrusionPath &path, ExtrusionLoopRole role = elrDefault) : m_loop_role(role) - { this->paths.push_back(path); }; + { this->paths.push_back(path); } ExtrusionLoop(const ExtrusionPath &&path, ExtrusionLoopRole role = elrDefault) : m_loop_role(role) - { this->paths.emplace_back(std::move(path)); }; - bool is_loop() const { return true; } - bool can_reverse() const { return false; } - ExtrusionLoop* clone() const { return new ExtrusionLoop (*this); } + { this->paths.emplace_back(std::move(path)); } + bool is_loop() const override{ return true; } + bool can_reverse() const override { return false; } + ExtrusionEntity* clone() const override{ return new ExtrusionLoop (*this); } + // Create a new object, initialize it with this object using the move semantics. + ExtrusionEntity* clone_move() override { return new ExtrusionLoop(std::move(*this)); } bool make_clockwise(); bool make_counter_clockwise(); - void reverse(); + void reverse() override; Point first_point() const override { return this->paths.front().polyline.points.front(); } Point last_point() const override { assert(first_point() == this->paths.back().polyline.points.back()); return first_point(); } Polygon polygon() const; @@ -242,21 +255,23 @@ public: ExtrusionLoopRole loop_role() const { return m_loop_role; } // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion width. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. - void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const; + void polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const override; // Produce a list of 2D polygons covered by the extruded paths, offsetted by the extrusion spacing. // Increase the offset by scaled_epsilon to achieve an overlap, so a union will produce no gaps. // Useful to calculate area of an infill, which has been really filled in by a 100% rectilinear infill. - void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const; + void polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const override; Polygons polygons_covered_by_width(const float scaled_epsilon = 0.f) const { Polygons out; this->polygons_covered_by_width(out, scaled_epsilon); return out; } Polygons polygons_covered_by_spacing(const float scaled_epsilon = 0.f) const { Polygons out; this->polygons_covered_by_spacing(out, scaled_epsilon); return out; } // Minimum volumetric velocity of this extrusion entity. Used by the constant nozzle pressure algorithm. - double min_mm3_per_mm() const; - Polyline as_polyline() const { return this->polygon().split_at_first_point(); } + double min_mm3_per_mm() const override; + Polyline as_polyline() const override { return this->polygon().split_at_first_point(); } void collect_polylines(Polylines &dst) const override { Polyline pl = this->as_polyline(); if (! pl.empty()) dst.emplace_back(std::move(pl)); } double total_volume() const override { double volume =0.; for (const auto& path : paths) volume += path.total_volume(); return volume; } + //static inline std::string role_to_string(ExtrusionLoopRole role); + private: ExtrusionLoopRole m_loop_role; }; diff --git a/src/libslic3r/ExtrusionEntityCollection.cpp b/src/libslic3r/ExtrusionEntityCollection.cpp index 7a086bcbf..9ae116c47 100644 --- a/src/libslic3r/ExtrusionEntityCollection.cpp +++ b/src/libslic3r/ExtrusionEntityCollection.cpp @@ -21,8 +21,7 @@ ExtrusionEntityCollection& ExtrusionEntityCollection::operator= (const Extrusion return *this; } -void -ExtrusionEntityCollection::swap(ExtrusionEntityCollection &c) +void ExtrusionEntityCollection::swap(ExtrusionEntityCollection &c) { std::swap(this->entities, c.entities); std::swap(this->orig_indices, c.orig_indices); @@ -39,15 +38,14 @@ void ExtrusionEntityCollection::clear() ExtrusionEntityCollection::operator ExtrusionPaths() const { ExtrusionPaths paths; - for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) { - if (const ExtrusionPath* path = dynamic_cast<const ExtrusionPath*>(*it)) + for (const ExtrusionEntity *ptr : this->entities) { + if (const ExtrusionPath *path = dynamic_cast<const ExtrusionPath*>(ptr)) paths.push_back(*path); } return paths; } -ExtrusionEntityCollection* -ExtrusionEntityCollection::clone() const +ExtrusionEntity* ExtrusionEntityCollection::clone() const { ExtrusionEntityCollection* coll = new ExtrusionEntityCollection(*this); for (size_t i = 0; i < coll->entities.size(); ++i) @@ -55,41 +53,36 @@ ExtrusionEntityCollection::clone() const return coll; } -void -ExtrusionEntityCollection::reverse() +void ExtrusionEntityCollection::reverse() { - for (ExtrusionEntitiesPtr::iterator it = this->entities.begin(); it != this->entities.end(); ++it) { + for (ExtrusionEntity *ptr : this->entities) // Don't reverse it if it's a loop, as it doesn't change anything in terms of elements ordering // and caller might rely on winding order - if (!(*it)->is_loop()) (*it)->reverse(); - } + if (! ptr->is_loop()) + ptr->reverse(); std::reverse(this->entities.begin(), this->entities.end()); } -void -ExtrusionEntityCollection::replace(size_t i, const ExtrusionEntity &entity) +void ExtrusionEntityCollection::replace(size_t i, const ExtrusionEntity &entity) { delete this->entities[i]; this->entities[i] = entity.clone(); } -void -ExtrusionEntityCollection::remove(size_t i) +void ExtrusionEntityCollection::remove(size_t i) { delete this->entities[i]; this->entities.erase(this->entities.begin() + i); } -ExtrusionEntityCollection -ExtrusionEntityCollection::chained_path(bool no_reverse, ExtrusionRole role) const +ExtrusionEntityCollection ExtrusionEntityCollection::chained_path(bool no_reverse, ExtrusionRole role) const { ExtrusionEntityCollection coll; this->chained_path(&coll, no_reverse, role); return coll; } -void -ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role, std::vector<size_t>* orig_indices) const +void ExtrusionEntityCollection::chained_path(ExtrusionEntityCollection* retval, bool no_reverse, ExtrusionRole role, std::vector<size_t>* orig_indices) const { if (this->entities.empty()) return; this->chained_path_from(this->entities.front()->first_point(), retval, no_reverse, role, orig_indices); @@ -108,6 +101,7 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt *retval = *this; return; } + retval->entities.reserve(this->entities.size()); retval->orig_indices.reserve(this->entities.size()); @@ -115,10 +109,10 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt std::map<ExtrusionEntity*,size_t> indices_map; ExtrusionEntitiesPtr my_paths; - for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) { + for (ExtrusionEntity * const &entity_src : this->entities) { if (role != erMixed) { // The caller wants only paths with a specific extrusion role. - auto role2 = (*it)->role(); + auto role2 = entity_src->role(); if (role != role2) { // This extrusion entity does not match the role asked. assert(role2 != erMixed); @@ -126,32 +120,30 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt } } - ExtrusionEntity* entity = (*it)->clone(); + ExtrusionEntity *entity = entity_src->clone(); my_paths.push_back(entity); - if (orig_indices != NULL) indices_map[entity] = it - this->entities.begin(); + if (orig_indices != nullptr) + indices_map[entity] = &entity_src - &this->entities.front(); } Points endpoints; - for (ExtrusionEntitiesPtr::iterator it = my_paths.begin(); it != my_paths.end(); ++it) { - endpoints.push_back((*it)->first_point()); - if (no_reverse || !(*it)->can_reverse()) { - endpoints.push_back((*it)->first_point()); - } else { - endpoints.push_back((*it)->last_point()); - } + for (const ExtrusionEntity *entity : my_paths) { + endpoints.push_back(entity->first_point()); + endpoints.push_back((no_reverse || ! entity->can_reverse()) ? + entity->first_point() : entity->last_point()); } - while (!my_paths.empty()) { + while (! my_paths.empty()) { // find nearest point int start_index = start_near.nearest_point_index(endpoints); int path_index = start_index/2; ExtrusionEntity* entity = my_paths.at(path_index); // never reverse loops, since it's pointless for chained path and callers might depend on orientation - if (start_index % 2 && !no_reverse && entity->can_reverse()) { + if (start_index % 2 && !no_reverse && entity->can_reverse()) entity->reverse(); - } retval->entities.push_back(my_paths.at(path_index)); - if (orig_indices != NULL) orig_indices->push_back(indices_map[entity]); + if (orig_indices != nullptr) + orig_indices->push_back(indices_map[entity]); my_paths.erase(my_paths.begin() + path_index); endpoints.erase(endpoints.begin() + 2*path_index, endpoints.begin() + 2*path_index + 2); start_near = retval->entities.back()->last_point(); @@ -160,60 +152,50 @@ void ExtrusionEntityCollection::chained_path_from(Point start_near, ExtrusionEnt void ExtrusionEntityCollection::polygons_covered_by_width(Polygons &out, const float scaled_epsilon) const { - for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) - (*it)->polygons_covered_by_width(out, scaled_epsilon); + for (const ExtrusionEntity *entity : this->entities) + entity->polygons_covered_by_width(out, scaled_epsilon); } void ExtrusionEntityCollection::polygons_covered_by_spacing(Polygons &out, const float scaled_epsilon) const { - for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) - (*it)->polygons_covered_by_spacing(out, scaled_epsilon); + for (const ExtrusionEntity *entity : this->entities) + entity->polygons_covered_by_spacing(out, scaled_epsilon); } -/* Recursively count paths and loops contained in this collection */ -size_t -ExtrusionEntityCollection::items_count() const +// Recursively count paths and loops contained in this collection. +size_t ExtrusionEntityCollection::items_count() const { size_t count = 0; - for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) { - if ((*it)->is_collection()) { - ExtrusionEntityCollection* collection = dynamic_cast<ExtrusionEntityCollection*>(*it); - count += collection->items_count(); - } else { - ++count; - } - } + for (const ExtrusionEntity *entity : this->entities) + if (entity->is_collection()) + count += static_cast<const ExtrusionEntityCollection*>(entity)->items_count(); + else + ++ count; return count; } -/* Returns a single vector of pointers to all non-collection items contained in this one */ -void -ExtrusionEntityCollection::flatten(ExtrusionEntityCollection* retval) const +// Returns a single vector of pointers to all non-collection items contained in this one. +void ExtrusionEntityCollection::flatten(ExtrusionEntityCollection* retval) const { - for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) { - if ((*it)->is_collection()) { - ExtrusionEntityCollection* collection = dynamic_cast<ExtrusionEntityCollection*>(*it); - retval->append(collection->flatten().entities); - } else { - retval->append(**it); - } - } + for (const ExtrusionEntity *entity : this->entities) + if (entity->is_collection()) + retval->append(static_cast<const ExtrusionEntityCollection*>(entity)->flatten().entities); + else + retval->append(*entity); } -ExtrusionEntityCollection -ExtrusionEntityCollection::flatten() const +ExtrusionEntityCollection ExtrusionEntityCollection::flatten() const { ExtrusionEntityCollection coll; this->flatten(&coll); return coll; } -double -ExtrusionEntityCollection::min_mm3_per_mm() const +double ExtrusionEntityCollection::min_mm3_per_mm() const { double min_mm3_per_mm = std::numeric_limits<double>::max(); - for (ExtrusionEntitiesPtr::const_iterator it = this->entities.begin(); it != this->entities.end(); ++it) - min_mm3_per_mm = std::min(min_mm3_per_mm, (*it)->min_mm3_per_mm()); + for (const ExtrusionEntity *entity : this->entities) + min_mm3_per_mm = std::min(min_mm3_per_mm, entity->min_mm3_per_mm()); return min_mm3_per_mm; } diff --git a/src/libslic3r/ExtrusionEntityCollection.hpp b/src/libslic3r/ExtrusionEntityCollection.hpp index 230c04160..4fe964ee1 100644 --- a/src/libslic3r/ExtrusionEntityCollection.hpp +++ b/src/libslic3r/ExtrusionEntityCollection.hpp @@ -9,7 +9,10 @@ namespace Slic3r { class ExtrusionEntityCollection : public ExtrusionEntity { public: - ExtrusionEntityCollection* clone() const; + ExtrusionEntity* clone() const override; + // Create a new object, initialize it with this object using the move semantics. + ExtrusionEntity* clone_move() override { return new ExtrusionEntityCollection(std::move(*this)); } + ExtrusionEntitiesPtr entities; // we own these entities std::vector<size_t> orig_indices; // handy for XS bool no_sort; @@ -36,11 +39,12 @@ public: bool empty() const { return this->entities.empty(); }; void clear(); void swap (ExtrusionEntityCollection &c); - void append(const ExtrusionEntity &entity) { this->entities.push_back(entity.clone()); } - void append(const ExtrusionEntitiesPtr &entities) { + void append(const ExtrusionEntity &entity) { this->entities.emplace_back(entity.clone()); } + void append(ExtrusionEntity &&entity) { this->entities.emplace_back(entity.clone_move()); } + void append(const ExtrusionEntitiesPtr &entities) { this->entities.reserve(this->entities.size() + entities.size()); - for (ExtrusionEntitiesPtr::const_iterator ptr = entities.begin(); ptr != entities.end(); ++ptr) - this->entities.push_back((*ptr)->clone()); + for (const ExtrusionEntity *ptr : entities) + this->entities.emplace_back(ptr->clone()); } void append(ExtrusionEntitiesPtr &&src) { if (entities.empty()) diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index fbdef29b9..ad308adab 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -13,103 +13,203 @@ namespace Slic3r { -struct SurfaceGroupAttrib +struct SurfaceFillParams { - SurfaceGroupAttrib() : is_solid(false), flow_width(0.f), pattern(-1) {} - bool operator==(const SurfaceGroupAttrib &other) const - { return is_solid == other.is_solid && flow_width == other.flow_width && pattern == other.pattern; } - bool is_solid; - float flow_width; - // pattern is of type InfillPattern, -1 for an unset pattern. - int pattern; + SurfaceFillParams() : flow(0.f, 0.f, 0.f, false) { memset(this, 0, sizeof(*this)); } + // Zero based extruder ID. + unsigned int extruder; + // Infill pattern, adjusted for the density etc. + InfillPattern pattern; + + // FillBase + // in unscaled coordinates + coordf_t spacing; + // infill / perimeter overlap, in unscaled coordinates + coordf_t overlap; + // Angle as provided by the region config, in radians. + float angle; + // Non-negative for a bridge. + float bridge_angle; + + // FillParams + float density; + // Don't connect the fill lines around the inner perimeter. + bool dont_connect; + // Don't adjust spacing to fill the space evenly. + bool dont_adjust; + + // width, height of extrusion, nozzle diameter, is bridge + // For the output, for fill generator. + Flow flow; + + // For the output + ExtrusionRole extrusion_role; + + // Various print settings? + + // Index of this entry in a linear vector. + size_t idx; + + + bool operator<(const SurfaceFillParams &rhs) const { +#define RETURN_COMPARE_NON_EQUAL(KEY) if (this->KEY < rhs.KEY) return true; if (this->KEY > rhs.KEY) return false; +#define RETURN_COMPARE_NON_EQUAL_TYPED(TYPE, KEY) if (TYPE(this->KEY) < TYPE(rhs.KEY)) return true; if (TYPE(this->KEY) > TYPE(rhs.KEY)) return false; + + // Sort first by decreasing bridging angle, so that the bridges are processed with priority when trimming one layer by the other. + if (this->bridge_angle > rhs.bridge_angle) return true; + if (this->bridge_angle < rhs.bridge_angle) return false; + + RETURN_COMPARE_NON_EQUAL(extruder); + RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, pattern); + RETURN_COMPARE_NON_EQUAL(spacing); + RETURN_COMPARE_NON_EQUAL(overlap); + RETURN_COMPARE_NON_EQUAL(angle); + RETURN_COMPARE_NON_EQUAL(density); + RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_connect); + RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, dont_adjust); + RETURN_COMPARE_NON_EQUAL(flow.width); + RETURN_COMPARE_NON_EQUAL(flow.height); + RETURN_COMPARE_NON_EQUAL(flow.nozzle_diameter); + RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, flow.bridge); + RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, extrusion_role); + return false; + } + + bool operator==(const SurfaceFillParams &rhs) const { + return this->extruder == rhs.extruder && + this->pattern == rhs.pattern && + this->pattern == rhs.pattern && + this->spacing == rhs.spacing && + this->overlap == rhs.overlap && + this->angle == rhs.angle && + this->density == rhs.density && + this->dont_connect == rhs.dont_connect && + this->dont_adjust == rhs.dont_adjust && + this->flow == rhs.flow && + this->extrusion_role == rhs.extrusion_role; + } }; -// Generate infills for Slic3r::Layer::Region. -// The Slic3r::Layer::Region at this point of time may contain -// surfaces of various types (internal/bridge/top/bottom/solid). -// The infills are generated on the groups of surfaces with a compatible type. -// Returns an array of Slic3r::ExtrusionPath::Collection objects containing the infills generaed now -// and the thin fills generated by generate_perimeters(). -void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) -{ -// Slic3r::debugf "Filling layer %d:\n", $layerm->layer->id; - - double fill_density = layerm.region()->config().fill_density; - Flow infill_flow = layerm.flow(frInfill); - Flow solid_infill_flow = layerm.flow(frSolidInfill); - Flow top_solid_infill_flow = layerm.flow(frTopSolidInfill); - - Surfaces surfaces; - - // merge adjacent surfaces - // in case of bridge surfaces, the ones with defined angle will be attached to the ones - // without any angle (shouldn't this logic be moved to process_external_surfaces()?) - { - Polygons polygons_bridged; - polygons_bridged.reserve(layerm.fill_surfaces.surfaces.size()); - for (Surfaces::iterator it = layerm.fill_surfaces.surfaces.begin(); it != layerm.fill_surfaces.surfaces.end(); ++ it) - if (it->bridge_angle >= 0) - polygons_append(polygons_bridged, *it); - - // group surfaces by distinct properties (equal surface_type, thickness, thickness_layers, bridge_angle) - // group is of type Slic3r::SurfaceCollection - //FIXME: Use some smart heuristics to merge similar surfaces to eliminate tiny regions. - std::vector<SurfacesPtr> groups; - layerm.fill_surfaces.group(&groups); - - // merge compatible groups (we can generate continuous infill for them) - { - // cache flow widths and patterns used for all solid groups - // (we'll use them for comparing compatible groups) - std::vector<SurfaceGroupAttrib> group_attrib(groups.size()); - for (size_t i = 0; i < groups.size(); ++ i) { - // we can only merge solid non-bridge surfaces, so discard - // non-solid surfaces - const Surface &surface = *groups[i].front(); - if (surface.is_solid() && (!surface.is_bridge() || layerm.layer()->id() == 0)) { - group_attrib[i].is_solid = true; - group_attrib[i].flow_width = (surface.surface_type == stTop) ? top_solid_infill_flow.width : solid_infill_flow.width; - group_attrib[i].pattern = surface.is_external() ? +struct SurfaceFill { + SurfaceFill(const SurfaceFillParams& params) : region_id(size_t(-1)), surface(stCount, ExPolygon()), params(params) {} + + size_t region_id; + Surface surface; + ExPolygons expolygons; + SurfaceFillParams params; +}; + +std::vector<SurfaceFill> group_fills(const Layer &layer) +{ + std::vector<SurfaceFill> surface_fills; + + // Fill in a map of a region & surface to SurfaceFillParams. + std::set<SurfaceFillParams> set_surface_params; + std::vector<std::vector<const SurfaceFillParams*>> region_to_surface_params(layer.regions().size(), std::vector<const SurfaceFillParams*>()); + SurfaceFillParams params; + bool has_internal_voids = false; + for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id) { + const LayerRegion &layerm = *layer.regions()[region_id]; + region_to_surface_params[region_id].assign(layerm.fill_surfaces.size(), nullptr); + for (const Surface &surface : layerm.fill_surfaces.surfaces) + if (surface.surface_type == stInternalVoid) + has_internal_voids = true; + else { + FlowRole extrusion_role = (surface.surface_type == stTop) ? frTopSolidInfill : (surface.is_solid() ? frSolidInfill : frInfill); + bool is_bridge = layer.id() > 0 && surface.is_bridge(); + params.extruder = layerm.region()->extruder(extrusion_role); + params.pattern = layerm.region()->config().fill_pattern.value; + params.density = float(layerm.region()->config().fill_density); + + if (surface.is_solid()) { + params.density = 100.f; + params.pattern = (surface.is_external() && ! is_bridge) ? (surface.is_top() ? layerm.region()->config().top_fill_pattern.value : layerm.region()->config().bottom_fill_pattern.value) : - ipRectilinear; - } - } - // Loop through solid groups, find compatible groups and append them to this one. - for (size_t i = 0; i < groups.size(); ++ i) { - if (! group_attrib[i].is_solid) - continue; - for (size_t j = i + 1; j < groups.size();) { - if (group_attrib[i] == group_attrib[j]) { - // groups are compatible, merge them - groups[i].insert(groups[i].end(), groups[j].begin(), groups[j].end()); - groups.erase(groups.begin() + j); - group_attrib.erase(group_attrib.begin() + j); - } else - ++ j; - } - } - } - - // Give priority to bridges. Process the bridges in the first round, the rest of the surfaces in the 2nd round. - for (size_t round = 0; round < 2; ++ round) { - for (std::vector<SurfacesPtr>::iterator it_group = groups.begin(); it_group != groups.end(); ++ it_group) { - const SurfacesPtr &group = *it_group; - bool is_bridge = group.front()->bridge_angle >= 0; - if (is_bridge != (round == 0)) - continue; - // Make a union of polygons defining the infiill regions of a group, use a safety offset. - Polygons union_p = union_(to_polygons(*it_group), true); - // Subtract surfaces having a defined bridge_angle from any other, use a safety offset. - if (! polygons_bridged.empty() && ! is_bridge) - union_p = diff(union_p, polygons_bridged, true); - // subtract any other surface already processed - //FIXME Vojtech: Because the bridge surfaces came first, they are subtracted twice! - // Using group.front() as a template. - surfaces_append(surfaces, diff_ex(union_p, to_polygons(surfaces), true), *group.front()); - } - } - } - + ipRectilinear; + } else if (params.density <= 0) + continue; + + params.extrusion_role = + is_bridge ? + erBridgeInfill : + (surface.is_solid() ? + ((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) : + erInternalInfill); + params.bridge_angle = float(surface.bridge_angle); + params.angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value)); + + // calculate the actual flow we'll be using for this infill + params.flow = layerm.region()->flow( + extrusion_role, + (surface.thickness == -1) ? layer.height : surface.thickness, // extrusion height + is_bridge || Fill::use_bridge_flow(params.pattern), // bridge flow? + layer.id() == 0, // first layer? + -1, // auto width + *layer.object() + ); + + // Calculate flow spacing for infill pattern generation. + if (! surface.is_solid() && ! is_bridge) { + // it's internal infill, so we can calculate a generic flow spacing + // for all layers, for avoiding the ugly effect of + // misaligned infill on first layer because of different extrusion width and + // layer height + params.spacing = layerm.region()->flow( + frInfill, + layer.object()->config().layer_height.value, // TODO: handle infill_every_layers? + false, // no bridge + false, // no first layer + -1, // auto width + *layer.object() + ).spacing(); + } else + params.spacing = params.flow.spacing(); + + auto it_params = set_surface_params.find(params); + if (it_params == set_surface_params.end()) + it_params = set_surface_params.insert(it_params, params); + region_to_surface_params[region_id][&surface - &layerm.fill_surfaces.surfaces.front()] = &(*it_params); + } + } + + surface_fills.reserve(set_surface_params.size()); + for (const SurfaceFillParams ¶ms : set_surface_params) { + const_cast<SurfaceFillParams&>(params).idx = surface_fills.size(); + surface_fills.emplace_back(params); + } + + for (size_t region_id = 0; region_id < layer.regions().size(); ++ region_id) { + const LayerRegion &layerm = *layer.regions()[region_id]; + for (const Surface &surface : layerm.fill_surfaces.surfaces) + if (surface.surface_type != stInternalVoid) { + const SurfaceFillParams *params = region_to_surface_params[region_id][&surface - &layerm.fill_surfaces.surfaces.front()]; + if (params != nullptr) { + SurfaceFill &fill = surface_fills[params->idx]; + if (fill.region_id == size_t(-1)) { + fill.region_id = region_id; + fill.surface = surface; + fill.expolygons.emplace_back(std::move(fill.surface.expolygon)); + } else + fill.expolygons.emplace_back(surface.expolygon); + } + } + } + + { + Polygons all_polygons; + for (SurfaceFill &fill : surface_fills) + if (! fill.expolygons.empty()) { + if (fill.expolygons.size() > 1 || ! all_polygons.empty()) { + Polygons polys = to_polygons(std::move(fill.expolygons)); + // Make a union of polygons, use a safety offset, subtract the preceding polygons. + // Bridges are processed first (see SurfaceFill::operator<()) + fill.expolygons = all_polygons.empty() ? union_ex(polys, true) : diff_ex(polys, all_polygons, true); + append(all_polygons, std::move(polys)); + } else if (&fill != &surface_fills.back()) + append(all_polygons, to_polygons(fill.expolygons)); + } + } + // we need to detect any narrow surfaces that might collapse // when adding spacing below // such narrow surfaces are often generated in sloping walls @@ -119,155 +219,204 @@ void make_fill(LayerRegion &layerm, ExtrusionEntityCollection &out) // we are going to grow such regions by overlapping them with the void (if any) // TODO: detect and investigate whether there could be narrow regions without // any void neighbors - { - coord_t distance_between_surfaces = std::max( - std::max(infill_flow.scaled_spacing(), solid_infill_flow.scaled_spacing()), - top_solid_infill_flow.scaled_spacing()); - Polygons surfaces_polygons = to_polygons(surfaces); - Polygons collapsed = diff( - surfaces_polygons, - offset2(surfaces_polygons, (float)-distance_between_surfaces/2, (float)+distance_between_surfaces/2), - true); - Polygons to_subtract; - to_subtract.reserve(collapsed.size() + number_polygons(surfaces)); - for (Surfaces::const_iterator it_surface = surfaces.begin(); it_surface != surfaces.end(); ++ it_surface) - if (it_surface->surface_type == stInternalVoid) - polygons_append(to_subtract, *it_surface); - polygons_append(to_subtract, collapsed); - surfaces_append( - surfaces, - intersection_ex( - offset(collapsed, (float)distance_between_surfaces), - to_subtract, - true), - stInternalSolid); + if (has_internal_voids) { + // Internal voids are generated only if "infill_only_where_needed" or "infill_every_layers" are active. + coord_t distance_between_surfaces = 0; + Polygons surfaces_polygons; + Polygons voids; + int region_internal_infill = -1; + int region_solid_infill = -1; + int region_some_infill = -1; + for (SurfaceFill &surface_fill : surface_fills) + if (! surface_fill.expolygons.empty()) { + distance_between_surfaces = std::max(distance_between_surfaces, surface_fill.params.flow.scaled_spacing()); + append((surface_fill.surface.surface_type == stInternalVoid) ? voids : surfaces_polygons, to_polygons(surface_fill.expolygons)); + if (surface_fill.surface.surface_type == stInternalSolid) + region_internal_infill = (int)surface_fill.region_id; + if (surface_fill.surface.is_solid()) + region_solid_infill = (int)surface_fill.region_id; + if (surface_fill.surface.surface_type != stInternalVoid) + region_some_infill = (int)surface_fill.region_id; + } + if (! voids.empty() && ! surfaces_polygons.empty()) { + // First clip voids by the printing polygons, as the voids were ignored by the loop above during mutual clipping. + voids = diff(voids, surfaces_polygons); + // Corners of infill regions, which would not be filled with an extrusion path with a radius of distance_between_surfaces/2 + Polygons collapsed = diff( + surfaces_polygons, + offset2(surfaces_polygons, (float)-distance_between_surfaces/2, (float)+distance_between_surfaces/2), + true); + //FIXME why the voids are added to collapsed here? First it is expensive, second the result may lead to some unwanted regions being + // added if two offsetted void regions merge. + // polygons_append(voids, collapsed); + ExPolygons extensions = intersection_ex(offset(collapsed, (float)distance_between_surfaces), voids, true); + // Now find an internal infill SurfaceFill to add these extrusions to. + SurfaceFill *internal_solid_fill = nullptr; + unsigned int region_id = 0; + if (region_internal_infill != -1) + region_id = region_internal_infill; + else if (region_solid_infill != -1) + region_id = region_solid_infill; + else if (region_some_infill != -1) + region_id = region_some_infill; + const LayerRegion& layerm = *layer.regions()[region_id]; + for (SurfaceFill &surface_fill : surface_fills) + if (surface_fill.surface.surface_type == stInternalSolid && std::abs(layer.height - surface_fill.params.flow.height) < EPSILON) { + internal_solid_fill = &surface_fill; + break; + } + if (internal_solid_fill == nullptr) { + // Produce another solid fill. + params.extruder = layerm.region()->extruder(frSolidInfill); + params.pattern = ipRectilinear; + params.density = 100.f; + params.extrusion_role = erInternalInfill; + params.angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value)); + // calculate the actual flow we'll be using for this infill + params.flow = layerm.region()->flow( + frSolidInfill, + layer.height, // extrusion height + false, // bridge flow? + layer.id() == 0, // first layer? + -1, // auto width + *layer.object() + ); + params.spacing = params.flow.spacing(); + surface_fills.emplace_back(params); + surface_fills.back().surface.surface_type = stInternalSolid; + surface_fills.back().surface.thickness = layer.height; + surface_fills.back().expolygons = std::move(extensions); + } else { + append(extensions, std::move(internal_solid_fill->expolygons)); + internal_solid_fill->expolygons = union_ex(extensions); + } + } } - if (0) { -// require "Slic3r/SVG.pm"; -// Slic3r::SVG::output("fill_" . $layerm->print_z . ".svg", -// expolygons => [ map $_->expolygon, grep !$_->is_solid, @surfaces ], -// red_expolygons => [ map $_->expolygon, grep $_->is_solid, @surfaces ], -// ); - } + return surface_fills; +} - for (const Surface &surface : surfaces) { - if (surface.surface_type == stInternalVoid) - continue; - InfillPattern fill_pattern = layerm.region()->config().fill_pattern.value; - double density = fill_density; - FlowRole role = (surface.surface_type == stTop) ? frTopSolidInfill : - (surface.is_solid() ? frSolidInfill : frInfill); - bool is_bridge = layerm.layer()->id() > 0 && surface.is_bridge(); - - if (surface.is_solid()) { - density = 100.; - fill_pattern = (surface.is_external() && ! is_bridge) ? - (surface.is_top() ? layerm.region()->config().top_fill_pattern.value : layerm.region()->config().bottom_fill_pattern.value) : - ipRectilinear; - } else if (density <= 0) - continue; - - // get filler object - std::unique_ptr<Fill> f = std::unique_ptr<Fill>(Fill::new_from_type(fill_pattern)); - f->set_bounding_box(layerm.layer()->object()->bounding_box()); - - // calculate the actual flow we'll be using for this infill - coordf_t h = (surface.thickness == -1) ? layerm.layer()->height : surface.thickness; - Flow flow = layerm.region()->flow( - role, - h, - is_bridge || f->use_bridge_flow(), // bridge flow? - layerm.layer()->id() == 0, // first layer? - -1, // auto width - *layerm.layer()->object() - ); - - // calculate flow spacing for infill pattern generation - bool using_internal_flow = false; - if (! surface.is_solid() && ! is_bridge) { - // it's internal infill, so we can calculate a generic flow spacing - // for all layers, for avoiding the ugly effect of - // misaligned infill on first layer because of different extrusion width and - // layer height - Flow internal_flow = layerm.region()->flow( - frInfill, - layerm.layer()->object()->config().layer_height.value, // TODO: handle infill_every_layers? - false, // no bridge - false, // no first layer - -1, // auto width - *layerm.layer()->object() - ); - f->spacing = internal_flow.spacing(); - using_internal_flow = true; - } else { - f->spacing = flow.spacing(); - } +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING +void export_group_fills_to_svg(const char *path, const std::vector<SurfaceFill> &fills) +{ + BoundingBox bbox; + for (const auto &fill : fills) + for (const auto &expoly : fill.expolygons) + bbox.merge(get_extents(expoly)); + Point legend_size = export_surface_type_legend_to_svg_box_size(); + Point legend_pos(bbox.min(0), bbox.max(1)); + bbox.merge(Point(std::max(bbox.min(0) + legend_size(0), bbox.max(0)), bbox.max(1) + legend_size(1))); + + SVG svg(path, bbox); + const float transparency = 0.5f; + for (const auto &fill : fills) + for (const auto &expoly : fill.expolygons) + svg.draw(expoly, surface_type_to_color_name(fill.surface.surface_type), transparency); + export_surface_type_legend_to_svg(svg, legend_pos); + svg.Close(); +} +#endif +// friend to Layer +void Layer::make_fills() +{ + for (LayerRegion *layerm : m_regions) + layerm->fills.clear(); + + +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING +// this->export_region_fill_surfaces_to_svg_debug("10_fill-initial"); +#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + + std::vector<SurfaceFill> surface_fills = group_fills(*this); + const Slic3r::BoundingBox bbox = this->object()->bounding_box(); + +#ifdef SLIC3R_DEBUG_SLICE_PROCESSING + { + static int iRun = 0; + export_group_fills_to_svg(debug_out_path("Layer-fill_surfaces-10_fill-final-%d.svg", iRun ++).c_str(), surface_fills); + } +#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ + + for (SurfaceFill &surface_fill : surface_fills) { + // Create the filler object. + std::unique_ptr<Fill> f = std::unique_ptr<Fill>(Fill::new_from_type(surface_fill.params.pattern)); + f->set_bounding_box(bbox); + f->layer_id = this->id(); + f->z = this->print_z; + f->angle = surface_fill.params.angle; + + // calculate flow spacing for infill pattern generation + bool using_internal_flow = ! surface_fill.surface.is_solid() && ! surface_fill.params.flow.bridge; double link_max_length = 0.; - if (! is_bridge) { + if (! surface_fill.params.flow.bridge) { #if 0 link_max_length = layerm.region()->config().get_abs_value(surface.is_external() ? "external_fill_link_max_length" : "fill_link_max_length", flow.spacing()); // printf("flow spacing: %f, is_external: %d, link_max_length: %lf\n", flow.spacing(), int(surface.is_external()), link_max_length); #else - if (density > 80.) // 80% + if (surface_fill.params.density > 80.) // 80% link_max_length = 3. * f->spacing; #endif } - f->layer_id = layerm.layer()->id(); - f->z = layerm.layer()->print_z; - f->angle = float(Geometry::deg2rad(layerm.region()->config().fill_angle.value)); // Maximum length of the perimeter segment linking two infill lines. f->link_max_length = (coord_t)scale_(link_max_length); // Used by the concentric infill pattern to clip the loops to create extrusion paths. - f->loop_clipping = coord_t(scale_(flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER); -// f->layer_height = h; + f->loop_clipping = coord_t(scale_(surface_fill.params.flow.nozzle_diameter) * LOOP_CLIPPING_LENGTH_OVER_NOZZLE_DIAMETER); // apply half spacing using this flow's own spacing and generate infill FillParams params; - params.density = float(0.01 * density); -// params.dont_adjust = true; - params.dont_adjust = false; - Polylines polylines = f->fill_surface(&surface, params); - if (polylines.empty()) - continue; - - // calculate actual flow from spacing (which might have been adjusted by the infill - // pattern generator) - if (using_internal_flow) { - // if we used the internal flow we're not doing a solid infill - // so we can safely ignore the slight variation that might have - // been applied to $f->flow_spacing - } else { - flow = Flow::new_from_spacing(f->spacing, flow.nozzle_diameter, (float)h, is_bridge || f->use_bridge_flow()); - } + params.density = float(0.01 * surface_fill.params.density); + params.dont_adjust = surface_fill.params.dont_adjust; // false - // Save into layer. - auto *eec = new ExtrusionEntityCollection(); - out.entities.push_back(eec); - // Only concentric fills are not sorted. - eec->no_sort = f->no_sort(); - extrusion_entities_append_paths( - eec->entities, std::move(polylines), - is_bridge ? - erBridgeInfill : - (surface.is_solid() ? - ((surface.surface_type == stTop) ? erTopSolidInfill : erSolidInfill) : - erInternalInfill), - flow.mm3_per_mm(), flow.width, flow.height); + for (ExPolygon &expoly : surface_fill.expolygons) { + // Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon. + f->spacing = surface_fill.params.spacing; + surface_fill.surface.expolygon = std::move(expoly); + Polylines polylines = f->fill_surface(&surface_fill.surface, params); + if (! polylines.empty()) { + // calculate actual flow from spacing (which might have been adjusted by the infill + // pattern generator) + double flow_mm3_per_mm = surface_fill.params.flow.mm3_per_mm(); + double flow_width = surface_fill.params.flow.width; + if (using_internal_flow) { + // if we used the internal flow we're not doing a solid infill + // so we can safely ignore the slight variation that might have + // been applied to f->spacing + } else { + Flow new_flow = Flow::new_from_spacing(float(f->spacing), surface_fill.params.flow.nozzle_diameter, surface_fill.params.flow.height, surface_fill.params.flow.bridge); + flow_mm3_per_mm = new_flow.mm3_per_mm(); + flow_width = new_flow.width; + } + // Save into layer. + auto *eec = new ExtrusionEntityCollection(); + m_regions[surface_fill.region_id]->fills.entities.push_back(eec); + // Only concentric fills are not sorted. + eec->no_sort = f->no_sort(); + extrusion_entities_append_paths( + eec->entities, std::move(polylines), + surface_fill.params.extrusion_role, + flow_mm3_per_mm, float(flow_width), surface_fill.params.flow.height); + } + } } // add thin fill regions - // thin_fills are of C++ Slic3r::ExtrusionEntityCollection, perl type Slic3r::ExtrusionPath::Collection // Unpacks the collection, creates multiple collections per path. // The path type could be ExtrusionPath, ExtrusionLoop or ExtrusionEntityCollection. // Why the paths are unpacked? - for (const ExtrusionEntity *thin_fill : layerm.thin_fills.entities) { - ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection()); - out.entities.push_back(&collection); - collection.entities.push_back(thin_fill->clone()); - } + for (LayerRegion *layerm : m_regions) + for (const ExtrusionEntity *thin_fill : layerm->thin_fills.entities) { + ExtrusionEntityCollection &collection = *(new ExtrusionEntityCollection()); + layerm->fills.entities.push_back(&collection); + collection.entities.push_back(thin_fill->clone()); + } + +#ifndef NDEBUG + for (LayerRegion *layerm : m_regions) + for (size_t i = 0; i < layerm->fills.entities.size(); ++ i) + assert(dynamic_cast<ExtrusionEntityCollection*>(layerm->fills.entities[i]) != nullptr); +#endif } } // namespace Slic3r diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 7a99e84f7..c16d76415 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -34,7 +34,7 @@ Fill* Fill::new_from_type(const InfillPattern type) case ipArchimedeanChords: return new FillArchimedeanChords(); case ipHilbertCurve: return new FillHilbertCurve(); case ipOctagramSpiral: return new FillOctagramSpiral(); - default: throw std::invalid_argument("unknown type");; + default: throw std::invalid_argument("unknown type"); } } @@ -45,6 +45,24 @@ Fill* Fill::new_from_type(const std::string &type) return (it == enum_keys_map.end()) ? nullptr : new_from_type(InfillPattern(it->second)); } +// Force initialization of the Fill::use_bridge_flow() internal static map in a thread safe fashion even on compilers +// not supporting thread safe non-static data member initializers. +static bool use_bridge_flow_initializer = Fill::use_bridge_flow(ipGrid); + +bool Fill::use_bridge_flow(const InfillPattern type) +{ + static std::vector<unsigned char> cached; + if (cached.empty()) { + cached.assign(size_t(ipCount), 0); + for (size_t i = 0; i < cached.size(); ++ i) { + auto *fill = Fill::new_from_type((InfillPattern)i); + cached[i] = fill->use_bridge_flow(); + delete fill; + } + } + return cached[type] != 0; +} + Polylines Fill::fill_surface(const Surface *surface, const FillParams ¶ms) { // Perform offset. diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index 8bf6c3689..fb7947893 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -70,6 +70,7 @@ public: static Fill* new_from_type(const InfillPattern type); static Fill* new_from_type(const std::string &type); + static bool use_bridge_flow(const InfillPattern type); void set_bounding_box(const Slic3r::BoundingBox &bbox) { bounding_box = bbox; } diff --git a/src/libslic3r/Flow.hpp b/src/libslic3r/Flow.hpp index 4b0a846ad..51cc4da9d 100644 --- a/src/libslic3r/Flow.hpp +++ b/src/libslic3r/Flow.hpp @@ -56,6 +56,8 @@ public: // Enable some perimeter squish (see INSET_OVERLAP_TOLERANCE). // Here an overlap of 0.2x external perimeter spacing is allowed for by the elephant foot compensation. coord_t scaled_elephant_foot_spacing() const { return coord_t(0.5f * float(this->scaled_width() + 0.6f * this->scaled_spacing())); } + + bool operator==(const Flow &rhs) const { return this->width == rhs.width && this->height == rhs.height && this->nozzle_diameter == rhs.nozzle_diameter && this->bridge == rhs.bridge; } static Flow new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height, float bridge_flow_ratio); // Create a flow from the spacing of extrusion lines. diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 819dcc56e..b5857a77b 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -690,15 +690,19 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_ throw std::runtime_error(msg); } - if (print->config().remaining_times.value) { - BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for normal mode" << log_memory_info(); - m_normal_time_estimator.post_process_remaining_times(path_tmp, 60.0f); + GCodeTimeEstimator::PostProcessData normal_data = m_normal_time_estimator.get_post_process_data(); + GCodeTimeEstimator::PostProcessData silent_data = m_silent_time_estimator.get_post_process_data(); + + bool remaining_times_enabled = print->config().remaining_times.value; + + BOOST_LOG_TRIVIAL(debug) << "Time estimator post processing" << log_memory_info(); + GCodeTimeEstimator::post_process(path_tmp, 60.0f, remaining_times_enabled ? &normal_data : nullptr, (remaining_times_enabled && m_silent_time_estimator_enabled) ? &silent_data : nullptr); + + if (remaining_times_enabled) + { m_normal_time_estimator.reset(); - if (m_silent_time_estimator_enabled) { - BOOST_LOG_TRIVIAL(debug) << "Processing remaining times for silent mode" << log_memory_info(); - m_silent_time_estimator.post_process_remaining_times(path_tmp, 60.0f); + if (m_silent_time_estimator_enabled) m_silent_time_estimator.reset(); - } } // starts analyzer calculations @@ -1771,13 +1775,9 @@ void GCode::process_layer( // This extrusion is part of certain Region, which tells us which extruder should be used for it: int correct_extruder_id = Print::get_extruder(*fill, region); - //FIXME what is this? - entity_type=="infills" ? - std::max<int>(0, (is_solid_infill(fill->entities.front()->role()) ? region.config().solid_infill_extruder : region.config().infill_extruder) - 1) : - std::max<int>(region.config().perimeter_extruder.value - 1, 0); // Let's recover vector of extruder overrides: - const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, (int)layer_to_print.object()->copies().size()); + const ExtruderPerCopy* entity_overrides = const_cast<LayerTools&>(layer_tools).wiping_extrusions().get_extruder_overrides(fill, correct_extruder_id, layer_to_print.object()->copies().size()); // Now we must add this extrusion into the by_extruder map, once for each extruder that will print it: for (unsigned int extruder : layer_tools.extruders) @@ -1949,7 +1949,7 @@ void GCode::process_layer( _write(file, gcode); BOOST_LOG_TRIVIAL(trace) << "Exported layer " << layer.id() << " print_z " << print_z << ", time estimator memory: " << - format_memsize_MB(m_normal_time_estimator.memory_used() + m_silent_time_estimator_enabled ? m_silent_time_estimator.memory_used() : 0) << + format_memsize_MB(m_normal_time_estimator.memory_used() + (m_silent_time_estimator_enabled ? m_silent_time_estimator.memory_used() : 0)) << ", analyzer memory: " << format_memsize_MB(m_analyzer.memory_used()) << log_memory_info(); @@ -2030,38 +2030,6 @@ std::string GCode::change_layer(coordf_t print_z) return gcode; } -static inline const char* ExtrusionRole2String(const ExtrusionRole role) -{ - switch (role) { - case erNone: return "erNone"; - case erPerimeter: return "erPerimeter"; - case erExternalPerimeter: return "erExternalPerimeter"; - case erOverhangPerimeter: return "erOverhangPerimeter"; - case erInternalInfill: return "erInternalInfill"; - case erSolidInfill: return "erSolidInfill"; - case erTopSolidInfill: return "erTopSolidInfill"; - case erBridgeInfill: return "erBridgeInfill"; - case erGapFill: return "erGapFill"; - case erSkirt: return "erSkirt"; - case erSupportMaterial: return "erSupportMaterial"; - case erSupportMaterialInterface: return "erSupportMaterialInterface"; - case erWipeTower: return "erWipeTower"; - case erMixed: return "erMixed"; - - default: return "erInvalid"; - }; -} - -static inline const char* ExtrusionLoopRole2String(const ExtrusionLoopRole role) -{ - switch (role) { - case elrDefault: return "elrDefault"; - case elrContourInternalPerimeter: return "elrContourInternalPerimeter"; - case elrSkirt: return "elrSkirt"; - default: return "elrInvalid"; - } -}; - // Return a value in <0, 1> of a cubic B-spline kernel centered around zero. // The B-spline is re-scaled so it has value 1 at zero. static inline float bspline_kernel(float x) @@ -2444,8 +2412,8 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou // extrude along the path std::string gcode; for (ExtrusionPaths::iterator path = paths.begin(); path != paths.end(); ++path) { -// description += ExtrusionLoopRole2String(loop.loop_role()); -// description += ExtrusionRole2String(path->role); +// description += ExtrusionLoop::role_to_string(loop.loop_role()); +// description += ExtrusionEntity::role_to_string(path->role); path->simplify(SCALED_RESOLUTION); gcode += this->_extrude(*path, description, speed); } @@ -2498,8 +2466,8 @@ std::string GCode::extrude_multi_path(ExtrusionMultiPath multipath, std::string // extrude along the path std::string gcode; for (ExtrusionPath path : multipath.paths) { -// description += ExtrusionLoopRole2String(loop.loop_role()); -// description += ExtrusionRole2String(path->role); +// description += ExtrusionLoop::role_to_string(loop.loop_role()); +// description += ExtrusionEntity::role_to_string(path->role); path.simplify(SCALED_RESOLUTION); gcode += this->_extrude(path, description, speed); } @@ -2528,7 +2496,7 @@ std::string GCode::extrude_entity(const ExtrusionEntity &entity, std::string des std::string GCode::extrude_path(ExtrusionPath path, std::string description, double speed) { -// description += ExtrusionRole2String(path.role()); +// description += ExtrusionEntity::role_to_string(path.role()); path.simplify(SCALED_RESOLUTION); std::string gcode = this->_extrude(path, description, speed); if (m_wipe.enable) { @@ -3056,7 +3024,7 @@ const std::vector<GCode::ObjectByExtruder::Island::Region>& GCode::ObjectByExtru // This function takes the eec and appends its entities to either perimeters or infills of this Region (depending on the first parameter) // It also saves pointer to ExtruderPerCopy struct (for each entity), that holds information about which extruders should be used for which copy. -void GCode::ObjectByExtruder::Island::Region::append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copies_extruder, unsigned int object_copies_num) +void GCode::ObjectByExtruder::Island::Region::append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copies_extruder, size_t object_copies_num) { // We are going to manipulate either perimeters or infills, exactly in the same way. Let's create pointers to the proper structure to not repeat ourselves: ExtrusionEntityCollection* perimeters_or_infills = &infills; diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 83d61c483..72813810b 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -246,7 +246,7 @@ protected: std::vector<const ExtruderPerCopy*> perimeters_overrides; // Appends perimeter/infill entities and writes don't indices of those that are not to be extruder as part of perimeter/infill wiping - void append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copy_extruders, unsigned int object_copies_num); + void append(const std::string& type, const ExtrusionEntityCollection* eec, const ExtruderPerCopy* copy_extruders, size_t object_copies_num); }; std::vector<Region> by_region; // all extrusions for this island, grouped by regions diff --git a/src/libslic3r/GCode/PreviewData.cpp b/src/libslic3r/GCode/PreviewData.cpp index e634461ae..74de2f532 100644 --- a/src/libslic3r/GCode/PreviewData.cpp +++ b/src/libslic3r/GCode/PreviewData.cpp @@ -121,7 +121,7 @@ GCodePreviewData::LegendItem::LegendItem(const std::string& text, const GCodePre { } -const GCodePreviewData::Color GCodePreviewData::Extrusion::Default_Extrusion_Role_Colors[Num_Extrusion_Roles] = +const GCodePreviewData::Color GCodePreviewData::Extrusion::Default_Extrusion_Role_Colors[erCount] = { Color(0.0f, 0.0f, 0.0f, 1.0f), // erNone Color(1.0f, 0.0f, 0.0f, 1.0f), // erPerimeter @@ -140,44 +140,20 @@ const GCodePreviewData::Color GCodePreviewData::Extrusion::Default_Extrusion_Rol Color(0.0f, 0.0f, 0.0f, 1.0f) // erMixed }; -// todo: merge with Slic3r::ExtrusionRole2String() from GCode.cpp -const std::string GCodePreviewData::Extrusion::Default_Extrusion_Role_Names[Num_Extrusion_Roles] -{ - L("None"), - L("Perimeter"), - L("External perimeter"), - L("Overhang perimeter"), - L("Internal infill"), - L("Solid infill"), - L("Top solid infill"), - L("Bridge infill"), - L("Gap fill"), - L("Skirt"), - L("Support material"), - L("Support material interface"), - L("Wipe tower"), - L("Custom"), - L("Mixed") -}; - const GCodePreviewData::Extrusion::EViewType GCodePreviewData::Extrusion::Default_View_Type = GCodePreviewData::Extrusion::FeatureType; void GCodePreviewData::Extrusion::set_default() { view_type = Default_View_Type; - ::memcpy((void*)role_colors, (const void*)Default_Extrusion_Role_Colors, Num_Extrusion_Roles * sizeof(Color)); + ::memcpy((void*)role_colors, (const void*)Default_Extrusion_Role_Colors, erCount * sizeof(Color)); - for (unsigned int i = 0; i < Num_Extrusion_Roles; ++i) - { - role_names[i] = Default_Extrusion_Role_Names[i]; - } + for (unsigned int i = 0; i < erCount; ++i) + role_names[i] = ExtrusionEntity::role_to_string(ExtrusionRole(i)); role_flags = 0; - for (unsigned int i = 0; i < Num_Extrusion_Roles; ++i) - { + for (unsigned int i = 0; i < erCount; ++i) role_flags |= 1 << i; - } } bool GCodePreviewData::Extrusion::is_role_flag_set(ExtrusionRole role) const @@ -318,7 +294,7 @@ GCodePreviewData::Color GCodePreviewData::get_volumetric_rate_color(float rate) void GCodePreviewData::set_extrusion_role_color(const std::string& role_name, float red, float green, float blue, float alpha) { - for (unsigned int i = 0; i < Extrusion::Num_Extrusion_Roles; ++i) + for (unsigned int i = 0; i < erCount; ++i) { if (role_name == extrusion.role_names[i]) { diff --git a/src/libslic3r/GCode/PreviewData.hpp b/src/libslic3r/GCode/PreviewData.hpp index 1c8e0463d..6490399b4 100644 --- a/src/libslic3r/GCode/PreviewData.hpp +++ b/src/libslic3r/GCode/PreviewData.hpp @@ -80,9 +80,8 @@ public: Num_View_Types }; - static const unsigned int Num_Extrusion_Roles = (unsigned int)erMixed + 1; - static const Color Default_Extrusion_Role_Colors[Num_Extrusion_Roles]; - static const std::string Default_Extrusion_Role_Names[Num_Extrusion_Roles]; + static const Color Default_Extrusion_Role_Colors[erCount]; + static const std::string Default_Extrusion_Role_Names[erCount]; static const EViewType Default_View_Type; struct Layer @@ -96,8 +95,8 @@ public: typedef std::vector<Layer> LayersList; EViewType view_type; - Color role_colors[Num_Extrusion_Roles]; - std::string role_names[Num_Extrusion_Roles]; + Color role_colors[erCount]; + std::string role_names[erCount]; LayersList layers; unsigned int role_flags; diff --git a/src/libslic3r/GCode/ToolOrdering.cpp b/src/libslic3r/GCode/ToolOrdering.cpp index 9280aa33a..0a9ec320e 100644 --- a/src/libslic3r/GCode/ToolOrdering.cpp +++ b/src/libslic3r/GCode/ToolOrdering.cpp @@ -329,6 +329,28 @@ void ToolOrdering::fill_wipe_tower_partitions(const PrintConfig &config, coordf_ } } + // If the model contains empty layers (such as https://github.com/prusa3d/Slic3r/issues/1266), there might be layers + // that were not marked as has_wipe_tower, even when they should have been. This produces a crash with soluble supports + // and maybe other problems. We will therefore go through layer_tools and detect and fix this. + // So, if there is a non-object layer starting with different extruder than the last one ended with (or containing more than one extruder), + // we'll mark it with has_wipe tower. + for (unsigned int i=0; i+1<m_layer_tools.size(); ++i) { + LayerTools& lt = m_layer_tools[i]; + LayerTools& lt_next = m_layer_tools[i+1]; + if (lt.extruders.empty() || lt_next.extruders.empty()) + break; + if (!lt_next.has_wipe_tower && (lt_next.extruders.front() != lt.extruders.back() || lt_next.extruders.size() > 1)) + lt_next.has_wipe_tower = true; + // We should also check that the next wipe tower layer is no further than max_layer_height: + unsigned int j = i+1; + double last_wipe_tower_print_z = lt_next.print_z; + while (++j < m_layer_tools.size()-1 && !m_layer_tools[j].has_wipe_tower) + if (m_layer_tools[j+1].print_z - last_wipe_tower_print_z > max_layer_height) { + m_layer_tools[j].has_wipe_tower = true; + last_wipe_tower_print_z = m_layer_tools[j].print_z; + } + } + // Calculate the wipe_tower_layer_height values. coordf_t wipe_tower_print_z_last = 0.; for (LayerTools < : m_layer_tools) @@ -458,14 +480,14 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int continue; } - const auto& object = object_list[i]; + const PrintObject* object = object_list[i]; // Finds this layer: auto this_layer_it = std::find_if(object->layers().begin(), object->layers().end(), [<](const Layer* lay) { return std::abs(lt.print_z - lay->print_z)<EPSILON; }); if (this_layer_it == object->layers().end()) continue; const Layer* this_layer = *this_layer_it; - unsigned int num_of_copies = object->copies().size(); + size_t num_of_copies = object->copies().size(); for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves @@ -494,7 +516,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { // this infill will be used to wipe this extruder set_extruder_override(fill, copy, new_extruder, num_of_copies); - volume_to_wipe -= fill->total_volume(); + volume_to_wipe -= float(fill->total_volume()); } } } @@ -512,7 +534,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int if ((!is_entity_overridden(fill, copy) && fill->total_volume() > min_infill_volume)) { set_extruder_override(fill, copy, new_extruder, num_of_copies); - volume_to_wipe -= fill->total_volume(); + volume_to_wipe -= float(fill->total_volume()); } } } @@ -540,9 +562,9 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) if (this_layer_it == object->layers().end()) continue; const Layer* this_layer = *this_layer_it; - unsigned int num_of_copies = object->copies().size(); + size_t num_of_copies = object->copies().size(); - for (unsigned int copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves + for (size_t copy = 0; copy < num_of_copies; ++copy) { // iterate through copies first, so that we mark neighbouring infills to minimize travel moves for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) { const auto& region = *object->print()->regions()[region_id]; @@ -598,7 +620,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print) // so -1 was used as "print as usual". // The resulting vector has to keep track of which extrusions are the ones that were overridden and which were not. In the extruder is used as overridden, // its number is saved as it is (zero-based index). Usual extrusions are saved as -number-1 (unfortunately there is no negative zero). -const std::vector<int>* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies) +const std::vector<int>* WipingExtrusions::get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies) { auto entity_map_it = entity_map.find(entity); if (entity_map_it == entity_map.end()) diff --git a/src/libslic3r/GCode/ToolOrdering.hpp b/src/libslic3r/GCode/ToolOrdering.hpp index d4006120d..0d4b83ecb 100644 --- a/src/libslic3r/GCode/ToolOrdering.hpp +++ b/src/libslic3r/GCode/ToolOrdering.hpp @@ -24,7 +24,7 @@ public: } // This is called from GCode::process_layer - see implementation for further comments: - const std::vector<int>* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, int num_of_copies); + const std::vector<int>* get_extruder_overrides(const ExtrusionEntity* entity, int correct_extruder_id, size_t num_of_copies); // This function goes through all infill entities, decides which ones will be used for wiping and // marks them by the extruder id. Returns volume that remains to be wiped on the wipe tower: @@ -44,7 +44,7 @@ private: void set_extruder_override(const ExtrusionEntity* entity, unsigned int copy_id, int extruder, unsigned int num_of_copies); // Returns true in case that entity is not printed with its usual extruder for a given copy: - bool is_entity_overridden(const ExtrusionEntity* entity, int copy_id) const { + bool is_entity_overridden(const ExtrusionEntity* entity, size_t copy_id) const { return (entity_map.find(entity) == entity_map.end() ? false : entity_map.at(entity).at(copy_id) != -1); } diff --git a/src/libslic3r/GCode/WipeTower.cpp b/src/libslic3r/GCode/WipeTower.cpp index da4c6aa6e..84d03ba20 100644 --- a/src/libslic3r/GCode/WipeTower.cpp +++ b/src/libslic3r/GCode/WipeTower.cpp @@ -82,7 +82,7 @@ public: } WipeTowerWriter& change_analyzer_mm3_per_mm(float len, float e) { - static const float area = M_PI * 1.75f * 1.75f / 4.f; + static const float area = float(M_PI) * 1.75f * 1.75f / 4.f; float mm3_per_mm = (len == 0.f ? 0.f : area * e / len); // adds tag for analyzer: char buf[64]; @@ -100,7 +100,7 @@ public: return *this; } - WipeTowerWriter& set_initial_tool(const unsigned int tool) { m_current_tool = tool; return *this; } + WipeTowerWriter& set_initial_tool(size_t tool) { m_current_tool = tool; return *this; } WipeTowerWriter& set_z(float z) { m_current_z = z; return *this; } @@ -151,7 +151,7 @@ public: float dx = x - m_current_pos.x(); float dy = y - m_current_pos.y(); - double len = sqrt(dx*dx+dy*dy); + float len = std::sqrt(dx*dx+dy*dy); if (record_length) m_used_filament_length += e; @@ -159,11 +159,11 @@ public: Vec2f rotated_current_pos(rotate(m_current_pos + Vec2f(0.f,m_y_shift), m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we are Vec2f rot(rotate(Vec2f(x,y+m_y_shift), m_wipe_tower_width, m_wipe_tower_depth, m_internal_angle)); // this is where we want to go - if (! m_preview_suppressed && e > 0.f && len > 0.) { + if (! m_preview_suppressed && e > 0.f && len > 0.f) { change_analyzer_mm3_per_mm(len, e); // Width of a squished extrusion, corrected for the roundings of the squished extrusions. // This is left zero if it is a travel move. - float width = float(double(e) * m_filpar[0].filament_area / (len * m_layer_height)); + float width = e * m_filpar[0].filament_area / (len * m_layer_height); // Correct for the roundings of a squished extrusion. width += m_layer_height * float(1. - M_PI / 4.); if (m_extrusions.empty() || m_extrusions.back().pos != rotated_current_pos) @@ -172,10 +172,10 @@ public: } m_gcode += "G1"; - if (std::abs(rot.x() - rotated_current_pos.x()) > EPSILON) + if (std::abs(rot.x() - rotated_current_pos.x()) > (float)EPSILON) m_gcode += set_format_X(rot.x()); - if (std::abs(rot.y() - rotated_current_pos.y()) > EPSILON) + if (std::abs(rot.y() - rotated_current_pos.y()) > (float)EPSILON) m_gcode += set_format_Y(rot.y()); @@ -184,7 +184,7 @@ public: if (f != 0.f && f != m_current_feedrate) { if (limit_volumetric_flow) { - float e_speed = e / (((len == 0) ? std::abs(e) : len) / f * 60.f); + float e_speed = e / (((len == 0.f) ? std::abs(e) : len) / f * 60.f); f /= std::max(1.f, e_speed / m_filpar[m_current_tool].max_e_speed); } m_gcode += set_format_F(f); @@ -194,7 +194,7 @@ public: m_current_pos.y() = y; // Update the elapsed time with a rough estimate. - m_elapsed_time += ((len == 0) ? std::abs(e) : len) / m_current_feedrate * 60.f; + m_elapsed_time += ((len == 0.f) ? std::abs(e) : len) / m_current_feedrate * 60.f; m_gcode += "\n"; return *this; } @@ -214,7 +214,7 @@ public: { float dx = x - m_current_pos.x(); float dy = y - m_current_pos.y(); - return extrude_explicit(x, y, sqrt(dx*dx+dy*dy) * m_extrusion_flow, f, true); + return extrude_explicit(x, y, std::sqrt(dx*dx+dy*dy) * m_extrusion_flow, f, true); } WipeTowerWriter& extrude(const Vec2f &dest, const float f = 0.f) @@ -311,7 +311,7 @@ public: return *this; } - WipeTowerWriter& set_tool(int tool) + WipeTowerWriter& set_tool(size_t tool) { m_current_tool = tool; return *this; @@ -320,57 +320,52 @@ public: // Set extruder temperature, don't wait by default. WipeTowerWriter& set_extruder_temp(int temperature, bool wait = false) { - char buf[128]; - sprintf(buf, "M%d S%d\n", wait ? 109 : 104, temperature); - m_gcode += buf; + m_gcode += "M" + std::to_string(wait ? 109 : 104) + " S" + std::to_string(temperature) + "\n"; return *this; - }; + } // Wait for a period of time (seconds). WipeTowerWriter& wait(float time) { - if (time==0) + if (time==0.f) return *this; char buf[128]; sprintf(buf, "G4 S%.3f\n", time); m_gcode += buf; return *this; - }; + } // Set speed factor override percentage. WipeTowerWriter& speed_override(int speed) { - char buf[128]; - sprintf(buf, "M220 S%d\n", speed); - m_gcode += buf; + m_gcode += "M220 S" + std::to_string(speed) + "\n"; return *this; - }; + } // Let the firmware back up the active speed override value. WipeTowerWriter& speed_override_backup() { m_gcode += "M220 B\n"; return *this; - }; + } // Let the firmware restore the active speed override value. WipeTowerWriter& speed_override_restore() { m_gcode += "M220 R\n"; return *this; - }; + } // Set digital trimpot motor WipeTowerWriter& set_extruder_trimpot(int current) { - char buf[128]; if (m_gcode_flavor == gcfRepRap) - sprintf(buf, "M906 E%d\n", current); + m_gcode += "M906 E"; else - sprintf(buf, "M907 E%d\n", current); - m_gcode += buf; + m_gcode += "M907 E"; + m_gcode += std::to_string(current) + "\n"; return *this; - }; + } WipeTowerWriter& flush_planner_queue() { @@ -386,28 +381,20 @@ public: } WipeTowerWriter& comment_with_value(const char *comment, int value) - { - char strvalue[64]; - sprintf(strvalue, "%d", value); - m_gcode += std::string(";") + comment + strvalue + "\n"; + { + m_gcode += std::string(";") + comment + std::to_string(value) + "\n"; return *this; - }; + } - WipeTowerWriter& set_fan(unsigned int speed) + WipeTowerWriter& set_fan(unsigned speed) { if (speed == m_last_fan_speed) return *this; - if (speed == 0) m_gcode += "M107\n"; - else - { - m_gcode += "M106 S"; - char buf[128]; - sprintf(buf,"%u\n",(unsigned int)(255.0 * speed / 100.0)); - m_gcode += buf; - } + else + m_gcode += "M106 S" + std::to_string(unsigned(255.0 * speed / 100.0)) + "\n"; m_last_fan_speed = speed; return *this; } @@ -419,7 +406,7 @@ private: Vec2f m_current_pos; float m_current_z; float m_current_feedrate; - unsigned int m_current_tool; + size_t m_current_tool; float m_layer_height; float m_extrusion_flow; bool m_preview_suppressed; @@ -430,7 +417,7 @@ private: float m_y_shift = 0.f; float m_wipe_tower_width = 0.f; float m_wipe_tower_depth = 0.f; - float m_last_fan_speed = 0.f; + unsigned m_last_fan_speed = 0; int current_temp = -1; const float m_default_analyzer_line_width; float m_used_filament_length = 0.f; @@ -479,12 +466,12 @@ private: WipeTower::WipeTower(const PrintConfig& config, const std::vector<std::vector<float>>& wiping_matrix, size_t initial_tool) : m_semm(config.single_extruder_multi_material.value), m_wipe_tower_pos(config.wipe_tower_x, config.wipe_tower_y), - m_wipe_tower_width(config.wipe_tower_width), - m_wipe_tower_rotation_angle(config.wipe_tower_rotation_angle), + m_wipe_tower_width(float(config.wipe_tower_width)), + m_wipe_tower_rotation_angle(float(config.wipe_tower_rotation_angle)), m_y_shift(0.f), m_z_pos(0.f), m_is_first_layer(false), - m_bridging(config.wipe_tower_bridging), + m_bridging(float(config.wipe_tower_bridging)), m_gcode_flavor(config.gcode_flavor), m_current_tool(initial_tool), wipe_volumes(wiping_matrix) @@ -492,16 +479,16 @@ WipeTower::WipeTower(const PrintConfig& config, const std::vector<std::vector<fl // If this is a single extruder MM printer, we will use all the SE-specific config values. // Otherwise, the defaults will be used to turn off the SE stuff. if (m_semm) { - m_cooling_tube_retraction = config.cooling_tube_retraction; - m_cooling_tube_length = config.cooling_tube_length; - m_parking_pos_retraction = config.parking_pos_retraction; - m_extra_loading_move = config.extra_loading_move; - m_set_extruder_trimpot = config.high_current_on_filament_swap; + m_cooling_tube_retraction = float(config.cooling_tube_retraction); + m_cooling_tube_length = float(config.cooling_tube_length); + m_parking_pos_retraction = float(config.parking_pos_retraction); + m_extra_loading_move = float(config.extra_loading_move); + m_set_extruder_trimpot = config.high_current_on_filament_swap; } // Calculate where the priming lines should be - very naive test not detecting parallelograms or custom shapes const std::vector<Vec2d>& bed_points = config.bed_shape.values; m_bed_shape = (bed_points.size() == 4 ? RectangularBed : CircularBed); - m_bed_width = BoundingBoxf(bed_points).size().x(); + m_bed_width = float(BoundingBoxf(bed_points).size().x()); } @@ -518,21 +505,21 @@ void WipeTower::set_extruder(size_t idx, const PrintConfig& config) // If this is a single extruder MM printer, we will use all the SE-specific config values. // Otherwise, the defaults will be used to turn off the SE stuff. if (m_semm) { - m_filpar[idx].loading_speed = config.filament_loading_speed.get_at(idx); - m_filpar[idx].loading_speed_start = config.filament_loading_speed_start.get_at(idx); - m_filpar[idx].unloading_speed = config.filament_unloading_speed.get_at(idx); - m_filpar[idx].unloading_speed_start = config.filament_unloading_speed_start.get_at(idx); - m_filpar[idx].delay = config.filament_toolchange_delay.get_at(idx); + m_filpar[idx].loading_speed = float(config.filament_loading_speed.get_at(idx)); + m_filpar[idx].loading_speed_start = float(config.filament_loading_speed_start.get_at(idx)); + m_filpar[idx].unloading_speed = float(config.filament_unloading_speed.get_at(idx)); + m_filpar[idx].unloading_speed_start = float(config.filament_unloading_speed_start.get_at(idx)); + m_filpar[idx].delay = float(config.filament_toolchange_delay.get_at(idx)); m_filpar[idx].cooling_moves = config.filament_cooling_moves.get_at(idx); - m_filpar[idx].cooling_initial_speed = config.filament_cooling_initial_speed.get_at(idx); - m_filpar[idx].cooling_final_speed = config.filament_cooling_final_speed.get_at(idx); + m_filpar[idx].cooling_initial_speed = float(config.filament_cooling_initial_speed.get_at(idx)); + m_filpar[idx].cooling_final_speed = float(config.filament_cooling_final_speed.get_at(idx)); } m_filpar[idx].filament_area = float((M_PI/4.f) * pow(config.filament_diameter.get_at(idx), 2)); // all extruders are assumed to have the same filament diameter at this point - float nozzle_diameter = config.nozzle_diameter.get_at(idx); + float nozzle_diameter = float(config.nozzle_diameter.get_at(idx)); m_filpar[idx].nozzle_diameter = nozzle_diameter; // to be used in future with (non-single) multiextruder MM - float max_vol_speed = config.filament_max_volumetric_speed.get_at(idx); + float max_vol_speed = float(config.filament_max_volumetric_speed.get_at(idx)); if (max_vol_speed!= 0.f) m_filpar[idx].max_e_speed = (max_vol_speed / filament_area()); @@ -561,7 +548,7 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime( const std::vector<unsigned int> &tools, // If true, the last priming are will be the same as the other priming areas, and the rest of the wipe will be performed inside the wipe tower. // If false, the last priming are will be large enough to wipe the last extruder sufficiently. - bool last_wipe_inside_wipe_tower) + bool /*last_wipe_inside_wipe_tower*/) { this->set_layer(first_layer_height, first_layer_height, tools.size(), true, false); this->m_current_tool = tools.front(); @@ -581,7 +568,7 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime( // Iterate over all priming toolchanges and push respective ToolChangeResults into results vector. for (size_t idx_tool = 0; idx_tool < tools.size(); ++ idx_tool) { - int old_tool = m_current_tool; + size_t old_tool = m_current_tool; WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); writer.set_extrusion_flow(m_extrusion_flow) @@ -630,8 +617,8 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime( ToolChangeResult result; result.priming = true; - result.initial_tool = old_tool; - result.new_tool = m_current_tool; + result.initial_tool = int(old_tool); + result.new_tool = int(m_current_tool); result.print_z = this->m_z_pos; result.layer_height = this->m_layer_height; result.gcode = writer.gcode(); @@ -666,12 +653,12 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime( return results; } -WipeTower::ToolChangeResult WipeTower::tool_change(unsigned int tool, bool last_in_layer) +WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool last_in_layer) { if ( m_print_brim ) return toolchange_Brim(); - int old_tool = m_current_tool; + size_t old_tool = m_current_tool; float wipe_area = 0.f; bool last_change_in_layer = false; @@ -696,7 +683,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(unsigned int tool, bool last_ box_coordinates cleaning_box( Vec2f(m_perimeter_width / 2.f, m_perimeter_width / 2.f), m_wipe_tower_width - m_perimeter_width, - (tool != (unsigned int)(-1) ? /*m_layer_info->depth*/wipe_area+m_depth_traversed-0.5*m_perimeter_width + (tool != (unsigned int)(-1) ? /*m_layer_info->depth*/wipe_area+m_depth_traversed-0.5f*m_perimeter_width : m_wipe_tower_depth-m_perimeter_width)); WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); @@ -764,8 +751,8 @@ WipeTower::ToolChangeResult WipeTower::tool_change(unsigned int tool, bool last_ ToolChangeResult result; result.priming = false; - result.initial_tool = old_tool; - result.new_tool = m_current_tool; + result.initial_tool = int(old_tool); + result.new_tool = int(m_current_tool); result.print_z = this->m_z_pos; result.layer_height = this->m_layer_height; result.gcode = writer.gcode(); @@ -778,7 +765,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(unsigned int tool, bool last_ WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_offset) { - int old_tool = m_current_tool; + size_t old_tool = m_current_tool; const box_coordinates wipeTower_box( Vec2f::Zero(), @@ -802,7 +789,7 @@ WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_of // Extrude 4 rounds of a brim around the future wipe tower. box_coordinates box(wipeTower_box); for (size_t i = 0; i < 4; ++ i) { - box.expand(m_perimeter_width - m_layer_height*(1.f-M_PI_4)); // the brim shall have 'normal' spacing with no extra void space + box.expand(m_perimeter_width - m_layer_height*float(1.-M_PI_4)); // the brim shall have 'normal' spacing with no extra void space writer.travel (box.ld, 7000) .extrude(box.lu, 2100).extrude(box.ru) .extrude(box.rd ).extrude(box.ld); @@ -822,8 +809,8 @@ WipeTower::ToolChangeResult WipeTower::toolchange_Brim(bool sideOnly, float y_of ToolChangeResult result; result.priming = false; - result.initial_tool = old_tool; - result.new_tool = m_current_tool; + result.initial_tool = int(old_tool); + result.new_tool = int(m_current_tool); result.print_z = this->m_z_pos; result.layer_height = this->m_layer_height; result.gcode = writer.gcode(); @@ -911,8 +898,8 @@ void WipeTower::toolchange_Unload( const float x = volume_to_length(m_filpar[m_current_tool].ramming_speed[i] * 0.25f, line_width, m_layer_height); const float e = m_filpar[m_current_tool].ramming_speed[i] * 0.25f / filament_area(); // transform volume per sec to E move; const float dist = std::min(x - e_done, remaining); // distance to travel for either the next 0.25s, or to the next turnaround - const float actual_time = dist/x * 0.25; - writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0, 0, e * (dist / x), dist / (actual_time / 60.)); + const float actual_time = dist/x * 0.25f; + writer.ram(writer.x(), writer.x() + (m_left_to_right ? 1.f : -1.f) * dist, 0.f, 0.f, e * (dist / x), dist / (actual_time / 60.f)); remaining -= dist; if (remaining < WT_EPSILON) { // we reached a turning point @@ -995,8 +982,8 @@ void WipeTower::toolchange_Unload( // Change the tool, set a speed override for soluble and flex materials. void WipeTower::toolchange_Change( WipeTowerWriter &writer, - const unsigned int new_tool, - const std::string& new_material) + const size_t new_tool, + const std::string& new_material) { // Ask the writer about how much of the old filament we consumed: if (m_current_tool < m_used_filament_length.size()) @@ -1091,21 +1078,21 @@ void WipeTower::toolchange_Wipe( float traversed_x = writer.x(); if (m_left_to_right) - writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff); + writer.extrude(xr - (i % 4 == 0 ? 0 : 1.5f*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff); else - writer.extrude(xl + (i % 4 == 1 ? 0 : 1.5*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff); + writer.extrude(xl + (i % 4 == 1 ? 0 : 1.5f*m_perimeter_width), writer.y(), wipe_speed * wipe_coeff); - if (writer.y()+EPSILON > cleaning_box.lu.y()-0.5f*m_perimeter_width) + if (writer.y()+float(EPSILON) > cleaning_box.lu.y()-0.5f*m_perimeter_width) break; // in case next line would not fit traversed_x -= writer.x(); - x_to_wipe -= fabs(traversed_x); + x_to_wipe -= std::abs(traversed_x); if (x_to_wipe < WT_EPSILON) { - writer.travel(m_left_to_right ? xl + 1.5*m_perimeter_width : xr - 1.5*m_perimeter_width, writer.y(), 7200); + writer.travel(m_left_to_right ? xl + 1.5f*m_perimeter_width : xr - 1.5f*m_perimeter_width, writer.y(), 7200); break; } // stepping to the next line: - writer.extrude(writer.x() + (i % 4 == 0 ? -1.f : (i % 4 == 1 ? 1.f : 0.f)) * 1.5*m_perimeter_width, writer.y() + dy); + writer.extrude(writer.x() + (i % 4 == 0 ? -1.f : (i % 4 == 1 ? 1.f : 0.f)) * 1.5f*m_perimeter_width, writer.y() + dy); m_left_to_right = !m_left_to_right; } @@ -1128,7 +1115,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() // Otherwise the caller would likely travel to the wipe tower in vain. assert(! this->layer_finished()); - int old_tool = m_current_tool; + size_t old_tool = m_current_tool; WipeTowerWriter writer(m_layer_height, m_perimeter_width, m_gcode_flavor, m_filpar); writer.set_extrusion_flow(m_extrusion_flow) @@ -1188,7 +1175,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() writer.travel(fill_box.ld + Vec2f(m_perimeter_width * 2, 0.f)) .extrude(fill_box.lu + Vec2f(m_perimeter_width * 2, 0.f), 2900 * speed_factor); - const int n = 1+(right-left)/(m_bridging); + const int n = 1+int((right-left)/m_bridging); const float dx = (right-left)/n; for (int i=1;i<=n;++i) { float x=left+dx*i; @@ -1211,8 +1198,8 @@ WipeTower::ToolChangeResult WipeTower::finish_layer() ToolChangeResult result; result.priming = false; - result.initial_tool = old_tool; - result.new_tool = m_current_tool; + result.initial_tool = int(old_tool); + result.new_tool = int(m_current_tool); result.print_z = this->m_z_pos; result.layer_height = this->m_layer_height; result.gcode = writer.gcode(); @@ -1267,7 +1254,7 @@ void WipeTower::plan_tower() for (auto& layer : m_plan) layer.depth = 0.f; - for (int layer_index = m_plan.size() - 1; layer_index >= 0; --layer_index) + for (int layer_index = int(m_plan.size()) - 1; layer_index >= 0; --layer_index) { float this_layer_depth = std::max(m_plan[layer_index].depth, m_plan[layer_index].toolchanges_depth()); m_plan[layer_index].depth = this_layer_depth; diff --git a/src/libslic3r/GCode/WipeTower.hpp b/src/libslic3r/GCode/WipeTower.hpp index fab75c5e6..5477aa609 100644 --- a/src/libslic3r/GCode/WipeTower.hpp +++ b/src/libslic3r/GCode/WipeTower.hpp @@ -7,21 +7,21 @@ #include <utility> #include <algorithm> -#include "libslic3r/PrintConfig.hpp" - +#include "libslic3r/Point.hpp" namespace Slic3r { class WipeTowerWriter; - +class PrintConfig; +enum GCodeFlavor : unsigned char; class WipeTower { public: struct Extrusion - { + { Extrusion(const Vec2f &pos, float width, unsigned int tool) : pos(pos), width(width), tool(tool) {} // End position of this extrusion. Vec2f pos; @@ -79,7 +79,6 @@ public: // width -- width of wipe tower in mm ( default 60 mm - leave as it is ) // wipe_area -- space available for one toolchange in mm WipeTower(const PrintConfig& config, const std::vector<std::vector<float>>& wiping_matrix, size_t initial_tool); - virtual ~WipeTower() {} // Set the extruder properties. @@ -149,7 +148,7 @@ public: // Returns gcode for a toolchange and a final print head position. // On the first layer, extrude a brim around the future wipe tower first. - ToolChangeResult tool_change(unsigned int new_tool, bool last_in_layer); + ToolChangeResult tool_change(size_t new_tool, bool last_in_layer); // Fill the unfilled space with a sparse infill. // Call this method only if layer_finished() is false. @@ -195,7 +194,7 @@ private: const bool m_peters_wipe_tower = false; // sparse wipe tower inspired by Peter's post processor - not finished yet const float Width_To_Nozzle_Ratio = 1.25f; // desired line width (oval) in multiples of nozzle diameter - may not be actually neccessary to adjust const float WT_EPSILON = 1e-3f; - const float filament_area() const { + float filament_area() const { return m_filpar[0].filament_area; // all extruders are assumed to have the same filament diameter at this point } @@ -244,7 +243,7 @@ private: bool m_print_brim = true; // A fill-in direction (positive Y, negative Y) alternates with each layer. wipe_shape m_current_shape = SHAPE_NORMAL; - unsigned int m_current_tool = 0; + size_t m_current_tool = 0; const std::vector<std::vector<float>> wipe_volumes; float m_depth_traversed = 0.f; // Current y position at the wipe tower. @@ -309,13 +308,13 @@ private: // to store information about tool changes for a given layer struct WipeTowerInfo{ struct ToolChange { - unsigned int old_tool; - unsigned int new_tool; + size_t old_tool; + size_t new_tool; float required_depth; float ramming_depth; float first_wipe_line; float wipe_volume; - ToolChange(unsigned int old, unsigned int newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f) + ToolChange(size_t old, size_t newtool, float depth=0.f, float ramming_depth=0.f, float fwl=0.f, float wv=0.f) : old_tool{old}, new_tool{newtool}, required_depth{depth}, ramming_depth{ramming_depth}, first_wipe_line{fwl}, wipe_volume{wv} {} }; float z; // z position of the layer @@ -350,7 +349,7 @@ private: void toolchange_Change( WipeTowerWriter &writer, - const unsigned int new_tool, + const size_t new_tool, const std::string& new_material); void toolchange_Load( diff --git a/src/libslic3r/GCodeTimeEstimator.cpp b/src/libslic3r/GCodeTimeEstimator.cpp index 00b409344..4693ba9e6 100644 --- a/src/libslic3r/GCodeTimeEstimator.cpp +++ b/src/libslic3r/GCodeTimeEstimator.cpp @@ -173,9 +173,7 @@ namespace Slic3r { const std::string GCodeTimeEstimator::Normal_Last_M73_Output_Placeholder_Tag = "; _TE_NORMAL_LAST_M73_OUTPUT_PLACEHOLDER"; const std::string GCodeTimeEstimator::Silent_Last_M73_Output_Placeholder_Tag = "; _TE_SILENT_LAST_M73_OUTPUT_PLACEHOLDER"; - // temporary human readable form to use until not removed from gcode by the new post-process method const std::string GCodeTimeEstimator::Color_Change_Tag = "PRINT_COLOR_CHANGE"; -// const std::string GCodeTimeEstimator::Color_Change_Tag = "_TE_COLOR_CHANGE"; GCodeTimeEstimator::GCodeTimeEstimator(EMode mode) : m_mode(mode) @@ -273,130 +271,138 @@ namespace Slic3r { #endif // ENABLE_MOVE_STATS } - bool GCodeTimeEstimator::post_process_remaining_times(const std::string& filename, float interval) + bool GCodeTimeEstimator::post_process(const std::string& filename, float interval_sec, const PostProcessData* const normal_mode, const PostProcessData* const silent_mode) { boost::nowide::ifstream in(filename); if (!in.good()) - throw std::runtime_error(std::string("Remaining times export failed.\nCannot open file for reading.\n")); + throw std::runtime_error(std::string("Time estimator post process export failed.\nCannot open file for reading.\n")); - std::string path_tmp = filename + ".times"; + std::string path_tmp = filename + ".postprocess"; FILE* out = boost::nowide::fopen(path_tmp.c_str(), "wb"); if (out == nullptr) - throw std::runtime_error(std::string("Remaining times export failed.\nCannot open file for writing.\n")); + throw std::runtime_error(std::string("Time estimator post process export failed.\nCannot open file for writing.\n")); - std::string time_mask; - switch (m_mode) - { - default: - case Normal: - { - time_mask = "M73 P%s R%s\n"; - break; - } - case Silent: - { - time_mask = "M73 Q%s S%s\n"; - break; - } - } + std::string normal_time_mask = "M73 P%s R%s\n"; + std::string silent_time_mask = "M73 Q%s S%s\n"; + char line_M73[64]; - unsigned int g1_lines_count = 0; - float last_recorded_time = 0.0f; std::string gcode_line; // buffer line to export only when greater than 64K to reduce writing calls std::string export_line; - char time_line[64]; - G1LineIdToBlockIdMap::const_iterator it_line_id = m_g1_line_ids.begin(); - while (std::getline(in, gcode_line)) + + // helper function to write to disk + auto write_string = [&](const std::string& str) { + fwrite((const void*)export_line.c_str(), 1, export_line.length(), out); + if (ferror(out)) + { + in.close(); + fclose(out); + boost::nowide::remove(path_tmp.c_str()); + throw std::runtime_error(std::string("Time estimator post process export failed.\nIs the disk full?\n")); + } + export_line.clear(); + }; + + GCodeReader parser; + unsigned int g1_lines_count = 0; + int normal_g1_line_id = 0; + float normal_last_recorded_time = 0.0f; + int silent_g1_line_id = 0; + float silent_last_recorded_time = 0.0f; + + // helper function to process g1 lines + auto process_g1_line = [&](const PostProcessData* const data, const GCodeReader::GCodeLine& line, int& g1_line_id, float& last_recorded_time, const std::string& time_mask) { + if (data == nullptr) + return; + + assert((g1_line_id >= (int)data->g1_line_ids.size()) || (data->g1_line_ids[g1_line_id].first >= g1_lines_count)); + const Block* block = nullptr; + const G1LineIdToBlockId& map_item = data->g1_line_ids[g1_line_id]; + if ((g1_line_id < (int)data->g1_line_ids.size()) && (map_item.first == g1_lines_count)) + { + if (line.has_e() && (map_item.second < (unsigned int)data->blocks.size())) + block = &data->blocks[map_item.second]; + ++g1_line_id; + } + + if ((block != nullptr) && (block->elapsed_time != -1.0f)) + { + float block_remaining_time = data->time - block->elapsed_time; + if (std::abs(last_recorded_time - block_remaining_time) > interval_sec) + { + sprintf(line_M73, time_mask.c_str(), std::to_string((int)(100.0f * block->elapsed_time / data->time)).c_str(), _get_time_minutes(block_remaining_time).c_str()); + gcode_line += line_M73; + + last_recorded_time = block_remaining_time; + } + } + }; + + while (std::getline(in, gcode_line)) { if (!in.good()) { fclose(out); - throw std::runtime_error(std::string("Remaining times export failed.\nError while reading from file.\n")); + throw std::runtime_error(std::string("Time estimator post process export failed.\nError while reading from file.\n")); } + // check tags + // remove color change tag + if (gcode_line == "; " + Color_Change_Tag) + continue; + // replaces placeholders for initial line M73 with the real lines - if (((m_mode == Normal) && (gcode_line == Normal_First_M73_Output_Placeholder_Tag)) || - ((m_mode == Silent) && (gcode_line == Silent_First_M73_Output_Placeholder_Tag))) + if ((normal_mode != nullptr) && (gcode_line == Normal_First_M73_Output_Placeholder_Tag)) { - sprintf(time_line, time_mask.c_str(), "0", _get_time_minutes(m_time).c_str()); - gcode_line = time_line; + sprintf(line_M73, normal_time_mask.c_str(), "0", _get_time_minutes(normal_mode->time).c_str()); + gcode_line = line_M73; + } + else if ((silent_mode != nullptr) && (gcode_line == Silent_First_M73_Output_Placeholder_Tag)) + { + sprintf(line_M73, silent_time_mask.c_str(), "0", _get_time_minutes(silent_mode->time).c_str()); + gcode_line = line_M73; } // replaces placeholders for final line M73 with the real lines - else if (((m_mode == Normal) && (gcode_line == Normal_Last_M73_Output_Placeholder_Tag)) || - ((m_mode == Silent) && (gcode_line == Silent_Last_M73_Output_Placeholder_Tag))) + else if ((normal_mode != nullptr) && (gcode_line == Normal_Last_M73_Output_Placeholder_Tag)) + { + sprintf(line_M73, normal_time_mask.c_str(), "100", "0"); + gcode_line = line_M73; + } + else if ((silent_mode != nullptr) && (gcode_line == Silent_Last_M73_Output_Placeholder_Tag)) { - sprintf(time_line, time_mask.c_str(), "100", "0"); - gcode_line = time_line; + sprintf(line_M73, silent_time_mask.c_str(), "100", "0"); + gcode_line = line_M73; } else - gcode_line += "\n"; - + gcode_line += "\n"; // add remaining time lines where needed - m_parser.parse_line(gcode_line, - [this, &it_line_id, &g1_lines_count, &last_recorded_time, &time_line, &gcode_line, time_mask, interval](GCodeReader& reader, const GCodeReader::GCodeLine& line) - { - if (line.cmd_is("G1")) + parser.parse_line(gcode_line, + [&](GCodeReader& reader, const GCodeReader::GCodeLine& line) { - ++g1_lines_count; - - assert(it_line_id == m_g1_line_ids.end() || it_line_id->first >= g1_lines_count); - - const Block *block = nullptr; - if (it_line_id != m_g1_line_ids.end() && it_line_id->first == g1_lines_count) { - if (line.has_e() && it_line_id->second < (unsigned int)m_blocks.size()) - block = &m_blocks[it_line_id->second]; - ++it_line_id; - } - - if (block != nullptr && block->elapsed_time != -1.0f) { - float block_remaining_time = m_time - block->elapsed_time; - if (std::abs(last_recorded_time - block_remaining_time) > interval) - { - sprintf(time_line, time_mask.c_str(), std::to_string((int)(100.0f * block->elapsed_time / m_time)).c_str(), _get_time_minutes(block_remaining_time).c_str()); - gcode_line += time_line; - - last_recorded_time = block_remaining_time; - } + if (line.cmd_is("G1")) + { + ++g1_lines_count; + process_g1_line(silent_mode, line, silent_g1_line_id, silent_last_recorded_time, silent_time_mask); + process_g1_line(normal_mode, line, normal_g1_line_id, normal_last_recorded_time, normal_time_mask); } - } - }); + }); export_line += gcode_line; if (export_line.length() > 65535) - { - fwrite((const void*)export_line.c_str(), 1, export_line.length(), out); - if (ferror(out)) - { - in.close(); - fclose(out); - boost::nowide::remove(path_tmp.c_str()); - throw std::runtime_error(std::string("Remaining times export failed.\nIs the disk full?\n")); - } - export_line.clear(); - } + write_string(export_line); } - if (export_line.length() > 0) - { - fwrite((const void*)export_line.c_str(), 1, export_line.length(), out); - if (ferror(out)) - { - in.close(); - fclose(out); - boost::nowide::remove(path_tmp.c_str()); - throw std::runtime_error(std::string("Remaining times export failed.\nIs the disk full?\n")); - } - } + if (!export_line.empty()) + write_string(export_line); fclose(out); in.close(); if (rename_file(path_tmp, filename)) throw std::runtime_error(std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + filename + '\n' + - "Is " + path_tmp + " locked?" + '\n'); + "Is " + path_tmp + " locked?" + '\n'); return true; } diff --git a/src/libslic3r/GCodeTimeEstimator.hpp b/src/libslic3r/GCodeTimeEstimator.hpp index dd5d14b57..d9f3bc211 100644 --- a/src/libslic3r/GCodeTimeEstimator.hpp +++ b/src/libslic3r/GCodeTimeEstimator.hpp @@ -213,9 +213,19 @@ namespace Slic3r { typedef std::map<Block::EMoveType, MoveStats> MovesStatsMap; #endif // ENABLE_MOVE_STATS + public: typedef std::pair<unsigned int, unsigned int> G1LineIdToBlockId; typedef std::vector<G1LineIdToBlockId> G1LineIdToBlockIdMap; + struct PostProcessData + { + const G1LineIdToBlockIdMap& g1_line_ids; + const BlocksList& blocks; + float time; + + PostProcessData(const G1LineIdToBlockIdMap& g1_line_ids, const BlocksList& blocks, float time) : g1_line_ids(g1_line_ids), blocks(blocks), time(time) {} + }; + private: EMode m_mode; GCodeReader m_parser; @@ -263,11 +273,12 @@ namespace Slic3r { void calculate_time_from_lines(const std::vector<std::string>& gcode_lines); // Process the gcode contained in the file with the given filename, - // placing in it new lines (M73) containing the remaining time, at the given interval in seconds - // and saving the result back in the same file - // This time estimator should have been already used to calculate the time estimate for the gcode - // contained in the given file before to call this method - bool post_process_remaining_times(const std::string& filename, float interval_sec); + // replacing placeholders with correspondent new lines M73 + // placing new lines M73 (containing the remaining time) where needed (in dependence of the given interval in seconds) + // and removing working tags (as those used for color changes) + // if normal_mode == nullptr no M73 line will be added for normal mode + // if silent_mode == nullptr no M73 line will be added for silent mode + static bool post_process(const std::string& filename, float interval_sec, const PostProcessData* const normal_mode, const PostProcessData* const silent_mode); // Set current position on the given axis with the given value void set_axis_position(EAxis axis, float position); @@ -362,6 +373,8 @@ namespace Slic3r { // Return an estimate of the memory consumed by the time estimator. size_t memory_used() const; + PostProcessData get_post_process_data() const { return PostProcessData(m_g1_line_ids, m_blocks, m_time); } + private: void _reset(); void _reset_time(); diff --git a/src/libslic3r/Layer.cpp b/src/libslic3r/Layer.cpp index a8160867a..4ac64b777 100644 --- a/src/libslic3r/Layer.cpp +++ b/src/libslic3r/Layer.cpp @@ -121,7 +121,6 @@ void Layer::make_perimeters() for (LayerRegionPtrs::const_iterator it = layerm + 1; it != m_regions.end(); ++it) { LayerRegion* other_layerm = *it; const PrintRegionConfig &other_config = other_layerm->region()->config(); - if (config.perimeter_extruder == other_config.perimeter_extruder && config.perimeters == other_config.perimeters && config.perimeter_speed == other_config.perimeter_speed @@ -130,7 +129,8 @@ void Layer::make_perimeters() && config.overhangs == other_config.overhangs && config.opt_serialize("perimeter_extrusion_width") == other_config.opt_serialize("perimeter_extrusion_width") && config.thin_walls == other_config.thin_walls - && config.external_perimeters_first == other_config.external_perimeters_first) { + && config.external_perimeters_first == other_config.external_perimeters_first + && config.infill_overlap == other_config.infill_overlap) { layerms.push_back(other_layerm); done[it - m_regions.begin()] = true; } @@ -142,12 +142,17 @@ void Layer::make_perimeters() (*layerm)->fill_expolygons = to_expolygons((*layerm)->fill_surfaces.surfaces); } else { SurfaceCollection new_slices; + // Use the region with highest infill rate, as the make_perimeters() function below decides on the gap fill based on the infill existence. + LayerRegion *layerm_config = layerms.front(); { // group slices (surfaces) according to number of extra perimeters std::map<unsigned short, Surfaces> slices; // extra_perimeters => [ surface, surface... ] - for (LayerRegion *layerm : layerms) + for (LayerRegion *layerm : layerms) { for (Surface &surface : layerm->slices.surfaces) slices[surface.extra_perimeters].emplace_back(surface); + if (layerm->region()->config().fill_density > layerm_config->region()->config().fill_density) + layerm_config = layerm; + } // merge the surfaces assigned to each group for (std::pair<const unsigned short,Surfaces> &surfaces_with_extra_perimeters : slices) new_slices.append(union_ex(surfaces_with_extra_perimeters.second, true), surfaces_with_extra_perimeters.second.front()); @@ -155,7 +160,7 @@ void Layer::make_perimeters() // make perimeters SurfaceCollection fill_surfaces; - (*layerm)->make_perimeters(new_slices, &fill_surfaces); + layerm_config->make_perimeters(new_slices, &fill_surfaces); // assign fill_surfaces to each layer if (!fill_surfaces.surfaces.empty()) { @@ -171,21 +176,6 @@ void Layer::make_perimeters() BOOST_LOG_TRIVIAL(trace) << "Generating perimeters for layer " << this->id() << " - Done"; } -void Layer::make_fills() -{ - #ifdef SLIC3R_DEBUG - printf("Making fills for layer " PRINTF_ZU "\n", this->id()); - #endif - for (LayerRegion *layerm : m_regions) { - layerm->fills.clear(); - make_fill(*layerm, layerm->fills); -#ifndef NDEBUG - for (size_t i = 0; i < layerm->fills.entities.size(); ++ i) - assert(dynamic_cast<ExtrusionEntityCollection*>(layerm->fills.entities[i]) != NULL); -#endif - } -} - void Layer::export_region_slices_to_svg(const char *path) const { BoundingBox bbox; diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index 6297b49d0..555017207 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -62,7 +62,7 @@ public: void slices_to_fill_surfaces_clipped(); void prepare_fill_surfaces(); void make_perimeters(const SurfaceCollection &slices, SurfaceCollection* fill_surfaces); - void process_external_surfaces(const Layer* lower_layer); + void process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered); double infill_area_threshold() const; // Trim surfaces by trimming polygons. Used by the elephant foot compensation at the 1st layer. void trim_surfaces(const Polygons &trimming_polygons); diff --git a/src/libslic3r/LayerRegion.cpp b/src/libslic3r/LayerRegion.cpp index caf8dc20f..6fc0b4e37 100644 --- a/src/libslic3r/LayerRegion.cpp +++ b/src/libslic3r/LayerRegion.cpp @@ -86,11 +86,12 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, SurfaceCollec //#define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtMiter, 1.5 #define EXTERNAL_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0. -void LayerRegion::process_external_surfaces(const Layer* lower_layer) +void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Polygons *lower_layer_covered) { - const Surfaces &surfaces = this->fill_surfaces.surfaces; - const double margin = scale_(EXTERNAL_INFILL_MARGIN); - + const Surfaces &surfaces = this->fill_surfaces.surfaces; + const bool has_infill = this->region()->config().fill_density.value > 0.; + const float margin = float(scale_(EXTERNAL_INFILL_MARGIN)); + #ifdef SLIC3R_DEBUG_SLICE_PROCESSING export_region_fill_surfaces_to_svg_debug("3_process_external_surfaces-initial"); #endif /* SLIC3R_DEBUG_SLICE_PROCESSING */ @@ -106,36 +107,44 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) // Internal surfaces, not grown. Surfaces internal; // Areas, where an infill of various types (top, bottom, bottom bride, sparse, void) could be placed. - //FIXME if non zero infill, then fill_boundaries could be cheaply initialized from layerm->fill_expolygons. - Polygons fill_boundaries; + Polygons fill_boundaries = to_polygons(this->fill_expolygons); + Polygons lower_layer_covered_tmp; // Collect top surfaces and internal surfaces. // Collect fill_boundaries: If we're slicing with no infill, we can't extend external surfaces over non-existent infill. // This loop destroys the surfaces (aliasing this->fill_surfaces.surfaces) by moving into top/internal/fill_boundaries! + { - // bottom_polygons are used to trim inflated top surfaces. - fill_boundaries.reserve(number_polygons(surfaces)); - bool has_infill = this->region()->config().fill_density.value > 0.; + // Voids are sparse infills if infill rate is zero. + Polygons voids; for (const Surface &surface : this->fill_surfaces.surfaces) { if (surface.surface_type == stTop) { // Collect the top surfaces, inflate them and trim them by the bottom surfaces. // This gives the priority to bottom surfaces. - surfaces_append(top, offset_ex(surface.expolygon, float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS), surface); - } else if (surface.surface_type == stBottom || (surface.surface_type == stBottomBridge && lower_layer == NULL)) { + surfaces_append(top, offset_ex(surface.expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS), surface); + } else if (surface.surface_type == stBottom || (surface.surface_type == stBottomBridge && lower_layer == nullptr)) { // Grown by 3mm. - surfaces_append(bottom, offset_ex(surface.expolygon, float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS), surface); + surfaces_append(bottom, offset_ex(surface.expolygon, margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS), surface); } else if (surface.surface_type == stBottomBridge) { if (! surface.empty()) - bridges.push_back(surface); + bridges.emplace_back(surface); + } + if (surface.is_internal()) { + assert(surface.surface_type == stInternal); + if (! has_infill && lower_layer != nullptr) + polygons_append(voids, surface.expolygon); + internal.emplace_back(std::move(surface)); + } + } + if (! has_infill && lower_layer != nullptr && ! voids.empty()) { + // Remove voids from fill_boundaries, that are not supported by the layer below. + if (lower_layer_covered == nullptr) { + lower_layer_covered = &lower_layer_covered_tmp; + lower_layer_covered_tmp = to_polygons(lower_layer->slices.expolygons); } - bool internal_surface = surface.surface_type != stTop && ! surface.is_bottom(); - if (has_infill || surface.surface_type != stInternal) { - if (internal_surface) - // Make a copy as the following line uses the move semantics. - internal.push_back(surface); - polygons_append(fill_boundaries, std::move(surface.expolygon)); - } else if (internal_surface) - internal.push_back(std::move(surface)); + if (! lower_layer_covered->empty()) + voids = diff(voids, *lower_layer_covered); + fill_boundaries = diff(fill_boundaries, voids); } } @@ -184,9 +193,9 @@ void LayerRegion::process_external_surfaces(const Layer* lower_layer) break; } // Grown by 3mm. - Polygons polys = offset(to_polygons(bridges[i].expolygon), float(margin), EXTERNAL_SURFACES_OFFSET_PARAMETERS); + Polygons polys = offset(to_polygons(bridges[i].expolygon), margin, EXTERNAL_SURFACES_OFFSET_PARAMETERS); if (idx_island == -1) { - printf("Bridge did not fall into the source region!\r\n"); + BOOST_LOG_TRIVIAL(trace) << "Bridge did not fall into the source region!"; } else { // Found an island, to which this bridge region belongs. Trim it, polys = intersection(polys, to_polygons(fill_boundaries_ex[idx_island])); @@ -356,26 +365,23 @@ void LayerRegion::prepare_fill_surfaces() // if no solid layers are requested, turn top/bottom surfaces to internal if (this->region()->config().top_solid_layers == 0) { - for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) - if (surface->surface_type == stTop) - surface->surface_type = (this->layer()->object()->config().infill_only_where_needed) ? - stInternalVoid : stInternal; + for (Surface &surface : this->fill_surfaces.surfaces) + if (surface.is_top()) + surface.surface_type = this->layer()->object()->config().infill_only_where_needed ? stInternalVoid : stInternal; } if (this->region()->config().bottom_solid_layers == 0) { - for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) { - if (surface->surface_type == stBottom || surface->surface_type == stBottomBridge) - surface->surface_type = stInternal; - } + for (Surface &surface : this->fill_surfaces.surfaces) + if (surface.is_bottom()) // (surface.surface_type == stBottom) + surface.surface_type = stInternal; } - + // turn too small internal regions into solid regions according to the user setting if (this->region()->config().fill_density.value > 0) { // scaling an area requires two calls! double min_area = scale_(scale_(this->region()->config().solid_infill_below_area.value)); - for (Surfaces::iterator surface = this->fill_surfaces.surfaces.begin(); surface != this->fill_surfaces.surfaces.end(); ++surface) { - if (surface->surface_type == stInternal && surface->area() <= min_area) - surface->surface_type = stInternalSolid; - } + for (Surface &surface : this->fill_surfaces.surfaces) + if (surface.surface_type == stInternal && surface.area() <= min_area) + surface.surface_type = stInternalSolid; } #ifdef SLIC3R_DEBUG_SLICE_PROCESSING diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 422467e08..19f032b1c 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -674,7 +674,7 @@ private: explicit ModelInstance(ModelObject* object) : print_volume_state(PVS_Inside), printable(true), object(object) { assert(this->id().valid()); } // Constructor, which assigns a new unique ID. explicit ModelInstance(ModelObject *object, const ModelInstance &other) : - m_transformation(other.m_transformation), print_volume_state(PVS_Inside), printable(true), object(object) {assert(this->id().valid() && this->id() != other.id());} + m_transformation(other.m_transformation), print_volume_state(PVS_Inside), printable(other.printable), object(object) { assert(this->id().valid() && this->id() != other.id()); } explicit ModelInstance(ModelInstance &&rhs) = delete; ModelInstance& operator=(const ModelInstance &rhs) = delete; diff --git a/src/libslic3r/PerimeterGenerator.cpp b/src/libslic3r/PerimeterGenerator.cpp index 4dac192ad..0c16f4a1d 100644 --- a/src/libslic3r/PerimeterGenerator.cpp +++ b/src/libslic3r/PerimeterGenerator.cpp @@ -6,21 +6,240 @@ namespace Slic3r { +static ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, Flow &flow, const float tolerance) +{ + ExtrusionPaths paths; + ExtrusionPath path(role); + ThickLines lines = thick_polyline.thicklines(); + + for (int i = 0; i < (int)lines.size(); ++i) { + const ThickLine& line = lines[i]; + + const coordf_t line_len = line.length(); + if (line_len < SCALED_EPSILON) continue; + + double thickness_delta = fabs(line.a_width - line.b_width); + if (thickness_delta > tolerance) { + const unsigned int segments = (unsigned int)ceil(thickness_delta / tolerance); + const coordf_t seg_len = line_len / segments; + Points pp; + std::vector<coordf_t> width; + { + pp.push_back(line.a); + width.push_back(line.a_width); + for (size_t j = 1; j < segments; ++j) { + pp.push_back((line.a.cast<double>() + (line.b - line.a).cast<double>().normalized() * (j * seg_len)).cast<coord_t>()); + + coordf_t w = line.a_width + (j*seg_len) * (line.b_width-line.a_width) / line_len; + width.push_back(w); + width.push_back(w); + } + pp.push_back(line.b); + width.push_back(line.b_width); + + assert(pp.size() == segments + 1u); + assert(width.size() == segments*2); + } + + // delete this line and insert new ones + lines.erase(lines.begin() + i); + for (size_t j = 0; j < segments; ++j) { + ThickLine new_line(pp[j], pp[j+1]); + new_line.a_width = width[2*j]; + new_line.b_width = width[2*j+1]; + lines.insert(lines.begin() + i + j, new_line); + } + + -- i; + continue; + } + + const double w = fmax(line.a_width, line.b_width); + if (path.polyline.points.empty()) { + path.polyline.append(line.a); + path.polyline.append(line.b); + // Convert from spacing to extrusion width based on the extrusion model + // of a square extrusion ended with semi circles. + flow.width = unscale<float>(w) + flow.height * float(1. - 0.25 * PI); + #ifdef SLIC3R_DEBUG + printf(" filling %f gap\n", flow.width); + #endif + path.mm3_per_mm = flow.mm3_per_mm(); + path.width = flow.width; + path.height = flow.height; + } else { + thickness_delta = fabs(scale_(flow.width) - w); + if (thickness_delta <= tolerance) { + // the width difference between this line and the current flow width is + // within the accepted tolerance + path.polyline.append(line.b); + } else { + // we need to initialize a new line + paths.emplace_back(std::move(path)); + path = ExtrusionPath(role); + -- i; + } + } + } + if (path.polyline.is_valid()) + paths.emplace_back(std::move(path)); + return paths; +} + +static ExtrusionEntityCollection variable_width(const ThickPolylines& polylines, ExtrusionRole role, Flow flow) +{ + // This value determines granularity of adaptive width, as G-code does not allow + // variable extrusion within a single move; this value shall only affect the amount + // of segments, and any pruning shall be performed before we apply this tolerance. + ExtrusionEntityCollection coll; + const float tolerance = float(scale_(0.05)); + for (const ThickPolyline &p : polylines) { + ExtrusionPaths paths = thick_polyline_to_extrusion_paths(p, role, flow, tolerance); + // Append paths to collection. + if (! paths.empty()) { + if (paths.front().first_point() == paths.back().last_point()) + coll.append(ExtrusionLoop(std::move(paths))); + else + coll.append(std::move(paths)); + } + } + return coll; +} + +// Hierarchy of perimeters. +class PerimeterGeneratorLoop { +public: + // Polygon of this contour. + Polygon polygon; + // Is it a contour or a hole? + // Contours are CCW oriented, holes are CW oriented. + bool is_contour; + // Depth in the hierarchy. External perimeter has depth = 0. An external perimeter could be both a contour and a hole. + unsigned short depth; + // Children contour, may be both CCW and CW oriented (outer contours or holes). + std::vector<PerimeterGeneratorLoop> children; + + PerimeterGeneratorLoop(Polygon polygon, unsigned short depth, bool is_contour) : + polygon(polygon), is_contour(is_contour), depth(depth) {} + // External perimeter. It may be CCW or CW oriented (outer contour or hole contour). + bool is_external() const { return this->depth == 0; } + // An island, which may have holes, but it does not have another internal island. + bool is_internal_contour() const; +}; + +typedef std::vector<PerimeterGeneratorLoop> PerimeterGeneratorLoops; + +static ExtrusionEntityCollection traverse_loops(const PerimeterGenerator &perimeter_generator, const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls) +{ + // loops is an arrayref of ::Loop objects + // turn each one into an ExtrusionLoop object + ExtrusionEntityCollection coll; + for (const PerimeterGeneratorLoop &loop : loops) { + bool is_external = loop.is_external(); + + ExtrusionRole role; + ExtrusionLoopRole loop_role; + role = is_external ? erExternalPerimeter : erPerimeter; + if (loop.is_internal_contour()) { + // Note that we set loop role to ContourInternalPerimeter + // also when loop is both internal and external (i.e. + // there's only one contour loop). + loop_role = elrContourInternalPerimeter; + } else { + loop_role = elrDefault; + } + + // detect overhanging/bridging perimeters + ExtrusionPaths paths; + if (perimeter_generator.config->overhangs && perimeter_generator.layer_id > 0 + && !(perimeter_generator.object_config->support_material && perimeter_generator.object_config->support_material_contact_distance.value == 0)) { + // get non-overhang paths by intersecting this loop with the grown lower slices + extrusion_paths_append( + paths, + intersection_pl(loop.polygon, perimeter_generator.lower_slices_polygons()), + role, + is_external ? perimeter_generator.ext_mm3_per_mm() : perimeter_generator.mm3_per_mm(), + is_external ? perimeter_generator.ext_perimeter_flow.width : perimeter_generator.perimeter_flow.width, + (float)perimeter_generator.layer_height); + + // get overhang paths by checking what parts of this loop fall + //Â outside the grown lower slices (thus where the distance between + // the loop centerline and original lower slices is >= half nozzle diameter + extrusion_paths_append( + paths, + diff_pl(loop.polygon, perimeter_generator.lower_slices_polygons()), + erOverhangPerimeter, + perimeter_generator.mm3_per_mm_overhang(), + perimeter_generator.overhang_flow.width, + perimeter_generator.overhang_flow.height); + + // reapply the nearest point search for starting point + // We allow polyline reversal because Clipper may have randomly + // reversed polylines during clipping. + paths = (ExtrusionPaths)ExtrusionEntityCollection(paths).chained_path(); + } else { + ExtrusionPath path(role); + path.polyline = loop.polygon.split_at_first_point(); + path.mm3_per_mm = is_external ? perimeter_generator.ext_mm3_per_mm() : perimeter_generator.mm3_per_mm(); + path.width = is_external ? perimeter_generator.ext_perimeter_flow.width : perimeter_generator.perimeter_flow.width; + path.height = (float)perimeter_generator.layer_height; + paths.push_back(path); + } + + coll.append(ExtrusionLoop(paths, loop_role)); + } + + // Append thin walls to the nearest-neighbor search (only for first iteration) + if (! thin_walls.empty()) { + ExtrusionEntityCollection tw = variable_width(thin_walls, erExternalPerimeter, perimeter_generator.ext_perimeter_flow); + coll.append(tw.entities); + thin_walls.clear(); + } + + // Sort entities into a new collection using a nearest-neighbor search, + // preserving the original indices which are useful for detecting thin walls. + ExtrusionEntityCollection sorted_coll; + coll.chained_path(&sorted_coll, false, erMixed, &sorted_coll.orig_indices); + + // traverse children and build the final collection + ExtrusionEntityCollection entities; + for (const size_t &idx : sorted_coll.orig_indices) { + if (idx >= loops.size()) { + // This is a thin wall. Let's get it from the sorted collection as it might have been reversed. + entities.append(std::move(*sorted_coll.entities[&idx - &sorted_coll.orig_indices.front()])); + } else { + const PerimeterGeneratorLoop &loop = loops[idx]; + ExtrusionLoop eloop = *dynamic_cast<ExtrusionLoop*>(coll.entities[idx]); + ExtrusionEntityCollection children = traverse_loops(perimeter_generator, loop.children, thin_walls); + if (loop.is_contour) { + eloop.make_counter_clockwise(); + entities.append(std::move(children.entities)); + entities.append(std::move(eloop)); + } else { + eloop.make_clockwise(); + entities.append(std::move(eloop)); + entities.append(std::move(children.entities)); + } + } + } + return entities; +} + void PerimeterGenerator::process() { // other perimeters - this->_mm3_per_mm = this->perimeter_flow.mm3_per_mm(); + m_mm3_per_mm = this->perimeter_flow.mm3_per_mm(); coord_t perimeter_width = this->perimeter_flow.scaled_width(); coord_t perimeter_spacing = this->perimeter_flow.scaled_spacing(); // external perimeters - this->_ext_mm3_per_mm = this->ext_perimeter_flow.mm3_per_mm(); + m_ext_mm3_per_mm = this->ext_perimeter_flow.mm3_per_mm(); coord_t ext_perimeter_width = this->ext_perimeter_flow.scaled_width(); coord_t ext_perimeter_spacing = this->ext_perimeter_flow.scaled_spacing(); coord_t ext_perimeter_spacing2 = this->ext_perimeter_flow.scaled_spacing(this->perimeter_flow); // overhang perimeters - this->_mm3_per_mm_overhang = this->overhang_flow.mm3_per_mm(); + m_mm3_per_mm_overhang = this->overhang_flow.mm3_per_mm(); // solid infill coord_t solid_infill_spacing = this->solid_infill_flow.scaled_spacing(); @@ -35,16 +254,17 @@ void PerimeterGenerator::process() // which is the spacing between external and internal, which is not correct // and would make the collapsing (thus the details resolution) dependent on // internal flow which is unrelated. - coord_t min_spacing = perimeter_spacing * (1 - INSET_OVERLAP_TOLERANCE); - coord_t ext_min_spacing = ext_perimeter_spacing * (1 - INSET_OVERLAP_TOLERANCE); - + coord_t min_spacing = coord_t(perimeter_spacing * (1 - INSET_OVERLAP_TOLERANCE)); + coord_t ext_min_spacing = coord_t(ext_perimeter_spacing * (1 - INSET_OVERLAP_TOLERANCE)); + bool has_gap_fill = this->config->gap_fill_speed.value > 0; + // prepare grown lower layer slices for overhang detection if (this->lower_slices != NULL && this->config->overhangs) { // We consider overhang any part where the entire nozzle diameter is not supported by the // lower layer, so we take lower slices and offset them by half the nozzle diameter used // in the current layer double nozzle_diameter = this->print_config->nozzle_diameter.get_at(this->config->perimeter_extruder-1); - this->_lower_slices_p = offset(*this->lower_slices, float(scale_(+nozzle_diameter/2))); + m_lower_slices_polygons = offset(*this->lower_slices, float(scale_(+nozzle_diameter/2))); } // we need to process each island separately because we might have different @@ -69,20 +289,20 @@ void PerimeterGenerator::process() offsets = this->config->thin_walls ? offset2_ex( last, - -(ext_perimeter_width / 2 + ext_min_spacing / 2 - 1), - +(ext_min_spacing / 2 - 1)) : - offset_ex(last, - ext_perimeter_width / 2); + - float(ext_perimeter_width / 2. + ext_min_spacing / 2. - 1), + + float(ext_min_spacing / 2. - 1)) : + offset_ex(last, - float(ext_perimeter_width / 2.)); // look for thin walls if (this->config->thin_walls) { // the following offset2 ensures almost nothing in @thin_walls is narrower than $min_width // (actually, something larger than that still may exist due to mitering or other causes) - coord_t min_width = scale_(this->ext_perimeter_flow.nozzle_diameter / 3); + coord_t min_width = coord_t(scale_(this->ext_perimeter_flow.nozzle_diameter / 3)); ExPolygons expp = offset2_ex( // medial axis requires non-overlapping geometry diff_ex(to_polygons(last), - offset(offsets, ext_perimeter_width / 2), + offset(offsets, float(ext_perimeter_width / 2.)), true), - - min_width / 2, min_width / 2); + - float(min_width / 2.), float(min_width / 2.)); // the maximum thickness of our thin wall area is equal to the minimum thickness of a single loop for (ExPolygon &ex : expp) ex.medial_axis(ext_perimeter_width + ext_perimeter_spacing2, min_width, &thin_walls); @@ -99,19 +319,19 @@ void PerimeterGenerator::process() // Also the offset2(perimeter, -x, x) may sometimes lead to a perimeter, which is larger than // the original. offset2_ex(last, - - (distance + min_spacing / 2 - 1), - min_spacing / 2 - 1) : + - float(distance + min_spacing / 2. - 1.), + float(min_spacing / 2. - 1.)) : // If "detect thin walls" is not enabled, this paths will be entered, which // leads to overflows, as in prusa3d/Slic3r GH #32 - offset_ex(last, - distance); + offset_ex(last, - float(distance)); // look for gaps - if (this->config->gap_fill_speed.value > 0 && this->config->fill_density.value > 0) + if (has_gap_fill) // not using safety offset here would "detect" very narrow gaps // (but still long enough to escape the area threshold) that gap fill // won't be able to fill but we'd still remove from infill area append(gaps, diff_ex( - offset(last, -0.5 * distance), - offset(offsets, 0.5 * distance + 10))); // safety offset + offset(last, - float(0.5 * distance)), + offset(offsets, float(0.5 * distance + 10)))); // safety offset } if (offsets.empty()) { // Store the number of loops actually generated. @@ -124,6 +344,11 @@ void PerimeterGenerator::process() break; } for (const ExPolygon &expolygon : offsets) { + // Outer contour may overlap with an inner contour, + // inner contour may overlap with another inner contour, + // outer contour may overlap with itself. + //FIXME evaluate the overlaps, annotate each point with an overlap depth, + // compensate for the depth of intersection. contours[i].emplace_back(PerimeterGeneratorLoop(expolygon.contour, i, true)); if (! expolygon.holes.empty()) { holes[i].reserve(holes[i].size() + expolygon.holes.size()); @@ -132,6 +357,11 @@ void PerimeterGenerator::process() } } last = std::move(offsets); + if (i == loop_number && (! has_gap_fill || this->config->fill_density.value == 0)) { + // The last run of this loop is executed to collect gaps for gap fill. + // As the gap fill is either disabled or not + break; + } } // nest loops: holes first @@ -189,7 +419,7 @@ void PerimeterGenerator::process() } } // at this point, all loops should be in contours[0] - ExtrusionEntityCollection entities = this->_traverse_loops(contours.front(), thin_walls); + ExtrusionEntityCollection entities = traverse_loops(*this, contours.front(), thin_walls); // if brim will be printed, reverse the order of perimeters so that // we continue inwards after having finished the brim // TODO: add test for perimeter order @@ -208,15 +438,14 @@ void PerimeterGenerator::process() double max = 2. * perimeter_spacing; ExPolygons gaps_ex = diff_ex( //FIXME offset2 would be enough and cheaper. - offset2_ex(gaps, -min/2, +min/2), - offset2_ex(gaps, -max/2, +max/2), + offset2_ex(gaps, - float(min / 2.), float(min / 2.)), + offset2_ex(gaps, - float(max / 2.), float(max / 2.)), true); ThickPolylines polylines; for (const ExPolygon &ex : gaps_ex) ex.medial_axis(max, min, &polylines); if (! polylines.empty()) { - ExtrusionEntityCollection gap_fill = this->_variable_width(polylines, - erGapFill, this->solid_infill_flow); + ExtrusionEntityCollection gap_fill = variable_width(polylines, erGapFill, this->solid_infill_flow); this->gap_fill->append(gap_fill.entities); /* Make sure we don't infill narrow parts that are already gap-filled (we only consider this surface's gaps to reduce the diff() complexity). @@ -243,229 +472,23 @@ void PerimeterGenerator::process() perimeter_spacing / 2; // only apply infill overlap if we actually have one perimeter if (inset > 0) - inset -= scale_(this->config->get_abs_value("infill_overlap", unscale<double>(inset + solid_infill_spacing / 2))); + inset -= coord_t(scale_(this->config->get_abs_value("infill_overlap", unscale<double>(inset + solid_infill_spacing / 2)))); // simplify infill contours according to resolution Polygons pp; for (ExPolygon &ex : last) ex.simplify_p(SCALED_RESOLUTION, &pp); // collapse too narrow infill areas - coord_t min_perimeter_infill_spacing = solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE); + coord_t min_perimeter_infill_spacing = coord_t(solid_infill_spacing * (1. - INSET_OVERLAP_TOLERANCE)); // append infill areas to fill_surfaces this->fill_surfaces->append( offset2_ex( union_ex(pp), - - inset - min_perimeter_infill_spacing / 2, - min_perimeter_infill_spacing / 2), + float(- inset - min_perimeter_infill_spacing / 2.), + float(min_perimeter_infill_spacing / 2.)), stInternal); } // for each island } -ExtrusionEntityCollection PerimeterGenerator::_traverse_loops( - const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls) const -{ - // loops is an arrayref of ::Loop objects - // turn each one into an ExtrusionLoop object - ExtrusionEntityCollection coll; - for (PerimeterGeneratorLoops::const_iterator loop = loops.begin(); - loop != loops.end(); ++loop) { - bool is_external = loop->is_external(); - - ExtrusionRole role; - ExtrusionLoopRole loop_role; - role = is_external ? erExternalPerimeter : erPerimeter; - if (loop->is_internal_contour()) { - // Note that we set loop role to ContourInternalPerimeter - // also when loop is both internal and external (i.e. - // there's only one contour loop). - loop_role = elrContourInternalPerimeter; - } else { - loop_role = elrDefault; - } - - // detect overhanging/bridging perimeters - ExtrusionPaths paths; - if (this->config->overhangs && this->layer_id > 0 - && !(this->object_config->support_material && this->object_config->support_material_contact_distance.value == 0)) { - // get non-overhang paths by intersecting this loop with the grown lower slices - extrusion_paths_append( - paths, - intersection_pl(loop->polygon, this->_lower_slices_p), - role, - is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm, - is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width, - this->layer_height); - - // get overhang paths by checking what parts of this loop fall - //Â outside the grown lower slices (thus where the distance between - // the loop centerline and original lower slices is >= half nozzle diameter - extrusion_paths_append( - paths, - diff_pl(loop->polygon, this->_lower_slices_p), - erOverhangPerimeter, - this->_mm3_per_mm_overhang, - this->overhang_flow.width, - this->overhang_flow.height); - - // reapply the nearest point search for starting point - // We allow polyline reversal because Clipper may have randomly - // reversed polylines during clipping. - paths = (ExtrusionPaths)ExtrusionEntityCollection(paths).chained_path(); - } else { - ExtrusionPath path(role); - path.polyline = loop->polygon.split_at_first_point(); - path.mm3_per_mm = is_external ? this->_ext_mm3_per_mm : this->_mm3_per_mm; - path.width = is_external ? this->ext_perimeter_flow.width : this->perimeter_flow.width; - path.height = this->layer_height; - paths.push_back(path); - } - - coll.append(ExtrusionLoop(paths, loop_role)); - } - - // append thin walls to the nearest-neighbor search (only for first iteration) - if (!thin_walls.empty()) { - ExtrusionEntityCollection tw = this->_variable_width - (thin_walls, erExternalPerimeter, this->ext_perimeter_flow); - - coll.append(tw.entities); - thin_walls.clear(); - } - - // sort entities into a new collection using a nearest-neighbor search, - // preserving the original indices which are useful for detecting thin walls - ExtrusionEntityCollection sorted_coll; - coll.chained_path(&sorted_coll, false, erMixed, &sorted_coll.orig_indices); - - // traverse children and build the final collection - ExtrusionEntityCollection entities; - for (std::vector<size_t>::const_iterator idx = sorted_coll.orig_indices.begin(); - idx != sorted_coll.orig_indices.end(); - ++idx) { - - if (*idx >= loops.size()) { - // this is a thin wall - // let's get it from the sorted collection as it might have been reversed - size_t i = idx - sorted_coll.orig_indices.begin(); - entities.append(*sorted_coll.entities[i]); - } else { - const PerimeterGeneratorLoop &loop = loops[*idx]; - ExtrusionLoop eloop = *dynamic_cast<ExtrusionLoop*>(coll.entities[*idx]); - - ExtrusionEntityCollection children = this->_traverse_loops(loop.children, thin_walls); - if (loop.is_contour) { - eloop.make_counter_clockwise(); - entities.append(children.entities); - entities.append(eloop); - } else { - eloop.make_clockwise(); - entities.append(eloop); - entities.append(children.entities); - } - } - } - return entities; -} - -static inline ExtrusionPaths thick_polyline_to_extrusion_paths(const ThickPolyline &thick_polyline, ExtrusionRole role, Flow &flow, const float tolerance) -{ - ExtrusionPaths paths; - ExtrusionPath path(role); - ThickLines lines = thick_polyline.thicklines(); - - for (int i = 0; i < (int)lines.size(); ++i) { - const ThickLine& line = lines[i]; - - const coordf_t line_len = line.length(); - if (line_len < SCALED_EPSILON) continue; - - double thickness_delta = fabs(line.a_width - line.b_width); - if (thickness_delta > tolerance) { - const unsigned short segments = ceil(thickness_delta / tolerance); - const coordf_t seg_len = line_len / segments; - Points pp; - std::vector<coordf_t> width; - { - pp.push_back(line.a); - width.push_back(line.a_width); - for (size_t j = 1; j < segments; ++j) { - pp.push_back((line.a.cast<double>() + (line.b - line.a).cast<double>().normalized() * (j * seg_len)).cast<coord_t>()); - - coordf_t w = line.a_width + (j*seg_len) * (line.b_width-line.a_width) / line_len; - width.push_back(w); - width.push_back(w); - } - pp.push_back(line.b); - width.push_back(line.b_width); - - assert(pp.size() == segments + 1u); - assert(width.size() == segments*2); - } - - // delete this line and insert new ones - lines.erase(lines.begin() + i); - for (size_t j = 0; j < segments; ++j) { - ThickLine new_line(pp[j], pp[j+1]); - new_line.a_width = width[2*j]; - new_line.b_width = width[2*j+1]; - lines.insert(lines.begin() + i + j, new_line); - } - - -- i; - continue; - } - - const double w = fmax(line.a_width, line.b_width); - if (path.polyline.points.empty()) { - path.polyline.append(line.a); - path.polyline.append(line.b); - // Convert from spacing to extrusion width based on the extrusion model - // of a square extrusion ended with semi circles. - flow.width = unscale<float>(w) + flow.height * (1. - 0.25 * PI); - #ifdef SLIC3R_DEBUG - printf(" filling %f gap\n", flow.width); - #endif - path.mm3_per_mm = flow.mm3_per_mm(); - path.width = flow.width; - path.height = flow.height; - } else { - thickness_delta = fabs(scale_(flow.width) - w); - if (thickness_delta <= tolerance) { - // the width difference between this line and the current flow width is - // within the accepted tolerance - path.polyline.append(line.b); - } else { - // we need to initialize a new line - paths.emplace_back(std::move(path)); - path = ExtrusionPath(role); - -- i; - } - } - } - if (path.polyline.is_valid()) - paths.emplace_back(std::move(path)); - return paths; -} - -ExtrusionEntityCollection PerimeterGenerator::_variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const -{ - // This value determines granularity of adaptive width, as G-code does not allow - // variable extrusion within a single move; this value shall only affect the amount - // of segments, and any pruning shall be performed before we apply this tolerance. - ExtrusionEntityCollection coll; - const double tolerance = scale_(0.05); - for (const ThickPolyline &p : polylines) { - ExtrusionPaths paths = thick_polyline_to_extrusion_paths(p, role, flow, tolerance); - // Append paths to collection. - if (! paths.empty()) { - if (paths.front().first_point() == paths.back().last_point()) - coll.append(ExtrusionLoop(std::move(paths))); - else - coll.append(std::move(paths)); - } - } - return coll; -} - bool PerimeterGeneratorLoop::is_internal_contour() const { // An internal contour is a contour containing no other contours diff --git a/src/libslic3r/PerimeterGenerator.hpp b/src/libslic3r/PerimeterGenerator.hpp index 44af8c8be..8cd71e697 100644 --- a/src/libslic3r/PerimeterGenerator.hpp +++ b/src/libslic3r/PerimeterGenerator.hpp @@ -11,29 +11,6 @@ namespace Slic3r { -// Hierarchy of perimeters. -class PerimeterGeneratorLoop { -public: - // Polygon of this contour. - Polygon polygon; - // Is it a contour or a hole? - // Contours are CCW oriented, holes are CW oriented. - bool is_contour; - // Depth in the hierarchy. External perimeter has depth = 0. An external perimeter could be both a contour and a hole. - unsigned short depth; - // Children contour, may be both CCW and CW oriented (outer contours or holes). - std::vector<PerimeterGeneratorLoop> children; - - PerimeterGeneratorLoop(Polygon polygon, unsigned short depth, bool is_contour) : - polygon(polygon), is_contour(is_contour), depth(depth) {} - // External perimeter. It may be CCW or CW oriented (outer contour or hole contour). - bool is_external() const { return this->depth == 0; } - // An island, which may have holes, but it does not have another internal island. - bool is_internal_contour() const; -}; - -typedef std::vector<PerimeterGeneratorLoop> PerimeterGeneratorLoops; - class PerimeterGenerator { public: // Inputs: @@ -73,18 +50,21 @@ public: overhang_flow(flow), solid_infill_flow(flow), config(config), object_config(object_config), print_config(print_config), loops(loops), gap_fill(gap_fill), fill_surfaces(fill_surfaces), - _ext_mm3_per_mm(-1), _mm3_per_mm(-1), _mm3_per_mm_overhang(-1) - {}; - void process(); + m_ext_mm3_per_mm(-1), m_mm3_per_mm(-1), m_mm3_per_mm_overhang(-1) + {} + + void process(); + + double ext_mm3_per_mm() const { return m_ext_mm3_per_mm; } + double mm3_per_mm() const { return m_mm3_per_mm; } + double mm3_per_mm_overhang() const { return m_mm3_per_mm_overhang; } + Polygons lower_slices_polygons() const { return m_lower_slices_polygons; } private: - double _ext_mm3_per_mm; - double _mm3_per_mm; - double _mm3_per_mm_overhang; - Polygons _lower_slices_p; - - ExtrusionEntityCollection _traverse_loops(const PerimeterGeneratorLoops &loops, ThickPolylines &thin_walls) const; - ExtrusionEntityCollection _variable_width(const ThickPolylines &polylines, ExtrusionRole role, Flow flow) const; + double m_ext_mm3_per_mm; + double m_mm3_per_mm; + double m_mm3_per_mm_overhang; + Polygons m_lower_slices_polygons; }; } diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp index 530d84907..0cfd97415 100644 --- a/src/libslic3r/PlaceholderParser.cpp +++ b/src/libslic3r/PlaceholderParser.cpp @@ -108,7 +108,6 @@ static inline bool opts_equal(const DynamicConfig &config_old, const DynamicConf std::vector<std::string> PlaceholderParser::config_diff(const DynamicPrintConfig &rhs) { - const ConfigDef *def = rhs.def(); std::vector<std::string> diff_keys; for (const t_config_option_key &opt_key : rhs.keys()) if (! opts_equal(m_config, rhs, opt_key)) @@ -124,7 +123,6 @@ std::vector<std::string> PlaceholderParser::config_diff(const DynamicPrintConfig // a current extruder ID is used. bool PlaceholderParser::apply_config(const DynamicPrintConfig &rhs) { - const ConfigDef *def = rhs.def(); bool modified = false; for (const t_config_option_key &opt_key : rhs.keys()) { if (! opts_equal(m_config, rhs, opt_key)) { diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 796215781..9d4df56d0 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -1228,24 +1228,35 @@ std::string Print::validate() const if (has_custom_layering) { const std::vector<coordf_t> &layer_height_profile_tallest = layer_height_profiles[tallest_object_idx]; for (size_t idx_object = 0; idx_object < m_objects.size(); ++ idx_object) { + if (idx_object == tallest_object_idx) + continue; const std::vector<coordf_t> &layer_height_profile = layer_height_profiles[idx_object]; - bool failed = false; - if (layer_height_profile_tallest.size() >= layer_height_profile.size()) { - size_t i = 0; - while (i < layer_height_profile.size() && i < layer_height_profile_tallest.size()) { - if (std::abs(layer_height_profile_tallest[i] - layer_height_profile[i])) { - failed = true; - break; - } - ++ i; - if (i == layer_height_profile.size() - 2) // this element contains this objects max z - if (layer_height_profile_tallest[i] > layer_height_profile[i]) // the difference does not matter in this case - ++ i; + + // The comparison of the profiles is not just about element-wise equality, some layers may not be + // explicitely included. Always remember z and height of last reference layer that in the vector + // and compare to that. + size_t i = 0; // index into tested profile + size_t j = 0; // index into reference profile + coordf_t ref_z = -1.; + coordf_t next_ref_z = layer_height_profile_tallest[0]; + coordf_t ref_height = -1.; + while (i < layer_height_profile.size()) { + coordf_t this_z = layer_height_profile[i]; + coordf_t this_height = layer_height_profile[i+1]; + if (next_ref_z < this_z + EPSILON) { + ref_z = next_ref_z; + do { // one layer can be in the vector several times + ref_height = layer_height_profile_tallest[j+1]; + if (j+2 >= layer_height_profile_tallest.size()) + break; + j += 2; + next_ref_z = layer_height_profile_tallest[j]; + } while (ref_z == next_ref_z); } - } else - failed = true; - if (failed) - return L("The Wipe tower is only supported if all objects have the same layer height profile"); + if (std::abs(this_height - ref_height) > EPSILON) + return L("The Wipe tower is only supported if all objects have the same layer height profile"); + i += 2; + } } } } diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index b6d7b678d..89a5f3e74 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -39,6 +39,8 @@ class PrintRegion public: const Print* print() const { return m_print; } const PrintRegionConfig& config() const { return m_config; } + // 1-based extruder identifier for this region and role. + unsigned int extruder(FlowRole role) const; Flow flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const; // Average diameter of nozzles participating on extruding this region. coordf_t nozzle_dmr_avg(const PrintConfig &print_config) const; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 8b73a2e82..1ce00f269 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -2049,7 +2049,7 @@ void PrintConfigDef::init_fff_params() { int threads = (unsigned int)boost::thread::hardware_concurrency(); def->set_default_value(new ConfigOptionInt(threads > 0 ? threads : 2)); - def->cli == ConfigOptionDef::nocli; + def->cli = ConfigOptionDef::nocli; } def = this->add("toolchange_gcode", coString); @@ -2894,7 +2894,7 @@ void DynamicPrintConfig::normalize() { this->opt<ConfigOptionInt>("perimeters", true)->value = 1; this->opt<ConfigOptionInt>("top_solid_layers", true)->value = 0; - this->opt<ConfigOptionPercent>("fill_density", true)->value = 0; + this->opt<ConfigOptionPercent>("fill_density", true)->value = 0; } } } diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 015b2bde7..6a19edf84 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -24,7 +24,7 @@ namespace Slic3r { -enum GCodeFlavor { +enum GCodeFlavor : unsigned char { gcfRepRap, gcfRepetier, gcfTeacup, gcfMakerWare, gcfMarlin, gcfSailfish, gcfMach3, gcfMachinekit, gcfSmoothie, gcfNoExtrusion, }; @@ -35,7 +35,7 @@ enum PrintHostType { enum InfillPattern { ipRectilinear, ipGrid, ipTriangles, ipStars, ipCubic, ipLine, ipConcentric, ipHoneycomb, ip3DHoneycomb, - ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, + ipGyroid, ipHilbertCurve, ipArchimedeanChords, ipOctagramSpiral, ipCount, }; enum SupportMaterialPattern { diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index e19bceeb7..2834d9105 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -258,8 +258,8 @@ void PrintObject::prepare_infill() m_print->throw_if_canceled(); // Decide what surfaces are to be filled. - // Here the S_TYPE_TOP / S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is turned to just S_TYPE_INTERNAL if zero top / bottom infill layers are configured. - // Also tiny S_TYPE_INTERNAL surfaces are turned to S_TYPE_INTERNAL_SOLID. + // Here the stTop / stBottomBridge / stBottom infill is turned to just stInternal if zero top / bottom infill layers are configured. + // Also tiny stInternal surfaces are turned to stInternalSolid. BOOST_LOG_TRIVIAL(info) << "Preparing fill surfaces..." << log_memory_info(); for (auto *layer : m_layers) for (auto *region : layer->m_regions) { @@ -271,8 +271,8 @@ void PrintObject::prepare_infill() // and rearrange top/bottom/internal surfaces // It produces enlarged overlapping bridging areas. // - // 1) S_TYPE_BOTTOMBRIDGE / S_TYPE_BOTTOM infill is grown by 3mm and clipped by the total infill area. Bridges are detected. The areas may overlap. - // 2) S_TYPE_TOP is grown by 3mm and clipped by the grown bottom areas. The areas may overlap. + // 1) stBottomBridge / stBottom infill is grown by 3mm and clipped by the total infill area. Bridges are detected. The areas may overlap. + // 2) stTop is grown by 3mm and clipped by the grown bottom areas. The areas may overlap. // 3) Clip the internal surfaces by the grown top/bottom surfaces. // 4) Merge surfaces with the same style. This will mostly get rid of the overlaps. //FIXME This does not likely merge surfaces, which are supported by a material with different colors, but same properties. @@ -468,9 +468,17 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_ || opt_key == "support_material_contact_distance" || opt_key == "xy_size_compensation") { steps.emplace_back(posSlice); + } else if (opt_key == "support_material") { + steps.emplace_back(posSupportMaterial); + if (m_config.support_material_contact_distance == 0.) { + // Enabling / disabling supports while soluble support interface is enabled. + // This changes the bridging logic (bridging enabled without supports, disabled with supports). + // Reset everything. + // See GH #1482 for details. + steps.emplace_back(posSlice); + } } else if ( - opt_key == "support_material" - || opt_key == "support_material_auto" + opt_key == "support_material_auto" || opt_key == "support_material_angle" || opt_key == "support_material_buildplate_only" || opt_key == "support_material_enforce_layers" @@ -524,8 +532,13 @@ bool PrintObject::invalidate_state_by_config_options(const std::vector<t_config_ steps.emplace_back(posPerimeters); steps.emplace_back(posSupportMaterial); } else if (opt_key == "bridge_flow_ratio") { - steps.emplace_back(posPerimeters); - steps.emplace_back(posInfill); + if (m_config.support_material_contact_distance > 0.) { + // Only invalidate due to bridging if bridging is enabled. + // If later "support_material_contact_distance" is modified, the complete PrintObject is invalidated anyway. + steps.emplace_back(posPerimeters); + steps.emplace_back(posInfill); + steps.emplace_back(posSupportMaterial); + } } else if ( opt_key == "seam_position" || opt_key == "seam_preferred_direction" @@ -810,15 +823,72 @@ void PrintObject::process_external_surfaces() { BOOST_LOG_TRIVIAL(info) << "Processing external surfaces..." << log_memory_info(); + // Cached surfaces covered by some extrusion, defining regions, over which the from the surfaces one layer higher are allowed to expand. + std::vector<Polygons> surfaces_covered; + // Is there any printing region, that has zero infill? If so, then we don't want the expansion to be performed over the complete voids, but only + // over voids, which are supported by the layer below. + bool has_voids = false; + for (size_t region_id = 0; region_id < this->region_volumes.size(); ++ region_id) + if (! this->region_volumes.empty() && this->print()->regions()[region_id]->config().fill_density == 0) { + has_voids = true; + break; + } + if (has_voids && m_layers.size() > 1) { + // All but stInternal fill surfaces will get expanded and possibly trimmed. + std::vector<unsigned char> layer_expansions_and_voids(m_layers.size(), false); + for (size_t layer_idx = 0; layer_idx < m_layers.size(); ++ layer_idx) { + const Layer *layer = m_layers[layer_idx]; + bool expansions = false; + bool voids = false; + for (const LayerRegion *layerm : layer->regions()) { + for (const Surface &surface : layerm->fill_surfaces.surfaces) { + if (surface.surface_type == stInternal) + voids = true; + else + expansions = true; + if (voids && expansions) { + layer_expansions_and_voids[layer_idx] = true; + goto end; + } + } + } + end:; + } + BOOST_LOG_TRIVIAL(debug) << "Collecting surfaces covered with extrusions in parallel - start"; + surfaces_covered.resize(m_layers.size() - 1, Polygons()); + auto unsupported_width = - float(scale_(0.3 * EXTERNAL_INFILL_MARGIN)); + tbb::parallel_for( + tbb::blocked_range<size_t>(0, m_layers.size() - 1), + [this, &surfaces_covered, &layer_expansions_and_voids, unsupported_width](const tbb::blocked_range<size_t>& range) { + for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) + if (layer_expansions_and_voids[layer_idx + 1]) { + m_print->throw_if_canceled(); + Polygons voids; + for (const LayerRegion *layerm : m_layers[layer_idx]->regions()) { + if (layerm->region()->config().fill_density.value == 0.) + for (const Surface &surface : layerm->fill_surfaces.surfaces) + // Shrink the holes, let the layer above expand slightly inside the unsupported areas. + polygons_append(voids, offset(surface.expolygon, unsupported_width)); + } + surfaces_covered[layer_idx] = diff(to_polygons(this->m_layers[layer_idx]->slices.expolygons), voids); + } + } + ); + m_print->throw_if_canceled(); + BOOST_LOG_TRIVIAL(debug) << "Collecting surfaces covered with extrusions in parallel - end"; + } + for (size_t region_id = 0; region_id < this->region_volumes.size(); ++region_id) { BOOST_LOG_TRIVIAL(debug) << "Processing external surfaces for region " << region_id << " in parallel - start"; tbb::parallel_for( tbb::blocked_range<size_t>(0, m_layers.size()), - [this, region_id](const tbb::blocked_range<size_t>& range) { + [this, &surfaces_covered, region_id](const tbb::blocked_range<size_t>& range) { for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++ layer_idx) { m_print->throw_if_canceled(); // BOOST_LOG_TRIVIAL(trace) << "Processing external surface, layer" << m_layers[layer_idx]->print_z; - m_layers[layer_idx]->get_region((int)region_id)->process_external_surfaces((layer_idx == 0) ? NULL : m_layers[layer_idx - 1]); + m_layers[layer_idx]->get_region((int)region_id)->process_external_surfaces( + (layer_idx == 0) ? nullptr : m_layers[layer_idx - 1], + (layer_idx == 0 || surfaces_covered.empty() || surfaces_covered[layer_idx - 1].empty()) ? nullptr : &surfaces_covered[layer_idx - 1]); } } ); @@ -1315,7 +1385,7 @@ void PrintObject::bridge_over_infill() )}; push @new_surfaces, map Slic3r::Surface->new( expolygon => $_, - surface_type => S_TYPE_INTERNALVOID, + surface_type => stInternalVoid, ), @{intersection_ex( [ map $_->p, @$group ], [ map @$_, @$to_bridge ], @@ -1443,11 +1513,8 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c layer_height_profile.clear(); if (layer_height_profile.empty()) { - if (0) -// if (this->layer_height_profile.empty()) - layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes); - else - layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges); // #ys_FIXME_experiment + //layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes); + layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges); updated = true; } return updated; @@ -1870,12 +1937,13 @@ std::vector<ExPolygons> PrintObject::slice_modifiers(size_t region_id, const std merge.assign(out.size(), false); } else { for (size_t i = 0; i < out.size(); ++ i) - if (! this_slices[i].empty()) + if (! this_slices[i].empty()) { if (! out[i].empty()) { append(out[i], this_slices[i]); merge[i] = true; } else out[i] = std::move(this_slices[i]); + } } i = j; } else @@ -2238,8 +2306,7 @@ void PrintObject::clip_fill_surfaces() // Detect things that we need to support. // Cummulative slices. Polygons slices; - for (const ExPolygon &expoly : layer->slices.expolygons) - polygons_append(slices, to_polygons(expoly)); + polygons_append(slices, layer->slices.expolygons); // Cummulative fill surfaces. Polygons fill_surfaces; // Solid surfaces to be supported. @@ -2348,7 +2415,7 @@ void PrintObject::discover_horizontal_shells() polygons_append(solid, to_polygons(surface.expolygon)); if (solid.empty()) continue; -// Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == S_TYPE_TOP) ? 'top' : 'bottom'; +// Slic3r::debugf "Layer %d has %s surfaces\n", $i, ($type == stTop) ? 'top' : 'bottom'; size_t solid_layers = (type == stTop) ? region_config.top_solid_layers.value : region_config.bottom_solid_layers.value; for (int n = (type == stTop) ? i-1 : i+1; std::abs(n - (int)i) < solid_layers; (type == stTop) ? -- n : ++ n) { @@ -2367,7 +2434,7 @@ void PrintObject::discover_horizontal_shells() // shells to be generated in the base but not in the walls (where there are many // narrow bottom surfaces): reassigning $solid will consider the 'shadow' of the // upper perimeter as an obstacle and shell will not be propagated to more upper layers - //FIXME How does it work for S_TYPE_INTERNALBRIDGE? This is set for sparse infill. Likely this does not work. + //FIXME How does it work for stInternalBRIDGE? This is set for sparse infill. Likely this does not work. Polygons new_internal_solid; { Polygons internal; @@ -2560,7 +2627,7 @@ void PrintObject::combine_infill() continue; // Slic3r::debugf " combining %d %s regions from layers %d-%d\n", // scalar(@$intersection), -// ($type == S_TYPE_INTERNAL ? 'internal' : 'internal-solid'), +// ($type == stInternal ? 'internal' : 'internal-solid'), // $layer_idx-($every-1), $layer_idx; // intersection now contains the regions that can be combined across the full amount of layers, // so let's remove those areas from all layers. diff --git a/src/libslic3r/PrintRegion.cpp b/src/libslic3r/PrintRegion.cpp index fc2bdfa7d..b3ac6a4a5 100644 --- a/src/libslic3r/PrintRegion.cpp +++ b/src/libslic3r/PrintRegion.cpp @@ -2,6 +2,21 @@ namespace Slic3r { +// 1-based extruder identifier for this region and role. +unsigned int PrintRegion::extruder(FlowRole role) const +{ + size_t extruder = 0; + if (role == frPerimeter || role == frExternalPerimeter) + extruder = m_config.perimeter_extruder; + else if (role == frInfill) + extruder = m_config.infill_extruder; + else if (role == frSolidInfill || role == frTopSolidInfill) + extruder = m_config.solid_infill_extruder; + else + throw std::invalid_argument("Unknown role"); + return extruder; +} + Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool first_layer, double width, const PrintObject &object) const { ConfigOptionFloatOrPercent config_width; @@ -28,24 +43,13 @@ Flow PrintRegion::flow(FlowRole role, double layer_height, bool bridge, bool fir throw std::invalid_argument("Unknown role"); } } - if (config_width.value == 0) { + + if (config_width.value == 0) config_width = object.config().extrusion_width; - } - - // get the configured nozzle_diameter for the extruder associated - // to the flow role requested - size_t extruder = 0; // 1-based - if (role == frPerimeter || role == frExternalPerimeter) { - extruder = m_config.perimeter_extruder; - } else if (role == frInfill) { - extruder = m_config.infill_extruder; - } else if (role == frSolidInfill || role == frTopSolidInfill) { - extruder = m_config.solid_infill_extruder; - } else { - throw std::invalid_argument("Unknown role"); - } - double nozzle_diameter = m_print->config().nozzle_diameter.get_at(extruder-1); + // Get the configured nozzle_diameter for the extruder associated to the flow role requested. + // Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right. + double nozzle_diameter = m_print->config().nozzle_diameter.get_at(this->extruder(role) - 1); return Flow::new_from_config_width(role, config_width, (float)nozzle_diameter, (float)layer_height, bridge ? (float)m_config.bridge_flow_ratio : 0.0f); } @@ -79,12 +83,14 @@ void PrintRegion::collect_object_printing_extruders(const PrintConfig &print_con void PrintRegion::collect_object_printing_extruders(std::vector<unsigned int> &object_extruders) const { - auto num_extruders = (int)print()->config().nozzle_diameter.size(); // PrintRegion, if used by some PrintObject, shall have all the extruders set to an existing printer extruder. // If not, then there must be something wrong with the Print::apply() function. +#ifndef NDEBUG + auto num_extruders = (int)print()->config().nozzle_diameter.size(); assert(this->config().perimeter_extruder <= num_extruders); assert(this->config().infill_extruder <= num_extruders); assert(this->config().solid_infill_extruder <= num_extruders); +#endif collect_object_printing_extruders(print()->config(), this->config(), object_extruders); } diff --git a/src/libslic3r/SLA/SLAAutoSupports.hpp b/src/libslic3r/SLA/SLAAutoSupports.hpp index 3b696212e..38f21e6cf 100644 --- a/src/libslic3r/SLA/SLAAutoSupports.hpp +++ b/src/libslic3r/SLA/SLAAutoSupports.hpp @@ -185,8 +185,6 @@ private: SLAAutoSupports::Config m_config; - float m_supports_force_total = 0.f; - void process(const std::vector<ExPolygons>& slices, const std::vector<float>& heights); void uniformly_cover(const ExPolygons& islands, Structure& structure, PointGrid3D &grid3d, bool is_new_island = false, bool just_one = false); void project_onto_mesh(std::vector<sla::SupportPoint>& points) const; diff --git a/src/libslic3r/Surface.cpp b/src/libslic3r/Surface.cpp index acc19e631..58ac7294c 100644 --- a/src/libslic3r/Surface.cpp +++ b/src/libslic3r/Surface.cpp @@ -4,64 +4,6 @@ namespace Slic3r { -Surface::operator Polygons() const -{ - return this->expolygon; -} - -double -Surface::area() const -{ - return this->expolygon.area(); -} - -bool -Surface::is_solid() const -{ - return this->surface_type == stTop - || this->surface_type == stBottom - || this->surface_type == stBottomBridge - || this->surface_type == stInternalSolid - || this->surface_type == stInternalBridge; -} - -bool -Surface::is_external() const -{ - return this->surface_type == stTop - || this->surface_type == stBottom - || this->surface_type == stBottomBridge; -} - -bool -Surface::is_internal() const -{ - return this->surface_type == stInternal - || this->surface_type == stInternalBridge - || this->surface_type == stInternalSolid - || this->surface_type == stInternalVoid; -} - -bool -Surface::is_top() const -{ - return this->surface_type == stTop; -} - -bool -Surface::is_bottom() const -{ - return this->surface_type == stBottom - || this->surface_type == stBottomBridge; -} - -bool -Surface::is_bridge() const -{ - return this->surface_type == stBottomBridge - || this->surface_type == stInternalBridge; -} - BoundingBox get_extents(const Surface &surface) { return get_extents(surface.expolygon.contour); diff --git a/src/libslic3r/Surface.hpp b/src/libslic3r/Surface.hpp index e0f7e1ea3..fbebe5610 100644 --- a/src/libslic3r/Surface.hpp +++ b/src/libslic3r/Surface.hpp @@ -24,9 +24,8 @@ enum SurfaceType { stInternalVoid, // Inner/outer perimeters. stPerimeter, - // Last surface type, if the SurfaceType is used as an index into a vector. - stLast, - stCount = stLast + 1 + // Number of SurfaceType enums. + stCount, }; class Surface @@ -91,16 +90,18 @@ public: return *this; } - operator Polygons() const; - double area() const; - bool empty() const { return expolygon.empty(); } - void clear() { expolygon.clear(); } - bool is_solid() const; - bool is_external() const; - bool is_internal() const; - bool is_top() const; - bool is_bottom() const; - bool is_bridge() const; + operator Polygons() const { return this->expolygon; } + double area() const { return this->expolygon.area(); } + bool empty() const { return expolygon.empty(); } + void clear() { expolygon.clear(); } + + // The following methods do not test for stPerimeter. + bool is_top() const { return this->surface_type == stTop; } + bool is_bottom() const { return this->surface_type == stBottom || this->surface_type == stBottomBridge; } + bool is_bridge() const { return this->surface_type == stBottomBridge || this->surface_type == stInternalBridge; } + bool is_external() const { return this->is_top() || this->is_bottom(); } + bool is_internal() const { return ! this->is_external(); } + bool is_solid() const { return this->is_external() || this->surface_type == stInternalSolid || this->surface_type == stInternalBridge; } }; typedef std::vector<Surface> Surfaces; diff --git a/src/libslic3r/SurfaceCollection.hpp b/src/libslic3r/SurfaceCollection.hpp index 9544748e9..b60105eb3 100644 --- a/src/libslic3r/SurfaceCollection.hpp +++ b/src/libslic3r/SurfaceCollection.hpp @@ -37,6 +37,7 @@ public: void clear() { surfaces.clear(); } bool empty() const { return surfaces.empty(); } + size_t size() const { return surfaces.size(); } bool has(SurfaceType type) const { for (const Surface &surface : this->surfaces) if (surface.surface_type == type) return true; diff --git a/src/slic3r/Config/Snapshot.cpp b/src/slic3r/Config/Snapshot.cpp index 8fd64bb2a..622b31a17 100644 --- a/src/slic3r/Config/Snapshot.cpp +++ b/src/slic3r/Config/Snapshot.cpp @@ -202,9 +202,9 @@ void Snapshot::export_selections(AppConfig &config) const config.clear_section("presets"); config.set("presets", "print", print); config.set("presets", "filament", filaments.front()); - for (int i = 1; i < filaments.size(); ++i) { + for (unsigned i = 1; i < filaments.size(); ++i) { char name[64]; - sprintf(name, "filament_%d", i); + sprintf(name, "filament_%u", i); config.set("presets", name, filaments[i]); } config.set("presets", "printer", printer); @@ -373,9 +373,9 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot: snapshot.print = app_config.get("presets", "print"); snapshot.filaments.emplace_back(app_config.get("presets", "filament")); snapshot.printer = app_config.get("presets", "printer"); - for (unsigned int i = 1; i < 1000; ++ i) { + for (unsigned i = 1; i < 1000; ++ i) { char name[64]; - sprintf(name, "filament_%d", i); + sprintf(name, "filament_%u", i); if (! app_config.has("presets", name)) break; snapshot.filaments.emplace_back(app_config.get("presets", name)); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 2916d0177..8094cdde1 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1142,7 +1142,7 @@ void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const Color color; ::memcpy((void*)color.data(), (const void*)volume->color, 4 * sizeof(float)); fprintf(fp, "\n# material volume %d\n", volumes_count); - fprintf(fp, "usemtl material_%lld\n", 1 + std::distance(colors.begin(), colors.find(color))); + fprintf(fp, "usemtl material_%lld\n", (long long)(1 + std::distance(colors.begin(), colors.find(color)))); int base_vertex_id = vertices_count + 1; int base_normal_id = normals_count + 1; @@ -1242,8 +1242,8 @@ static void thick_lines_to_indexed_vertex_array( // calculate new XY normals Vec2d xy_right_normal = unscale(line.normal()).normalized(); - int idx_a[4]; - int idx_b[4]; + int idx_a[4] = { 0, 0, 0, 0 }; // initialized to avoid warnings + int idx_b[4] = { 0, 0, 0, 0 }; // initialized to avoid warnings int idx_last = int(volume.vertices_and_normals_interleaved.size() / 6); bool bottom_z_different = bottom_z_prev != bottom_z; diff --git a/src/slic3r/GUI/BedShapeDialog.cpp b/src/slic3r/GUI/BedShapeDialog.cpp index a9be260bd..90fc0ff80 100644 --- a/src/slic3r/GUI/BedShapeDialog.cpp +++ b/src/slic3r/GUI/BedShapeDialog.cpp @@ -446,7 +446,7 @@ void BedShapePanel::update_shape() auto twopi = 2 * PI; auto edges = 72; std::vector<Vec2d> points; - for (size_t i = 1; i <= edges; ++i) { + for (int i = 1; i <= edges; ++i) { auto angle = i * twopi / edges; points.push_back(Vec2d(r*cos(angle), r*sin(angle))); } diff --git a/src/slic3r/GUI/BitmapCache.cpp b/src/slic3r/GUI/BitmapCache.cpp index 3e8924143..f7cefe72b 100644 --- a/src/slic3r/GUI/BitmapCache.cpp +++ b/src/slic3r/GUI/BitmapCache.cpp @@ -57,7 +57,7 @@ wxBitmap* BitmapCache::insert(const std::string &bitmap_key, size_t width, size_ m_map[bitmap_key] = bitmap; } else { bitmap = it->second; - if (bitmap->GetWidth() != width || bitmap->GetHeight() != height) + if (size_t(bitmap->GetWidth()) != width || size_t(bitmap->GetHeight()) != height) bitmap->Create(width, height); } #ifndef BROKEN_ALPHA @@ -194,7 +194,7 @@ wxBitmap* BitmapCache::insert_raw_rgba(const std::string &bitmap_key, unsigned w return this->insert(bitmap_key, wxImage_to_wxBitmap_with_alpha(std::move(image), scale)); } -wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned int width, unsigned int height, +wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned width, unsigned height, const bool grayscale/* = false*/) { std::string bitmap_key = bitmap_name + ( height !=0 ? @@ -211,10 +211,10 @@ wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned int wid image.GetWidth() == 0 || image.GetHeight() == 0) return nullptr; - if (height != 0 && image.GetHeight() != height) - width = int(0.5f + float(image.GetWidth()) * height / image.GetHeight()); - else if (width != 0 && image.GetWidth() != width) - height = int(0.5f + float(image.GetHeight()) * width / image.GetWidth()); + if (height != 0 && unsigned(image.GetHeight()) != height) + width = unsigned(0.5f + float(image.GetWidth()) * height / image.GetHeight()); + else if (width != 0 && unsigned(image.GetWidth()) != width) + height = unsigned(0.5f + float(image.GetHeight()) * width / image.GetWidth()); if (height != 0 && width != 0) image.Rescale(width, height, wxIMAGE_QUALITY_BILINEAR); diff --git a/src/slic3r/GUI/BitmapCache.hpp b/src/slic3r/GUI/BitmapCache.hpp index 10ce3299e..ce77057c8 100644 --- a/src/slic3r/GUI/BitmapCache.hpp +++ b/src/slic3r/GUI/BitmapCache.hpp @@ -32,9 +32,9 @@ public: wxBitmap* insert_raw_rgba(const std::string &bitmap_key, unsigned width, unsigned height, const unsigned char *raw_data, float scale = 1.0f, const bool grayscale = false); // Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height/width if nonzero. - wxBitmap* load_png(const std::string &bitmap_key, unsigned int width = 0, unsigned int height = 0, const bool grayscale = false); + wxBitmap* load_png(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false); // Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height/width. - wxBitmap* load_svg(const std::string &bitmap_key, unsigned int width = 0, unsigned int height = 0, float scale = 1.0f, const bool grayscale = false); + wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, float scale = 1.0f, const bool grayscale = false); static wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency); static wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3]) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE); } diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 2f37eb894..c9cdff162 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -243,7 +243,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config) "infill_speed", "bridge_speed" }) toggle_field(el, have_infill || have_solid_infill); - toggle_field("gap_fill_speed", have_perimeters && have_infill); + // Gap fill is newly allowed in between perimeter lines even for empty infill (see GH #1476). + toggle_field("gap_fill_speed", have_perimeters); bool have_top_solid_infill = config->opt_int("top_solid_layers") > 0; for (auto el : { "top_infill_extrusion_width", "top_solid_infill_speed" }) diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 454b4874f..07d75c947 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -189,7 +189,7 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true case coFloatOrPercent: { if (m_opt.type == coFloatOrPercent && !str.IsEmpty() && str.Last() != '%') { - double val; + double val = 0.; // Replace the first occurence of comma in decimal number. str.Replace(",", ".", false); if (check_value && !str.ToCDouble(&val)) @@ -197,8 +197,8 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true show_error(m_parent, _(L("Invalid numeric input."))); set_value(double_to_string(val), true); } - else if (check_value && (m_opt.sidetext.rfind("mm/s") != std::string::npos && val > m_opt.max || - m_opt.sidetext.rfind("mm ") != std::string::npos && val > 1) && + else if (check_value && ((m_opt.sidetext.rfind("mm/s") != std::string::npos && val > m_opt.max) || + (m_opt.sidetext.rfind("mm ") != std::string::npos && val > 1)) && (m_value.empty() || std::string(str.ToUTF8().data()) != boost::any_cast<std::string>(m_value))) { const std::string sidetext = m_opt.sidetext.rfind("mm/s") != std::string::npos ? "mm/s" : "mm"; @@ -444,7 +444,7 @@ void TextCtrl::disable() { dynamic_cast<wxTextCtrl*>(window)->Disable(); dynamic #ifdef __WXGTK__ void TextCtrl::change_field_value(wxEvent& event) { - if (bChangedValueEvent = event.GetEventType()==wxEVT_KEY_UP) + if (bChangedValueEvent = (event.GetEventType()==wxEVT_KEY_UP)) on_change_field(); event.Skip(); }; @@ -768,7 +768,7 @@ void Choice::set_selection() size_t idx = 0; for (auto el : m_opt.enum_values) { - if (el.compare(text_value) == 0) + if (el == text_value) break; ++idx; } @@ -789,7 +789,7 @@ void Choice::set_selection() size_t idx = 0; for (auto el : m_opt.enum_values) { - if (el.compare(text_value) == 0) + if (el == text_value) break; ++idx; } @@ -804,7 +804,7 @@ void Choice::set_selection() size_t idx = 0; for (auto el : m_opt.enum_values) { - if (el.compare(text_value) == 0) + if (el == text_value) break; ++idx; } @@ -813,6 +813,7 @@ void Choice::set_selection() field->SetSelection(idx); break; } + default: break; } } @@ -823,7 +824,7 @@ void Choice::set_value(const std::string& value, bool change_event) //! Redunda size_t idx=0; for (auto el : m_opt.enum_values) { - if (el.compare(value) == 0) + if (el == value) break; ++idx; } @@ -853,10 +854,10 @@ void Choice::set_value(const boost::any& value, bool change_event) text_value = wxString::Format(_T("%i"), int(boost::any_cast<int>(value))); else text_value = boost::any_cast<wxString>(value); - auto idx = 0; + size_t idx = 0; for (auto el : m_opt.enum_values) { - if (el.compare(text_value) == 0) + if (el == text_value) break; ++idx; } @@ -887,7 +888,7 @@ void Choice::set_value(const boost::any& value, bool change_event) size_t idx = 0; for (auto el : m_opt.enum_values) { - if (el.compare(key) == 0) + if (el == key) break; ++idx; } diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 71bacd815..edb9c7830 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -60,10 +60,6 @@ #endif // ENABLE_RENDER_STATISTICS static const float TRACKBALLSIZE = 0.8f; -static const float GROUND_Z = -0.02f; - -static const float GIZMO_RESET_BUTTON_HEIGHT = 22.0f; -static const float GIZMO_RESET_BUTTON_WIDTH = 70.f; static const float DEFAULT_BG_DARK_COLOR[3] = { 0.478f, 0.478f, 0.478f }; static const float DEFAULT_BG_LIGHT_COLOR[3] = { 0.753f, 0.753f, 0.753f }; @@ -845,22 +841,23 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_values(const GCodePrevie { auto& config = wxGetApp().preset_bundle->project_config; const std::vector<double>& color_print_values = config.option<ConfigOptionFloats>("colorprint_heights")->values; - const size_t values_cnt = color_print_values.size(); - if (values_cnt > 0) { + + if (!color_print_values.empty()) { std::vector<double> print_zs = canvas.get_current_print_zs(true); - size_t z = 0; - for (size_t i = 0; i < values_cnt; ++i) + for (auto cp_value : color_print_values) { - double prev_z = -1.0; - for ( ; z < print_zs.size(); ++z) - if (fabs(color_print_values[i] - print_zs[z]) < EPSILON) { - prev_z = z > 0 ? print_zs[z - 1] : 0.; - break; - } - if (prev_z < 0) + auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), cp_value - DoubleSlider::epsilon()); + + if (lower_b == print_zs.end()) continue; - cp_legend_values.push_back(std::pair<double, double>(prev_z, color_print_values[i])); + double current_z = *lower_b; + double previous_z = lower_b == print_zs.begin() ? 0.0 : *(--lower_b); + + // to avoid duplicate values, check adding values + if (cp_legend_values.empty() || + !(cp_legend_values.back().first == previous_z && cp_legend_values.back().second == current_z) ) + cp_legend_values.push_back(std::pair<double, double>(previous_z, current_z)); } } } @@ -901,11 +898,11 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c // Disabling ClearType works, but the font returned is very different (much thicker) from the default. // msw_disable_cleartype(font); - bool cleartype = is_font_cleartype(font); +// bool cleartype = is_font_cleartype(font); #else // select default font wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT).Scale(scale_gl); - bool cleartype = false; +// bool cleartype = false; #endif /* __WXMSW__ */ memDC.SetFont(font); @@ -1126,6 +1123,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar , m_retina_helper(nullptr) #endif , m_in_render(false) + , m_render_enabled(true) , m_bed(bed) , m_camera(camera) , m_view_toolbar(view_toolbar) @@ -1330,13 +1328,13 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject void GLCanvas3D::update_instance_printable_state_for_object(const size_t obj_idx) { ModelObject* model_object = m_model->objects[obj_idx]; - for (int inst_idx = 0; inst_idx < model_object->instances.size(); inst_idx++) + for (int inst_idx = 0; inst_idx < (int)model_object->instances.size(); ++inst_idx) { ModelInstance* instance = model_object->instances[inst_idx]; for (GLVolume* volume : m_volumes.volumes) { - if ((volume->object_idx() == obj_idx) && (volume->instance_idx() == inst_idx)) + if ((volume->object_idx() == (int)obj_idx) && (volume->instance_idx() == inst_idx)) volume->printable = instance->printable; } } @@ -1512,7 +1510,7 @@ void GLCanvas3D::update_volumes_colors_by_extruder() void GLCanvas3D::render() { - if (m_in_render) + if (!m_render_enabled || m_in_render) { // if called recursively, return m_dirty = true; @@ -1672,7 +1670,7 @@ void GLCanvas3D::ensure_on_bed(unsigned int object_idx) for (GLVolume* volume : m_volumes.volumes) { - if ((volume->object_idx() == object_idx) && !volume->is_modifier) + if ((volume->object_idx() == (int)object_idx) && !volume->is_modifier) { double min_z = volume->transformed_convex_hull_bounding_box().min(2); std::pair<int, int> instance = std::make_pair(volume->object_idx(), volume->instance_idx()); @@ -1968,8 +1966,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re } } - // stores the current volumes count - size_t volumes_count = m_volumes.volumes.size(); +// // stores the current volumes count +// size_t volumes_count = m_volumes.volumes.size(); for (size_t istep = 0; istep < sla_steps.size(); ++istep) if (!instances[istep].empty()) @@ -1978,7 +1976,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re // Shift-up all volumes of the object so that it has the right elevation with respect to the print bed for (GLVolume* volume : m_volumes.volumes) - if (volume->object_idx() < m_model->objects.size() && m_model->objects[volume->object_idx()]->instances[volume->instance_idx()]->is_printable()) + if (volume->object_idx() < (int)m_model->objects.size() && m_model->objects[volume->object_idx()]->instances[volume->instance_idx()]->is_printable()) volume->set_sla_shift_z(shift_zs[volume->object_idx()]); } @@ -3733,7 +3731,7 @@ bool GLCanvas3D::_init_undoredo_toolbar() item.right.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(true, 0.5f * (left + right)); }; item.enabling_callback = [this]()->bool { bool can_undo = wxGetApp().plater()->can_undo(); - unsigned int id = m_undoredo_toolbar.get_item_id("undo"); + int id = m_undoredo_toolbar.get_item_id("undo"); std::string curr_additional_tooltip; m_undoredo_toolbar.get_additional_tooltip(id, curr_additional_tooltip); @@ -3765,7 +3763,7 @@ bool GLCanvas3D::_init_undoredo_toolbar() item.right.render_callback = [this](float left, float right, float, float) { if (m_canvas != nullptr) _render_undo_redo_stack(false, 0.5f * (left + right)); }; item.enabling_callback = [this]()->bool { bool can_redo = wxGetApp().plater()->can_redo(); - unsigned int id = m_undoredo_toolbar.get_item_id("redo"); + int id = m_undoredo_toolbar.get_item_id("redo"); std::string curr_additional_tooltip; m_undoredo_toolbar.get_additional_tooltip(id, curr_additional_tooltip); @@ -3908,7 +3906,7 @@ void GLCanvas3D::_picking_pass() const m_gizmos.set_hover_id(-1); } else - m_gizmos.set_hover_id(inside && volume_id <= GLGizmoBase::BASE_ID ? (GLGizmoBase::BASE_ID - volume_id) : -1); + m_gizmos.set_hover_id(inside && (unsigned int)volume_id <= GLGizmoBase::BASE_ID ? ((int)GLGizmoBase::BASE_ID - volume_id) : -1); _update_volumes_hover_state(); } @@ -4858,7 +4856,7 @@ void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_ ctxt.print = print; ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors; if (print->wipe_tower_data().priming && print->config().single_extruder_multi_material_priming) - for (int i=0; i<print->wipe_tower_data().priming.get()->size(); ++i) + for (int i=0; i<(int)print->wipe_tower_data().priming.get()->size(); ++i) ctxt.priming.emplace_back(print->wipe_tower_data().priming.get()->at(i)); if (print->wipe_tower_data().final_purge) ctxt.final.emplace_back(*print->wipe_tower_data().final_purge.get()); @@ -5037,7 +5035,7 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat } case GCodePreviewData::Extrusion::ColorPrint: { - const size_t color_cnt = tool_colors.size() / 4; + int color_cnt = (int)tool_colors.size() / 4; int val = int(value); while (val >= color_cnt) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 0beef094b..202d3ef77 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -403,6 +403,7 @@ private: std::unique_ptr<RetinaHelper> m_retina_helper; #endif bool m_in_render; + bool m_render_enabled; LegendTexture m_legend_texture; WarningTexture m_warning_texture; wxTimer m_timer; @@ -433,7 +434,6 @@ private: bool m_initialized; bool m_apply_zoom_to_volumes_filter; mutable std::vector<int> m_hover_volume_idxs; - bool m_warning_texture_enabled; bool m_legend_texture_enabled; bool m_picking_enabled; bool m_moving_enabled; @@ -533,6 +533,9 @@ public: void enable_dynamic_background(bool enable); void allow_multisample(bool allow); + void enable_render(bool enable) { m_render_enabled = enable; } + bool is_render_enabled() const { return m_render_enabled; } + void zoom_to_bed(); void zoom_to_volumes(); void zoom_to_selection(); @@ -641,11 +644,9 @@ public: void start_keeping_dirty() { m_keep_dirty = true; } void stop_keeping_dirty() { m_keep_dirty = false; } - unsigned int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); } - void force_main_toolbar_left_action(unsigned int item_id) { m_main_toolbar.force_left_action(item_id, *this); } - void force_main_toolbar_right_action(unsigned int item_id) { m_main_toolbar.force_right_action(item_id, *this); } - void get_undoredo_toolbar_additional_tooltip(unsigned int item_id, std::string& text) { return m_undoredo_toolbar.get_additional_tooltip(item_id, text); } - void set_undoredo_toolbar_additional_tooltip(unsigned int item_id, const std::string& text) { m_undoredo_toolbar.set_additional_tooltip(item_id, text); } + int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); } + void force_main_toolbar_left_action(int item_id) { m_main_toolbar.force_left_action(item_id, *this); } + void force_main_toolbar_right_action(int item_id) { m_main_toolbar.force_right_action(item_id, *this); } bool has_toolpaths_to_export() const; void export_toolpaths_to_obj(const char* filename) const; diff --git a/src/slic3r/GUI/GLCanvas3DManager.cpp b/src/slic3r/GUI/GLCanvas3DManager.cpp index 59a34d8cd..5fbefcc6e 100644 --- a/src/slic3r/GUI/GLCanvas3DManager.cpp +++ b/src/slic3r/GUI/GLCanvas3DManager.cpp @@ -359,9 +359,7 @@ bool GLCanvas3DManager::init(GLCanvas3D& canvas) void GLCanvas3DManager::detect_multisample(int* attribList) { int wxVersion = wxMAJOR_VERSION * 10000 + wxMINOR_VERSION * 100 + wxRELEASE_NUMBER; - const AppConfig* app_config = GUI::get_app_config(); bool enable_multisample = wxVersion >= 30003; - s_multisample = (enable_multisample && wxGLCanvas::IsDisplaySupported(attribList)) ? MS_Enabled : MS_Disabled; // Alternative method: it was working on previous version of wxWidgets but not with the latest, at least on Windows // s_multisample = enable_multisample && wxGLCanvas::IsExtensionSupported("WGL_ARB_multisample"); diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index 4fdf12489..55ca5f723 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -56,7 +56,7 @@ bool GLTexture::Compressor::unsent_compressed_data_available() const { if (m_levels.empty()) return false; - // Querying the atomic m_num_levels_compressed value synchronizes processor caches, so that the dat of m_levels modified by the worker thread are accessible to the calling thread. + // Querying the atomic m_num_levels_compressed value synchronizes processor caches, so that the data of m_levels modified by the worker thread are accessible to the calling thread. unsigned int num_compressed = m_num_levels_compressed; for (unsigned int i = 0; i < num_compressed; ++ i) if (! m_levels[i].sent_to_gpu && ! m_levels[i].compressed_data.empty()) @@ -89,8 +89,8 @@ void GLTexture::Compressor::send_compressed_data_to_gpu() } glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); - if (num_compressed == (unsigned int)m_levels.size()) - // Finalize the worker thread, close it. + if (num_compressed == (int)m_levels.size()) + // Finalize the worker thread, close it. this->reset(); } diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index 63387bf2d..51d787d9d 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -345,9 +345,9 @@ bool GLToolbar::is_any_item_pressed() const return false; } -unsigned int GLToolbar::get_item_id(const std::string& name) const +int GLToolbar::get_item_id(const std::string& name) const { - for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) + for (int i = 0; i < (int)m_items.size(); ++i) { if (m_items[i]->get_name() == name) return i; @@ -356,19 +356,9 @@ unsigned int GLToolbar::get_item_id(const std::string& name) const return -1; } -void GLToolbar::force_left_action(unsigned int item_id, GLCanvas3D& parent) -{ - do_action(GLToolbarItem::Left, item_id, parent, false); -} - -void GLToolbar::force_right_action(unsigned int item_id, GLCanvas3D& parent) -{ - do_action(GLToolbarItem::Right, item_id, parent, false); -} - -void GLToolbar::get_additional_tooltip(unsigned int item_id, std::string& text) +void GLToolbar::get_additional_tooltip(int item_id, std::string& text) { - if (item_id < (unsigned int)m_items.size()) + if ((0 <= item_id) && (item_id < (int)m_items.size())) { GLToolbarItem* item = m_items[item_id]; if (item != nullptr) @@ -381,9 +371,9 @@ void GLToolbar::get_additional_tooltip(unsigned int item_id, std::string& text) text = L(""); } -void GLToolbar::set_additional_tooltip(unsigned int item_id, const std::string& text) +void GLToolbar::set_additional_tooltip(int item_id, const std::string& text) { - if (item_id < (unsigned int)m_items.size()) + if ((0 <= item_id) && (item_id < (int)m_items.size())) { GLToolbarItem* item = m_items[item_id]; if (item != nullptr) @@ -466,7 +456,7 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) if ((item_id != -2) && !m_items[item_id]->is_separator() && ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Left))) { // mouse is inside an icon - do_action(GLToolbarItem::Left, (unsigned int)item_id, parent, true); + do_action(GLToolbarItem::Left, item_id, parent, true); parent.set_as_dirty(); } } @@ -483,7 +473,7 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) if ((item_id != -2) && !m_items[item_id]->is_separator() && ((m_pressed_toggable_id == -1) || (m_items[item_id]->get_last_action_type() == GLToolbarItem::Right))) { // mouse is inside an icon - do_action(GLToolbarItem::Right, (unsigned int)item_id, parent, true); + do_action(GLToolbarItem::Right, item_id, parent, true); parent.set_as_dirty(); } } @@ -556,11 +546,11 @@ float GLToolbar::get_main_size() const return size * m_layout.scale; } -void GLToolbar::do_action(GLToolbarItem::EActionType type, unsigned int item_id, GLCanvas3D& parent, bool check_hover) +void GLToolbar::do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas3D& parent, bool check_hover) { if ((m_pressed_toggable_id == -1) || (m_pressed_toggable_id == item_id)) { - if (item_id < (unsigned int)m_items.size()) + if ((0 <= item_id) && (item_id < (int)m_items.size())) { GLToolbarItem* item = m_items[item_id]; if ((item != nullptr) && !item->is_separator() && (!check_hover || item->is_hovered())) @@ -871,11 +861,10 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3 float left = m_layout.left + scaled_border; float top = m_layout.top - scaled_border; - int id = -1; - - for (GLToolbarItem* item : m_items) + + for (size_t id=0; id<m_items.size(); ++id) { - ++id; + GLToolbarItem* item = m_items[id]; if (!item->is_visible()) continue; @@ -946,11 +935,9 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& float left = m_layout.left + scaled_border; float top = m_layout.top - scaled_border; - int id = -1; - - for (GLToolbarItem* item : m_items) + for (size_t id=0; id<m_items.size(); ++id) { - ++id; + GLToolbarItem* item = m_items[id]; if (!item->is_visible()) continue; @@ -1246,7 +1233,7 @@ bool GLToolbar::update_items_enabled_state() { bool ret = false; - for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) + for (int i = 0; i < (int)m_items.size(); ++i) { GLToolbarItem* item = m_items[i]; ret |= item->update_enabled_state(); diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index 527317e58..07da836fc 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -254,7 +254,7 @@ private: MouseCapture m_mouse_capture; std::string m_tooltip; - unsigned int m_pressed_toggable_id; + int m_pressed_toggable_id; public: GLToolbar(EType type, const std::string& name); @@ -293,15 +293,15 @@ public: bool is_any_item_pressed() const; - unsigned int get_item_id(const std::string& name) const; + int get_item_id(const std::string& name) const; - void force_left_action(unsigned int item_id, GLCanvas3D& parent); - void force_right_action(unsigned int item_id, GLCanvas3D& parent); + void force_left_action(int item_id, GLCanvas3D& parent) { do_action(GLToolbarItem::Left, item_id, parent, false); } + void force_right_action(int item_id, GLCanvas3D& parent) { do_action(GLToolbarItem::Right, item_id, parent, false); } const std::string& get_tooltip() const { return m_tooltip; } - void get_additional_tooltip(unsigned int item_id, std::string& text); - void set_additional_tooltip(unsigned int item_id, const std::string& text); + void get_additional_tooltip(int item_id, std::string& text); + void set_additional_tooltip(int item_id, const std::string& text); // returns true if any item changed its state bool update_items_state(); @@ -317,7 +317,7 @@ private: float get_height_horizontal() const; float get_height_vertical() const; float get_main_size() const; - void do_action(GLToolbarItem::EActionType type, unsigned int item_id, GLCanvas3D& parent, bool check_hover); + void do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas3D& parent, bool check_hover); std::string update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent); std::string update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent); std::string update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent); diff --git a/src/slic3r/GUI/GUI.cpp b/src/slic3r/GUI/GUI.cpp index 826f2d6fc..f94372667 100644 --- a/src/slic3r/GUI/GUI.cpp +++ b/src/slic3r/GUI/GUI.cpp @@ -262,7 +262,7 @@ void change_opt_value(DynamicPrintConfig& config, const t_config_option_key& opt } catch (const std::exception & /* e */) { - int i = 0;//no reason, just experiment + // int i = 0;//no reason, just experiment } } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 1b2da4769..80c02ea78 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -302,8 +302,9 @@ bool GUI_App::on_init_inner() * change min hight of object list to the normal min value (15 * wxGetApp().em_unit()) * after first whole Mainframe updating/layouting */ - if (obj_list()->GetMinSize().GetY() > 15 * em_unit()) - obj_list()->SetMinSize(wxSize(-1, 15 * em_unit())); + const int list_min_height = 15 * em_unit(); + if (obj_list()->GetMinSize().GetY() > list_min_height) + obj_list()->SetMinSize(wxSize(-1, list_min_height)); update_mode(); // update view mode after fix of the object_list size @@ -474,8 +475,9 @@ void GUI_App::recreate_GUI() * change min hight of object list to the normal min value (15 * wxGetApp().em_unit()) * after first whole Mainframe updating/layouting */ - if (obj_list()->GetMinSize().GetY() > 15 * em_unit()) - obj_list()->SetMinSize(wxSize(-1, 15 * em_unit())); + const int list_min_height = 15 * em_unit(); + if (obj_list()->GetMinSize().GetY() > list_min_height) + obj_list()->SetMinSize(wxSize(-1, list_min_height)); update_mode(); @@ -908,11 +910,13 @@ bool GUI_App::check_unsaved_changes(const wxString &header) wxString dirty; PrinterTechnology printer_technology = preset_bundle->printers.get_edited_preset().printer_technology(); for (Tab *tab : tabs_list) - if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) + if (tab->supports_printer_technology(printer_technology) && tab->current_preset_is_dirty()) { if (dirty.empty()) dirty = tab->title(); else dirty += wxString(", ") + tab->title(); + } + if (dirty.empty()) // No changes, the application may close or reload presets. return true; @@ -1085,7 +1089,7 @@ void GUI_App::window_pos_restore(wxTopLevelWindow* window, const std::string &na void GUI_App::window_pos_sanitize(wxTopLevelWindow* window) { - unsigned display_idx = wxDisplay::GetFromWindow(window); + /*unsigned*/int display_idx = wxDisplay::GetFromWindow(window); wxRect display; if (display_idx == wxNOT_FOUND) { display = wxDisplay(0u).GetClientArea(); diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index 7ba26f471..a79426ed2 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -29,7 +29,7 @@ namespace GUI wxDEFINE_EVENT(EVT_OBJ_LIST_OBJECT_SELECT, SimpleEvent); // pt_FFF -SettingsBundle FREQ_SETTINGS_BUNDLE_FFF = +static SettingsBundle FREQ_SETTINGS_BUNDLE_FFF = { { L("Layers and Perimeters"), { "layer_height" , "perimeters", "top_solid_layers", "bottom_solid_layers" } }, { L("Infill") , { "fill_density", "fill_pattern" } }, @@ -40,13 +40,13 @@ SettingsBundle FREQ_SETTINGS_BUNDLE_FFF = }; // pt_SLA -SettingsBundle FREQ_SETTINGS_BUNDLE_SLA = +static SettingsBundle FREQ_SETTINGS_BUNDLE_SLA = { { L("Pad and Support") , { "supports_enable", "pad_enable" } } }; // Note: id accords to type of the sub-object (adding volume), so sequence of the menu items is important -std::vector<std::pair<std::string, std::string>> ADD_VOLUME_MENU_ITEMS = { +static std::vector<std::pair<std::string, std::string>> ADD_VOLUME_MENU_ITEMS = { // menu_item Name menu_item bitmap name {L("Add part"), "add_part" }, // ~ModelVolumeType::MODEL_PART {L("Add modifier"), "add_modifier"}, // ~ModelVolumeType::PARAMETER_MODIFIER @@ -126,7 +126,7 @@ ObjectList::ObjectList(wxWindow* parent) : #ifdef __WXMSW__ // Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected. int new_selected_column = -1; -#endif __WXMSW__ +#endif //__WXMSW__ if (wxGetKeyState(WXK_SHIFT)) { wxDataViewItemArray sels; @@ -149,12 +149,12 @@ ObjectList::ObjectList(wxWindow* parent) : wxUIActionSimulator sim; sim.Char(WXK_RETURN); } -#endif __WXMSW__ +#endif //__WXMSW__ m_last_selected_item = new_selected_item; } #ifdef __WXMSW__ m_last_selected_column = new_selected_column; -#endif __WXMSW__ +#endif //__WXMSW__ selection_changed(); #ifndef __WXMSW__ @@ -192,7 +192,7 @@ ObjectList::ObjectList(wxWindow* parent) : this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->undo(); }, wxID_UNDO); this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->redo(); }, wxID_REDO); } -#else __WXOSX__ +#else //__WXOSX__ Bind(wxEVT_CHAR, [this](wxKeyEvent& event) { key_event(event); }); // doesn't work on OSX #endif @@ -281,9 +281,9 @@ void ObjectList::create_popup_menus() create_instance_popupmenu(&m_menu_instance); } -void ObjectList::get_selected_item_indexes(int& obj_idx, int& vol_idx, const wxDataViewItem& input_item/* = wxDataViewItem(0)*/) +void ObjectList::get_selected_item_indexes(int& obj_idx, int& vol_idx, const wxDataViewItem& input_item/* = wxDataViewItem(nullptr)*/) { - const wxDataViewItem item = input_item == wxDataViewItem(0) ? GetSelection() : input_item; + const wxDataViewItem item = input_item == wxDataViewItem(nullptr) ? GetSelection() : input_item; if (!item) { @@ -422,7 +422,7 @@ DynamicPrintConfig& ObjectList::get_item_config(const wxDataViewItem& item) cons (*m_objects)[obj_idx]->config; } -wxDataViewColumn* ObjectList::create_objects_list_extruder_column(int extruders_count) +wxDataViewColumn* ObjectList::create_objects_list_extruder_column(size_t extruders_count) { wxArrayString choices; choices.Add(_(L("default"))); @@ -435,9 +435,9 @@ wxDataViewColumn* ObjectList::create_objects_list_extruder_column(int extruders_ return column; } -void ObjectList::update_extruder_values_for_items(const int max_extruder) +void ObjectList::update_extruder_values_for_items(const size_t max_extruder) { - for (int i = 0; i < m_objects->size(); ++i) + for (size_t i = 0; i < m_objects->size(); ++i) { wxDataViewItem item = m_objects_model->GetItemById(i); if (!item) continue; @@ -453,7 +453,7 @@ void ObjectList::update_extruder_values_for_items(const int max_extruder) m_objects_model->SetValue(extruder, item, colExtruder); if (object->volumes.size() > 1) { - for (auto id = 0; id < object->volumes.size(); id++) { + for (size_t id = 0; id < object->volumes.size(); id++) { item = m_objects_model->GetItemByVolumeId(i, id); if (!item) continue; if (!object->volumes[id]->config.has("extruder") || @@ -468,7 +468,7 @@ void ObjectList::update_extruder_values_for_items(const int max_extruder) } } -void ObjectList::update_objects_list_extruder_column(int extruders_count) +void ObjectList::update_objects_list_extruder_column(size_t extruders_count) { if (!this) return; // #ys_FIXME if (printer_technology() == ptSLA) @@ -737,7 +737,7 @@ void ObjectList::paste_volumes_into_list(int obj_idx, const ModelVolumePtrs& vol if (items.size() > 1) { m_selection_mode = smVolume; - m_last_selected_item = wxDataViewItem(0); + m_last_selected_item = wxDataViewItem(nullptr); } select_items(items); @@ -839,7 +839,7 @@ void ObjectList::show_context_menu() wxMenu* menu = type & itInstance ? &m_menu_instance : type & itLayer ? &m_menu_layer : - m_objects_model->GetParent(item) != wxDataViewItem(0) ? &m_menu_part : + m_objects_model->GetParent(item) != wxDataViewItem(nullptr) ? &m_menu_part : printer_technology() == ptFFF ? &m_menu_object : &m_menu_sla_object; if (!(type & itInstance)) @@ -910,9 +910,9 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event) const bool mult_sel = multiple_selection(); - if (mult_sel && !selected_instances_of_same_object() || - !mult_sel && (GetSelection() != item || - m_objects_model->GetParent(item) == wxDataViewItem(0) ) ) { + if ((mult_sel && !selected_instances_of_same_object()) || + (!mult_sel && (GetSelection() != item)) || + m_objects_model->GetParent(item) == wxDataViewItem(nullptr) ) { event.Veto(); return; } @@ -957,10 +957,10 @@ void ObjectList::OnBeginDrag(wxDataViewEvent &event) bool ObjectList::can_drop(const wxDataViewItem& item) const { - return m_dragged_data.type() == itInstance && !item.IsOk() || - m_dragged_data.type() == itVolume && item.IsOk() && + return (m_dragged_data.type() == itInstance && !item.IsOk()) || + (m_dragged_data.type() == itVolume && item.IsOk() && m_objects_model->GetItemType(item) == itVolume && - m_dragged_data.obj_idx() == m_objects_model->GetObjectIdByItem(item); + m_dragged_data.obj_idx() == m_objects_model->GetObjectIdByItem(item)); } void ObjectList::OnDropPossible(wxDataViewEvent &event) @@ -1069,8 +1069,8 @@ const std::vector<std::string>& ObjectList::get_options_for_bundle(const wxStrin static bool improper_category(const std::string& category, const int extruders_cnt, const bool is_object_settings = true) { return category.empty() || - extruders_cnt == 1 && (category == "Extruders" || category == "Wipe options" ) || - !is_object_settings && category == "Support material"; + (extruders_cnt == 1 && (category == "Extruders" || category == "Wipe options" )) || + (!is_object_settings && category == "Support material"); } void ObjectList::get_options_menu(settings_menu_hierarchy& settings_menu, const bool is_part) @@ -1326,7 +1326,7 @@ void ObjectList::append_menu_items_add_volume(wxMenu* menu) return; } - for (int type = mode == comExpert ? 0 : 1 ; type < ADD_VOLUME_MENU_ITEMS.size(); type++) + for (size_t type = (mode == comExpert ? 0 : 1) ; type < ADD_VOLUME_MENU_ITEMS.size(); type++) { auto& item = ADD_VOLUME_MENU_ITEMS[type]; @@ -1397,7 +1397,7 @@ wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu_) // Create new items for settings popupmenu if (printer_technology() == ptFFF || - menu->GetMenuItems().size() > 0 && !menu->GetMenuItems().back()->IsSeparator()) + (menu->GetMenuItems().size() > 0 && !menu->GetMenuItems().back()->IsSeparator())) menu->SetFirstSeparator(); // Add frequently settings @@ -1430,9 +1430,9 @@ wxMenuItem* ObjectList::append_menu_item_instance_to_object(wxMenu* menu, wxWind [this](wxCommandEvent&) { split_instances(); }, "", menu, [](){return wxGetApp().plater()->can_set_instance_to_object(); }, parent); } -wxMenuItem* ObjectList::append_menu_item_printable(wxMenu* menu, wxWindow* parent) +wxMenuItem* ObjectList::append_menu_item_printable(wxMenu* menu, wxWindow* /*parent*/) { - return append_menu_check_item(menu, wxID_ANY, _(L("Printable")), "", [this](wxCommandEvent&) { + return append_menu_check_item(menu, wxID_ANY, _(L("Printable")), "", [](wxCommandEvent&) { wxGetApp().plater()->canvas3D()->get_selection().toggle_instance_printable_state(); }, menu); } @@ -1505,7 +1505,7 @@ void ObjectList::append_menu_item_delete(wxMenu* menu) void ObjectList::append_menu_item_scale_selection_to_fit_print_volume(wxMenu* menu) { append_menu_item(menu, wxID_ANY, _(L("Scale to print volume")), _(L("Scale the selected object to fit the print volume")), - [this](wxCommandEvent&) { wxGetApp().plater()->scale_selection_to_fit_print_volume(); }, "", menu); + [](wxCommandEvent&) { wxGetApp().plater()->scale_selection_to_fit_print_volume(); }, "", menu); } void ObjectList::create_object_popupmenu(wxMenu *menu) @@ -1571,7 +1571,7 @@ void ObjectList::create_instance_popupmenu(wxMenu*menu) * 2. Separate selected instances from the initial object to the separated object, * if some (not all) instances are selected */ - wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { + wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [](wxUpdateUIEvent& evt) { // evt.Enable(can_split_instances()); }, m_menu_item_split_instances->GetId()); evt.SetText(wxGetApp().plater()->canvas3D()->get_selection().is_single_full_object() ? _(L("Set as a Separated Objects")) : _(L("Set as a Separated Object"))); @@ -1583,7 +1583,7 @@ wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu) wxMenu *menu = new wxMenu; settings_menu_hierarchy settings_menu; - const bool is_part = m_objects_model->GetParent(GetSelection()) != wxDataViewItem(0); + const bool is_part = m_objects_model->GetParent(GetSelection()) != wxDataViewItem(nullptr); get_options_menu(settings_menu, is_part); for (auto cat : settings_menu) { @@ -1676,7 +1676,7 @@ void ObjectList::load_part( ModelObject* model_object, wxArrayString input_files; wxGetApp().import_model(parent, input_files); - for (int i = 0; i < input_files.size(); ++i) { + for (size_t i = 0; i < input_files.size(); ++i) { std::string input_file = input_files.Item(i).ToUTF8().data(); Model model; @@ -1714,7 +1714,7 @@ void ObjectList::load_part( ModelObject* model_object, void ObjectList::load_generic_subobject(const std::string& type_name, const ModelVolumeType type) { - const auto obj_idx = get_selected_obj_idx(); + const int obj_idx = get_selected_obj_idx(); if (obj_idx < 0) return; @@ -1847,9 +1847,9 @@ void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item) { const bool is_layer_settings = m_objects_model->GetItemType(parent_item) == itLayer; - const int opt_cnt = m_config->keys().size(); - if (opt_cnt == 1 && m_config->has("extruder") || - is_layer_settings && opt_cnt == 2 && m_config->has("extruder") && m_config->has("layer_height")) + const size_t opt_cnt = m_config->keys().size(); + if ((opt_cnt == 1 && m_config->has("extruder")) || + (is_layer_settings && opt_cnt == 2 && m_config->has("extruder") && m_config->has("layer_height"))) return; take_snapshot(_(L("Delete Settings"))); @@ -2047,13 +2047,13 @@ wxDataViewItem ObjectList::add_layer_root_item(const wxDataViewItem obj_item) if (obj_idx < 0 || object(obj_idx)->layer_config_ranges.empty() || printer_technology() == ptSLA) - return wxDataViewItem(0); + return wxDataViewItem(nullptr); // create LayerRoot item wxDataViewItem layers_item = m_objects_model->AddLayersRoot(obj_item); // and create Layer item(s) according to the layer_config_ranges - for (const auto range : object(obj_idx)->layer_config_ranges) + for (const auto& range : object(obj_idx)->layer_config_ranges) add_layer_item(range.first, layers_item); Expand(layers_item); @@ -2145,7 +2145,7 @@ void ObjectList::part_selection_changed() const auto item = GetSelection(); - if ( multiple_selection() || item && m_objects_model->GetItemType(item) == itInstanceRoot ) + if ( multiple_selection() || (item && m_objects_model->GetItemType(item) == itInstanceRoot )) { og_name = _(L("Group manipulation")); @@ -2157,7 +2157,7 @@ void ObjectList::part_selection_changed() { if (item) { - if (m_objects_model->GetParent(item) == wxDataViewItem(0)) { + if (m_objects_model->GetParent(item) == wxDataViewItem(nullptr)) { obj_idx = m_objects_model->GetIdByItem(item); og_name = _(L("Object manipulation")); m_config = &(*m_objects)[obj_idx]->config; @@ -2276,7 +2276,7 @@ SettingsBundle ObjectList::get_item_settings_bundle(const DynamicPrintConfig* co // Add new SettingsItem for parent_item if it doesn't exist, or just update a digest according to new config wxDataViewItem ObjectList::add_settings_item(wxDataViewItem parent_item, const DynamicPrintConfig* config) { - wxDataViewItem ret = wxDataViewItem(0); + wxDataViewItem ret = wxDataViewItem(nullptr); if (!parent_item) return ret; @@ -2332,7 +2332,7 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed) if (model_object->instances.size()>1) { std::vector<bool> print_idicator(model_object->instances.size()); - for (int i = 0; i < model_object->instances.size(); ++i) + for (size_t i = 0; i < model_object->instances.size(); ++i) print_idicator[i] = model_object->instances[i]->printable; const wxDataViewItem object_item = m_objects_model->GetItemById(obj_idx); @@ -2359,7 +2359,7 @@ void ObjectList::delete_object_from_list() auto item = GetSelection(); if (!item) return; - if (m_objects_model->GetParent(item) == wxDataViewItem(0)) + if (m_objects_model->GetParent(item) == wxDataViewItem(nullptr)) select_item(m_objects_model->Delete(item)); else select_item(m_objects_model->Delete(m_objects_model->GetParent(item))); @@ -2516,7 +2516,7 @@ void ObjectList::remove() wxDataViewItemArray sels; GetSelections(sels); - wxDataViewItem parent = wxDataViewItem(0); + wxDataViewItem parent = wxDataViewItem(nullptr); if (sels.Count() == 1) parent = delete_item(GetSelection()); @@ -2565,7 +2565,14 @@ static double get_min_layer_height(const int extruder_idx) static double get_max_layer_height(const int extruder_idx) { const DynamicPrintConfig& config = wxGetApp().preset_bundle->printers.get_edited_preset().config; - return config.opt_float("max_layer_height", extruder_idx <= 0 ? 0 : extruder_idx-1); + int extruder_idx_zero_based = extruder_idx <= 0 ? 0 : extruder_idx-1; + double max_layer_height = config.opt_float("max_layer_height", extruder_idx_zero_based); + + // In case max_layer_height is set to zero, it should default to 75 % of nozzle diameter: + if (max_layer_height < EPSILON) + max_layer_height = 0.75 * config.opt_float("nozzle_diameter", extruder_idx_zero_based); + + return max_layer_height; } void ObjectList::add_layer_range_after_current(const t_layer_height_range& current_range) @@ -2583,7 +2590,7 @@ void ObjectList::add_layer_range_after_current(const t_layer_height_range& curre { take_snapshot(_(L("Add Height Range"))); - const t_layer_height_range& new_range = { last_range.second, last_range.second + 2.0f }; + const t_layer_height_range& new_range = { last_range.second, last_range.second + 2. }; ranges[new_range] = get_default_layer_config(obj_idx); add_layer_item(new_range, layers_item); } @@ -2606,7 +2613,7 @@ void ObjectList::add_layer_range_after_current(const t_layer_height_range& curre if (delta < get_min_layer_height(old_config.opt_int("extruder"))/*0.05f*/) // next range division has no sense return; - const coordf_t midl_layer = next_range.first + 0.5f * delta; + const coordf_t midl_layer = next_range.first + 0.5 * delta; t_layer_height_range new_range = { midl_layer, next_range.second }; @@ -2706,7 +2713,7 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_lay if (root_item.IsOk()) // create Layer item(s) according to the layer_config_ranges - for (const auto r : ranges) + for (const auto& r : ranges) add_layer_item(r.first, root_item); select_item(sel_type&itLayer ? m_objects_model->GetItemByLayerRange(obj_idx, new_range) : root_item); @@ -3023,7 +3030,7 @@ void ObjectList::select_item_all_children() // There is no selection before OR some object is selected => select all objects if (!GetSelection() || m_objects_model->GetItemType(GetSelection()) == itObject) { - for (int i = 0; i < m_objects->size(); i++) + for (size_t i = 0; i < m_objects->size(); i++) sels.Add(m_objects_model->GetItemById(i)); m_selection_mode = smInstance; } @@ -3049,7 +3056,7 @@ void ObjectList::update_selection_mode() // All items are unselected if (!GetSelection()) { - m_last_selected_item = wxDataViewItem(0); + m_last_selected_item = wxDataViewItem(nullptr); m_selection_mode = smUndef; return; } @@ -3091,9 +3098,9 @@ bool ObjectList::check_last_selection(wxString& msg_str) if (impossible_multi_selection(itVolume, smVolume) || impossible_multi_selection(itLayer, smLayer ) || type & itSettings || - type & itVolume && !(m_selection_mode & smVolume ) || - type & itLayer && !(m_selection_mode & smLayer ) || - type & itInstance && !(m_selection_mode & smInstance) + (type & itVolume && !(m_selection_mode & smVolume )) || + (type & itLayer && !(m_selection_mode & smLayer )) || + (type & itInstance && !(m_selection_mode & smInstance)) ) { // Inform user why selection isn't complited @@ -3146,7 +3153,7 @@ void ObjectList::fix_multiselection_conflicts() const ItemType item_type = m_selection_mode & smVolume ? itVolume : itLayer; - for (const auto child : children) + for (const auto& child : children) if (IsSelected(child) && m_objects_model->GetItemType(child) & item_type) sels.Add(child); @@ -3156,7 +3163,7 @@ void ObjectList::fix_multiselection_conflicts() } else { - for (const auto item : sels) + for (const auto& item : sels) { if (!IsSelected(item)) // if this item is unselected now (from previous actions) continue; @@ -3167,13 +3174,13 @@ void ObjectList::fix_multiselection_conflicts() } const wxDataViewItem& parent = m_objects_model->GetParent(item); - if (parent != wxDataViewItem(0) && IsSelected(parent)) + if (parent != wxDataViewItem(nullptr) && IsSelected(parent)) Unselect(parent); else { wxDataViewItemArray unsels; m_objects_model->GetAllChildren(item, unsels); - for (const auto unsel_item : unsels) + for (const auto& unsel_item : unsels) Unselect(unsel_item); } @@ -3188,7 +3195,7 @@ void ObjectList::fix_multiselection_conflicts() show_info(this, msg_string, _(L("Info"))); if (!IsSelected(m_last_selected_item)) - m_last_selected_item = wxDataViewItem(0); + m_last_selected_item = wxDataViewItem(nullptr); m_prevent_list_events = false; } @@ -3262,7 +3269,7 @@ void ObjectList::change_part_type() void ObjectList::last_volume_is_deleted(const int obj_idx) { - if (obj_idx < 0 || obj_idx >= m_objects->size() || (*m_objects)[obj_idx]->volumes.size() != 1) + if (obj_idx < 0 || size_t(obj_idx) >= m_objects->size() || (*m_objects)[obj_idx]->volumes.size() != 1) return; auto volume = (*m_objects)[obj_idx]->volumes.front(); @@ -3329,7 +3336,7 @@ void ObjectList::update_object_list_by_printer_technology() GetSelections(sel); // stash selection wxDataViewItemArray object_items; - m_objects_model->GetChildren(wxDataViewItem(0), object_items); + m_objects_model->GetChildren(wxDataViewItem(nullptr), object_items); for (auto& object_item : object_items) { // Update Settings Item for object @@ -3400,7 +3407,7 @@ void ObjectList::instances_to_separated_object(const int obj_idx, const std::set // create new object from selected instance ModelObject* model_object = (*m_objects)[obj_idx]->get_model()->add_object(*(*m_objects)[obj_idx]); - for (int inst_idx = model_object->instances.size() - 1; inst_idx >= 0; inst_idx--) + for (int inst_idx = int(model_object->instances.size()) - 1; inst_idx >= 0; inst_idx--) { if (find(inst_idxs.begin(), inst_idxs.end(), inst_idx) != inst_idxs.end()) continue; @@ -3594,7 +3601,7 @@ void ObjectList::OnEditingStarted(wxDataViewEvent &event) { m_last_selected_column = -1; } -#endif __WXMSW__ +#endif //__WXMSW__ void ObjectList::OnEditingDone(wxDataViewEvent &event) { @@ -3613,7 +3620,7 @@ void ObjectList::OnEditingDone(wxDataViewEvent &event) // Workaround for entering the column editing mode on Windows. Simulate keyboard enter when another column of the active line is selected. // Here the last active column is forgotten, so when leaving the editing mode, the next mouse click will not enter the editing mode of the newly selected column. m_last_selected_column = -1; -#endif __WXMSW__ +#endif //__WXMSW__ } void ObjectList::show_multi_selection_menu() diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index d19a1002d..13d1106fc 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -182,15 +182,15 @@ public: void create_objects_ctrl(); void create_popup_menus(); - wxDataViewColumn* create_objects_list_extruder_column(int extruders_count); - void update_objects_list_extruder_column(int extruders_count); + wxDataViewColumn* create_objects_list_extruder_column(size_t extruders_count); + void update_objects_list_extruder_column(size_t extruders_count); // show/hide "Extruder" column for Objects List void set_extruder_column_hidden(const bool hide) const; // update extruder in current config void update_extruder_in_config(const wxDataViewItem& item); // update changed name in the object model void update_name_in_model(const wxDataViewItem& item) const; - void update_extruder_values_for_items(const int max_extruder); + void update_extruder_values_for_items(const size_t max_extruder); void init_icons(); void msw_rescale_icons(); diff --git a/src/slic3r/GUI/GUI_ObjectManipulation.cpp b/src/slic3r/GUI/GUI_ObjectManipulation.cpp index 1f1de4003..695c81b5f 100644 --- a/src/slic3r/GUI/GUI_ObjectManipulation.cpp +++ b/src/slic3r/GUI/GUI_ObjectManipulation.cpp @@ -668,7 +668,7 @@ void ObjectManipulation::update_mirror_buttons_visibility() wxGetApp().CallAfter([this, new_states]{ for (int i=0; i<3; ++i) { if (new_states[i] != m_mirror_buttons[i].second) { - const ScalableBitmap* bmp; + const ScalableBitmap* bmp = nullptr; switch (new_states[i]) { case mbHidden : bmp = &m_mirror_bitmap_hidden; m_mirror_buttons[i].first->Enable(false); break; case mbShown : bmp = &m_mirror_bitmap_off; m_mirror_buttons[i].first->Enable(true); break; diff --git a/src/slic3r/GUI/GUI_ObjectSettings.cpp b/src/slic3r/GUI/GUI_ObjectSettings.cpp index c90e1e4ed..0c10c5460 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.cpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.cpp @@ -116,7 +116,7 @@ bool ObjectSettings::update_settings_list() auto optgroup = std::make_shared<ConfigOptionsGroup>(m_og->ctrl_parent(), _(cat.first), config, false, extra_column); optgroup->label_width = 15; - optgroup->sidetext_width = 5.5; + optgroup->sidetext_width = 5; optgroup->m_on_change = [this, config](const t_config_option_key& opt_id, const boost::any& value) { this->update_config_values(config); @@ -241,4 +241,4 @@ void ObjectSettings::msw_rescale() } } //namespace GUI -} //namespace Slic3r
\ No newline at end of file +} //namespace Slic3r diff --git a/src/slic3r/GUI/GUI_ObjectSettings.hpp b/src/slic3r/GUI/GUI_ObjectSettings.hpp index 61d90600b..a5724811a 100644 --- a/src/slic3r/GUI/GUI_ObjectSettings.hpp +++ b/src/slic3r/GUI/GUI_ObjectSettings.hpp @@ -5,11 +5,11 @@ #include <vector> #include <wx/panel.h> #include "wxExtensions.hpp" -#include "libslic3r/PrintConfig.hpp" class wxBoxSizer; namespace Slic3r { +class DynamicPrintConfig; namespace GUI { class ConfigOptionsGroup; diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index 2b9da6aa0..7c761ed5f 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -640,9 +640,10 @@ void Preview::update_double_slider(const std::vector<double>& layers_z, bool kee bool snap_to_min = force_sliders_full_range || m_slider->is_lower_at_min(); bool snap_to_max = force_sliders_full_range || m_slider->is_higher_at_max(); - std::vector<std::pair<int, double>> values; - fill_slider_values(values, layers_z); - m_slider->SetSliderValues(values); + std::vector<double> &ticks_from_config = (wxGetApp().preset_bundle->project_config.option<ConfigOptionFloats>("colorprint_heights"))->values; + check_slider_values(ticks_from_config, layers_z); + + m_slider->SetSliderValues(layers_z); assert(m_slider->GetMinValue() == 0); m_slider->SetMaxValue(layers_z.empty() ? 0 : layers_z.size() - 1); @@ -662,9 +663,6 @@ void Preview::update_double_slider(const std::vector<double>& layers_z, bool kee } m_slider->SetSelectionSpan(idx_low, idx_high); - const auto& config = wxGetApp().preset_bundle->project_config; - const std::vector<double> &ticks_from_config = (config.option<ConfigOptionFloats>("colorprint_heights"))->values; - m_slider->SetTicksValues(ticks_from_config); bool color_print_enable = (wxGetApp().plater()->printer_technology() == ptFFF); @@ -676,26 +674,18 @@ void Preview::update_double_slider(const std::vector<double>& layers_z, bool kee m_slider->EnableTickManipulation(color_print_enable); } -void Preview::fill_slider_values(std::vector<std::pair<int, double>> &values, +void Preview::check_slider_values(std::vector<double>& ticks_from_config, const std::vector<double> &layers_z) { - values.clear(); - for (int i = 0; i < layers_z.size(); ++i) - { - values.push_back(std::pair<int, double>(i + 1, layers_z[i])); - } - // All ticks that would end up outside the slider range should be erased. // TODO: this should be placed into more appropriate part of code, // this function is e.g. not called when the last object is deleted - std::vector<double> &ticks_from_config = (wxGetApp().preset_bundle->project_config.option<ConfigOptionFloats>("colorprint_heights"))->values; unsigned int old_size = ticks_from_config.size(); ticks_from_config.erase(std::remove_if(ticks_from_config.begin(), ticks_from_config.end(), - [values](double val) + [layers_z](double val) { - return (values.back().second < val && - // we can't ignore tick on last layer - fabs(values.back().second - val) > DoubleSlider::epsilon()); + auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val - DoubleSlider::epsilon()); + return it == layers_z.end(); }), ticks_from_config.end()); if (ticks_from_config.size() != old_size) diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index 401b17a4b..08d5991b4 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -155,7 +155,7 @@ private: // Create/Update/Reset double slider on 3dPreview void create_double_slider(); void update_double_slider(const std::vector<double>& layers_z, bool keep_z_range = false); - void fill_slider_values(std::vector<std::pair<int, double>> &values, + void check_slider_values(std::vector<double> &ticks_from_config, const std::vector<double> &layers_z); void reset_double_slider(); // update DoubleSlider after keyDown in canvas diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index 18053dcd6..da3042779 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -69,7 +69,6 @@ public: enum EState { Off, - Hover, On, Num_States }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp index c69d64134..9cd2ab6bb 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp @@ -44,12 +44,12 @@ public: Vec3d get_flattening_normal() const; protected: - virtual bool on_init(); - virtual std::string on_get_name() const; - virtual bool on_is_activable() const; - virtual void on_start_dragging(); - virtual void on_render() const; - virtual void on_render_for_picking() const; + virtual bool on_init() override; + virtual std::string on_get_name() const override; + virtual bool on_is_activable() const override; + virtual void on_start_dragging() override; + virtual void on_render() const override; + virtual void on_render_for_picking() const override; virtual void on_set_state() override; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index 8733c9a11..c856e5465 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -87,16 +87,12 @@ protected: virtual void on_set_state() { for (GLGizmoRotate& g : m_gizmos) - { g.set_state(m_state); - } } virtual void on_set_hover_id() { for (int i = 0; i < 3; ++i) - { m_gizmos[i].set_hover_id((m_hover_id == i) ? 0 : -1); - } } virtual void on_enable_grabber(unsigned int id) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index bf540cb00..a685fb774 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -329,6 +329,7 @@ void GLGizmoScale3D::do_scale_along_axis(Axis axis, const UpdateData& data) case X: { local_offset_vec = local_offset * Vec3d::UnitX(); break; } case Y: { local_offset_vec = local_offset * Vec3d::UnitY(); break; } case Z: { local_offset_vec = local_offset * Vec3d::UnitZ(); break; } + default: break; } m_offset = m_offsets_transform * local_offset_vec; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 28d569761..0a5c21cce 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -12,7 +12,9 @@ #include "slic3r/GUI/GUI.hpp" #include "slic3r/GUI/GUI_ObjectSettings.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" +#include "slic3r/GUI/Plater.hpp" #include "slic3r/GUI/PresetBundle.hpp" +#include "libslic3r/SLAPrint.hpp" #include "libslic3r/Tesselate.hpp" @@ -306,7 +308,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) } else { render_color[3] = 1.f; - if ((m_hover_id == i && m_editing_mode)) { // ignore hover state unless editing mode is active + if ((size_t(m_hover_id) == i && m_editing_mode)) { // ignore hover state unless editing mode is active render_color[0] = 0.f; render_color[1] = 1.0f; render_color[2] = 1.0f; @@ -328,7 +330,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. glsafe(::glPushMatrix()); - glsafe(::glTranslated(support_point.pos(0), support_point.pos(1), support_point.pos(2))); + glsafe(::glTranslatef(support_point.pos(0), support_point.pos(1), support_point.pos(2))); glsafe(::glMultMatrixd(instance_scaling_matrix_inverse.data())); if (vol->is_left_handed()) @@ -345,16 +347,16 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) Eigen::AngleAxisd aa(q); glsafe(::glRotated(aa.angle() * (180. / M_PI), aa.axis()(0), aa.axis()(1), aa.axis()(2))); - const float cone_radius = 0.25f; // mm - const float cone_height = 0.75f; + const double cone_radius = 0.25; // mm + const double cone_height = 0.75; glsafe(::glPushMatrix()); glsafe(::glTranslatef(0.f, 0.f, support_point.head_front_radius * RenderPointScale)); - ::gluCylinder(m_quadric, 0.f, cone_radius, cone_height, 24, 1); + ::gluCylinder(m_quadric, 0., cone_radius, cone_height, 24, 1); glsafe(::glTranslatef(0.f, 0.f, cone_height)); ::gluDisk(m_quadric, 0.0, cone_radius, 24, 1); glsafe(::glPopMatrix()); } - ::gluSphere(m_quadric, support_point.head_front_radius * RenderPointScale, 24, 12); + ::gluSphere(m_quadric, (double)support_point.head_front_radius * RenderPointScale, 24, 12); if (vol->is_left_handed()) glFrontFace(GL_CCW); @@ -775,7 +777,7 @@ std::vector<const ConfigOption*> GLGizmoSlaSupports::get_config_options(const st } -void GLGizmoSlaSupports::update_cache_entry_normal(unsigned int i) const +void GLGizmoSlaSupports::update_cache_entry_normal(size_t i) const { int idx = 0; Eigen::Matrix<float, 1, 3> pp = m_editing_cache[i].support_point.pos; @@ -1098,9 +1100,6 @@ std::string GLGizmoSlaSupports::on_get_name() const void GLGizmoSlaSupports::on_set_state() { - if (m_state == Hover) - return; - // m_model_object pointer can be invalid (for instance because of undo/redo action), // we should recover it from the object id m_model_object = nullptr; @@ -1111,6 +1110,9 @@ void GLGizmoSlaSupports::on_set_state() } } + if (m_state == m_old_state) + return; + if (m_state == On && m_old_state != On) { // the gizmo was just turned on Plater::TakeSnapshot snapshot(wxGetApp().plater(), _(L("SLA gizmo turned on"))); if (is_mesh_update_necessary()) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index feb2455cb..d2451f64e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -11,7 +11,6 @@ #include "slic3r/GUI/I18N.hpp" // ...and redefine again when we are done with the igl code #include "libslic3r/SLA/SLACommon.hpp" -#include "libslic3r/SLAPrint.hpp" #include <wx/dialog.h> #include <cereal/types/vector.hpp> @@ -31,7 +30,7 @@ private: ObjectID m_model_object_id = 0; int m_active_instance = -1; float m_active_instance_bb_radius; // to cache the bb - mutable float m_z_shift = 0.f; + mutable double m_z_shift = 0.f; bool unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec3f, Vec3f>& pos_and_normal); const float RenderPointScale = 1.f; @@ -78,7 +77,7 @@ private: public: GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - virtual ~GLGizmoSlaSupports(); + ~GLGizmoSlaSupports() override; void set_sla_support_data(ModelObject* model_object, const Selection& selection); bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); void delete_selected_points(bool force = false); @@ -90,21 +89,19 @@ public: void reslice_SLA_supports(bool postpone_error_messages = false) const; private: - bool on_init(); - void on_update(const UpdateData& data); - virtual void on_render() const; - virtual void on_render_for_picking() const; + bool on_init() override; + void on_update(const UpdateData& data) override; + void on_render() const override; + void on_render_for_picking() const override; //void render_selection_rectangle() const; void render_points(const Selection& selection, bool picking = false) const; void render_clipping_plane(const Selection& selection) const; bool is_mesh_update_necessary() const; void update_mesh(); - void update_cache_entry_normal(unsigned int i) const; + void update_cache_entry_normal(size_t i) const; bool unsaved_changes() const; - EState m_no_hover_state = Off; - EState m_no_hover_old_state = Off; bool m_lock_unique_islands = false; bool m_editing_mode = false; // Is editing mode active? bool m_old_editing_state = false; // To keep track of whether the user toggled between the modes (needed for imgui refreshes). @@ -157,20 +154,21 @@ private: protected: void on_set_state() override; - virtual void on_set_hover_id() + void on_set_hover_id() override + { if (! m_editing_mode || (int)m_editing_cache.size() <= m_hover_id) m_hover_id = -1; } void on_start_dragging() override; void on_stop_dragging() override; - virtual void on_render_input_window(float x, float y, float bottom_limit) override; + void on_render_input_window(float x, float y, float bottom_limit) override; - virtual std::string on_get_name() const; - virtual bool on_is_activable() const; - virtual bool on_is_selectable() const; - virtual void on_load(cereal::BinaryInputArchive& ar) override; - virtual void on_save(cereal::BinaryOutputArchive& ar) const override; + std::string on_get_name() const override; + bool on_is_activable() const override; + bool on_is_selectable() const override; + void on_load(cereal::BinaryInputArchive& ar) override; + void on_save(cereal::BinaryOutputArchive& ar) const override; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 3bad1b0a3..16fa30fa7 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -29,9 +29,49 @@ GLGizmosManager::GLGizmosManager(GLCanvas3D& parent) { } -GLGizmosManager::~GLGizmosManager() +std::vector<size_t> GLGizmosManager::get_selectable_idxs() const { - reset(); + std::vector<size_t> out; + for (size_t i=0; i<m_gizmos.size(); ++i) + if (m_gizmos[i]->is_selectable()) + out.push_back(i); + return out; +} + +std::vector<size_t> GLGizmosManager::get_activable_idxs() const +{ + std::vector<size_t> out; + for (size_t i=0; i<m_gizmos.size(); ++i) + if (m_gizmos[i]->is_activable()) + out.push_back(i); + return out; +} + +size_t GLGizmosManager::get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const +{ + if (! m_enabled) + return Undefined; + + float cnv_h = (float)m_parent.get_canvas_size().get_height(); + float height = get_total_overlay_height(); + float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; + float scaled_border = m_overlay_border * m_overlay_scale; + float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; + float scaled_stride_y = scaled_icons_size + scaled_gap_y; + float top_y = 0.5f * (cnv_h - height) + scaled_border; + + // is mouse horizontally in the area? + if ((scaled_border <= (float)mouse_pos(0) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size))) { + // which icon is it on? + size_t from_top = (size_t)((float)mouse_pos(1) - top_y)/scaled_stride_y; + // is it really on the icon or already past the border? + if ((float)mouse_pos(1) <= top_y + from_top*scaled_stride_y + scaled_icons_size) { + std::vector<size_t> selectable = get_selectable_idxs(); + if (from_top < selectable.size()) + return selectable[from_top]; + } + } + return Undefined; } bool GLGizmosManager::init() @@ -45,77 +85,25 @@ bool GLGizmosManager::init() if (!m_background_texture.metadata.filename.empty()) { if (!m_background_texture.texture.load_from_file(resources_dir() + "/icons/" + m_background_texture.metadata.filename, false, GLTexture::SingleThreaded, false)) - { - reset(); return false; - } } - GLGizmoBase* gizmo = new GLGizmoMove3D(m_parent, "move.svg", 0); - if (gizmo == nullptr) - return false; - - if (!gizmo->init()) - return false; - - m_gizmos.insert(GizmosMap::value_type(Move, gizmo)); - - gizmo = new GLGizmoScale3D(m_parent, "scale.svg", 1); - if (gizmo == nullptr) - return false; - - if (!gizmo->init()) - return false; - - m_gizmos.insert(GizmosMap::value_type(Scale, gizmo)); - - gizmo = new GLGizmoRotate3D(m_parent, "rotate.svg", 2); - if (gizmo == nullptr) - { - reset(); - return false; - } - - if (!gizmo->init()) - { - reset(); - return false; - } - - m_gizmos.insert(GizmosMap::value_type(Rotate, gizmo)); - - gizmo = new GLGizmoFlatten(m_parent, "place.svg", 3); - if (gizmo == nullptr) - return false; - - if (!gizmo->init()) { - reset(); - return false; - } - - m_gizmos.insert(GizmosMap::value_type(Flatten, gizmo)); - - gizmo = new GLGizmoCut(m_parent, "cut.svg", 4); - if (gizmo == nullptr) - return false; - - if (!gizmo->init()) { - reset(); - return false; - } - - m_gizmos.insert(GizmosMap::value_type(Cut, gizmo)); - - gizmo = new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 5); - if (gizmo == nullptr) - return false; + m_gizmos.emplace_back(new GLGizmoMove3D(m_parent, "move.svg", 0)); + m_gizmos.emplace_back(new GLGizmoScale3D(m_parent, "scale.svg", 1)); + m_gizmos.emplace_back(new GLGizmoRotate3D(m_parent, "rotate.svg", 2)); + m_gizmos.emplace_back(new GLGizmoFlatten(m_parent, "place.svg", 3)); + m_gizmos.emplace_back(new GLGizmoCut(m_parent, "cut.svg", 4)); + m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", 5)); - if (!gizmo->init()) { - reset(); - return false; + for (auto& gizmo : m_gizmos) { + if (! gizmo->init()) { + m_gizmos.clear(); + return false; + } } - m_gizmos.insert(GizmosMap::value_type(SlaSupports, gizmo)); + m_current = Undefined; + m_hover = Undefined; return true; } @@ -140,65 +128,39 @@ void GLGizmosManager::set_overlay_scale(float scale) void GLGizmosManager::refresh_on_off_state() { - if (m_serializing) + if (m_serializing || m_current == Undefined || m_gizmos.empty()) return; - GizmosMap::iterator it = m_gizmos.find(m_current); - if ((it != m_gizmos.end()) && (it->second != nullptr)) - { - if (!it->second->is_activable()) - { - it->second->set_state(GLGizmoBase::Off); - m_current = Undefined; - } - } + if (m_current != Undefined && ! m_gizmos[m_current]->is_activable()) + activate_gizmo(Undefined); } void GLGizmosManager::reset_all_states() { - if (!m_enabled) - return; - - if (m_serializing) + if (! m_enabled || m_serializing) return; - for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) - { - if (it->second != nullptr) - { - it->second->set_state(GLGizmoBase::Off); - it->second->set_hover_id(-1); - } - } - - m_current = Undefined; + activate_gizmo(Undefined); + m_hover = Undefined; } void GLGizmosManager::set_hover_id(int id) { - if (!m_enabled) + if (!m_enabled || m_current == Undefined) return; - for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) - { - if ((it->second != nullptr) && (it->second->get_state() == GLGizmoBase::On)) - it->second->set_hover_id(id); - } + m_gizmos[m_current]->set_hover_id(id); } void GLGizmosManager::enable_grabber(EType type, unsigned int id, bool enable) { - if (!m_enabled) + if (!m_enabled || type == Undefined || m_gizmos.empty()) return; - GizmosMap::const_iterator it = m_gizmos.find(type); - if (it != m_gizmos.end()) - { - if (enable) - it->second->enable_grabber(id); - else - it->second->disable_grabber(id); - } + if (enable) + m_gizmos[type]->enable_grabber(id); + else + m_gizmos[type]->disable_grabber(id); } void GLGizmosManager::update(const Linef3& mouse_ray, const Point& mouse_pos) @@ -269,8 +231,9 @@ bool GLGizmosManager::is_running() const if (!m_enabled) return false; - GLGizmoBase* curr = get_current(); - return (curr != nullptr) ? (curr->get_state() == GLGizmoBase::On) : false; + //GLGizmoBase* curr = get_current(); + //return (curr != nullptr) ? (curr->get_state() == GLGizmoBase::On) : false; + return m_current != Undefined; } bool GLGizmosManager::handle_shortcut(int key) @@ -283,43 +246,12 @@ bool GLGizmosManager::handle_shortcut(int key) bool handled = false; - for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) - { - if ((it->second == nullptr) || !it->second->is_selectable()) - continue; + for (size_t idx : get_selectable_idxs()) { + int it_key = m_gizmos[idx]->get_shortcut_key(); - int it_key = it->second->get_shortcut_key(); - - if (it->second->is_activable() && ((it_key == key - 64) || (it_key == key - 96))) - { - if ((it->second->get_state() == GLGizmoBase::On)) - { - it->second->set_state(GLGizmoBase::Off); - if (it->second->get_state() == GLGizmoBase::Off) { - m_current = Undefined; - } + if (m_gizmos[idx]->is_activable() && ((it_key == key - 64) || (it_key == key - 96))) { + activate_gizmo(m_current == idx ? Undefined : (EType)idx); handled = true; - } - else if ((it->second->get_state() == GLGizmoBase::Off)) - { - // Before turning anything on, turn everything else off to see if - // nobody refuses. Only then activate the gizmo. - bool can_open = true; - for (GizmosMap::iterator i = m_gizmos.begin(); i != m_gizmos.end(); ++i) { - if (i->first != it->first) { - if (m_current == i->first && i->second->get_state() != GLGizmoBase::Off ) { - i->second->set_state(GLGizmoBase::Off); - if (i->second->get_state() != GLGizmoBase::Off) - can_open = false; - } - } - } - if (can_open) { - it->second->set_state(GLGizmoBase::On); - m_current = it->first; - } - handled = true; - } } } @@ -328,31 +260,25 @@ bool GLGizmosManager::handle_shortcut(int key) bool GLGizmosManager::is_dragging() const { - if (!m_enabled) + if (! m_enabled || m_current == Undefined) return false; - GLGizmoBase* curr = get_current(); - return (curr != nullptr) ? curr->is_dragging() : false; + return m_gizmos[m_current]->is_dragging(); } void GLGizmosManager::start_dragging() { - if (!m_enabled) + if (! m_enabled || m_current == Undefined) return; - - GLGizmoBase* curr = get_current(); - if (curr != nullptr) - curr->start_dragging(); + m_gizmos[m_current]->start_dragging(); } void GLGizmosManager::stop_dragging() { - if (!m_enabled) + if (! m_enabled || m_current == Undefined) return; - GLGizmoBase* curr = get_current(); - if (curr != nullptr) - curr->stop_dragging(); + m_gizmos[m_current]->stop_dragging(); } Vec3d GLGizmosManager::get_displacement() const @@ -360,8 +286,7 @@ Vec3d GLGizmosManager::get_displacement() const if (!m_enabled) return Vec3d::Zero(); - GizmosMap::const_iterator it = m_gizmos.find(Move); - return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoMove3D*>(it->second)->get_displacement() : Vec3d::Zero(); + return dynamic_cast<GLGizmoMove3D*>(m_gizmos[Move].get())->get_displacement(); } Vec3d GLGizmosManager::get_scale() const @@ -369,126 +294,101 @@ Vec3d GLGizmosManager::get_scale() const if (!m_enabled) return Vec3d::Ones(); - GizmosMap::const_iterator it = m_gizmos.find(Scale); - return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoScale3D*>(it->second)->get_scale() : Vec3d::Ones(); + return dynamic_cast<GLGizmoScale3D*>(m_gizmos[Scale].get())->get_scale(); } void GLGizmosManager::set_scale(const Vec3d& scale) { - if (!m_enabled) + if (!m_enabled || m_gizmos.empty()) return; - GizmosMap::const_iterator it = m_gizmos.find(Scale); - if (it != m_gizmos.end()) - reinterpret_cast<GLGizmoScale3D*>(it->second)->set_scale(scale); + dynamic_cast<GLGizmoScale3D*>(m_gizmos[Scale].get())->set_scale(scale); } Vec3d GLGizmosManager::get_scale_offset() const { - if (!m_enabled) + if (!m_enabled || m_gizmos.empty()) return Vec3d::Zero(); - GizmosMap::const_iterator it = m_gizmos.find(Scale); - return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoScale3D*>(it->second)->get_offset() : Vec3d::Zero(); + return dynamic_cast<GLGizmoScale3D*>(m_gizmos[Scale].get())->get_offset(); } Vec3d GLGizmosManager::get_rotation() const { - if (!m_enabled) + if (!m_enabled || m_gizmos.empty()) return Vec3d::Zero(); - GizmosMap::const_iterator it = m_gizmos.find(Rotate); - return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoRotate3D*>(it->second)->get_rotation() : Vec3d::Zero(); + return dynamic_cast<GLGizmoRotate3D*>(m_gizmos[Rotate].get())->get_rotation(); } void GLGizmosManager::set_rotation(const Vec3d& rotation) { - if (!m_enabled) + if (!m_enabled || m_gizmos.empty()) return; - - GizmosMap::const_iterator it = m_gizmos.find(Rotate); - if (it != m_gizmos.end()) - reinterpret_cast<GLGizmoRotate3D*>(it->second)->set_rotation(rotation); + dynamic_cast<GLGizmoRotate3D*>(m_gizmos[Rotate].get())->set_rotation(rotation); } Vec3d GLGizmosManager::get_flattening_normal() const { - if (!m_enabled) + if (!m_enabled || m_gizmos.empty()) return Vec3d::Zero(); - GizmosMap::const_iterator it = m_gizmos.find(Flatten); - return (it != m_gizmos.end()) ? reinterpret_cast<GLGizmoFlatten*>(it->second)->get_flattening_normal() : Vec3d::Zero(); + return dynamic_cast<GLGizmoFlatten*>(m_gizmos[Flatten].get())->get_flattening_normal(); } void GLGizmosManager::set_flattening_data(const ModelObject* model_object) { - if (!m_enabled) + if (!m_enabled || m_gizmos.empty()) return; - GizmosMap::const_iterator it = m_gizmos.find(Flatten); - if (it != m_gizmos.end()) - reinterpret_cast<GLGizmoFlatten*>(it->second)->set_flattening_data(model_object); + dynamic_cast<GLGizmoFlatten*>(m_gizmos[Flatten].get())->set_flattening_data(model_object); } void GLGizmosManager::set_sla_support_data(ModelObject* model_object) { - if (!m_enabled) + if (!m_enabled || m_gizmos.empty()) return; - GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); - if (it != m_gizmos.end()) - reinterpret_cast<GLGizmoSlaSupports*>(it->second)->set_sla_support_data(model_object, m_parent.get_selection()); + dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get())->set_sla_support_data(model_object, m_parent.get_selection()); } // Returns true if the gizmo used the event to do something, false otherwise. bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) { - if (!m_enabled) + if (!m_enabled || m_gizmos.empty()) return false; - GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); - if (it != m_gizmos.end()) - return reinterpret_cast<GLGizmoSlaSupports*>(it->second)->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); - - return false; + return dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); } ClippingPlane GLGizmosManager::get_sla_clipping_plane() const { - if (!m_enabled || m_current != SlaSupports) + if (!m_enabled || m_current != SlaSupports || m_gizmos.empty()) return ClippingPlane::ClipsNothing(); - GizmosMap::const_iterator it = m_gizmos.find(SlaSupports); - if (it != m_gizmos.end()) - return reinterpret_cast<GLGizmoSlaSupports*>(it->second)->get_sla_clipping_plane(); - - return ClippingPlane::ClipsNothing(); + return dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get())->get_sla_clipping_plane(); } bool GLGizmosManager::wants_reslice_supports_on_undo() const { return (m_current == SlaSupports - && dynamic_cast<const GLGizmoSlaSupports*>(m_gizmos.at(SlaSupports))->has_backend_supports()); + && dynamic_cast<const GLGizmoSlaSupports*>(m_gizmos.at(SlaSupports).get())->has_backend_supports()); } void GLGizmosManager::render_current_gizmo() const { - if (!m_enabled) + if (!m_enabled || m_current == Undefined) return; - GLGizmoBase* curr = get_current(); - if (curr != nullptr) - curr->render(); + m_gizmos[m_current]->render(); } void GLGizmosManager::render_current_gizmo_for_picking_pass() const { - if (!m_enabled) + if (! m_enabled || m_current == Undefined) return; - GLGizmoBase* curr = get_current(); - if (curr != nullptr) - curr->render_for_picking(); + m_gizmos[m_current]->render_for_picking(); } void GLGizmosManager::render_overlay() const @@ -547,7 +447,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) // if the button down was done on this toolbar, prevent from dragging into the scene processed = true; - if (!overlay_contains_mouse(mouse_pos)) + if (get_gizmo_idx_from_mouse(mouse_pos) == Undefined) { // mouse is outside the toolbar m_tooltip = ""; @@ -557,14 +457,12 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown())) // the gizmo got the event and took some action, there is no need to do anything more processed = true; - else if (!selection.is_empty() && grabber_contains_mouse()) - { + else if (!selection.is_empty() && grabber_contains_mouse()) { update_data(); selection.start_dragging(); start_dragging(); - if (m_current == Flatten) - { + if (m_current == Flatten) { // Rotate the object so the normal points downward: m_parent.do_flatten(get_flattening_normal(), L("Gizmo-Place on Face")); wxGetApp().obj_manipul()->set_dirty(); @@ -634,25 +532,14 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) } else if (evt.LeftUp() && is_dragging()) { - switch (m_current) - { - case Move: - { - m_parent.do_move(L("Gizmo-Move")); - break; - } - case Scale: - { - m_parent.do_scale(L("Gizmo-Scale")); - break; - } - case Rotate: - { - m_parent.do_rotate(L("Gizmo-Rotate")); - break; - } - default: - break; + switch (m_current) { + case Move : m_parent.do_move(L("Gizmo-Move")); + break; + case Scale : m_parent.do_scale(L("Gizmo-Scale")); + break; + case Rotate : m_parent.do_rotate(L("Gizmo-Rotate")); + break; + default : break; } stop_dragging(); @@ -842,7 +729,7 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) { if (m_current == SlaSupports) { - GLGizmoSlaSupports* gizmo = reinterpret_cast<GLGizmoSlaSupports*>(get_current()); + GLGizmoSlaSupports* gizmo = dynamic_cast<GLGizmoSlaSupports*>(get_current()); if (keyCode == WXK_SHIFT) { @@ -863,7 +750,8 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) } else if (evt.GetEventType() == wxEVT_KEY_DOWN) { - if ((m_current == SlaSupports) && ((keyCode == WXK_SHIFT) || (keyCode == WXK_ALT)) && reinterpret_cast<GLGizmoSlaSupports*>(get_current())->is_in_editing_mode()) + if ((m_current == SlaSupports) && ((keyCode == WXK_SHIFT) || (keyCode == WXK_ALT)) + && dynamic_cast<GLGizmoSlaSupports*>(get_current())->is_in_editing_mode()) { // m_parent.set_cursor(GLCanvas3D::Cross); processed = true; @@ -882,18 +770,7 @@ void GLGizmosManager::update_after_undo_redo(const UndoRedo::Snapshot& snapshot) m_serializing = false; if (m_current == SlaSupports && snapshot.snapshot_data.flags & UndoRedo::SnapshotData::RECALCULATE_SLA_SUPPORTS) - dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports])->reslice_SLA_supports(true); -} - -void GLGizmosManager::reset() -{ - for (GizmosMap::value_type& gizmo : m_gizmos) - { - delete gizmo.second; - gizmo.second = nullptr; - } - - m_gizmos.clear(); + dynamic_cast<GLGizmoSlaSupports*>(m_gizmos[SlaSupports].get())->reslice_SLA_supports(true); } void GLGizmosManager::render_background(float left, float top, float right, float bottom, float border) const @@ -911,7 +788,7 @@ void GLGizmosManager::render_background(float left, float top, float right, floa float internal_top = top - border; float internal_bottom = bottom + border; - float left_uv = 0.0f; + // float left_uv = 0.0f; float right_uv = 1.0f; float top_uv = 1.0f; float bottom_uv = 0.0f; @@ -981,33 +858,32 @@ void GLGizmosManager::do_render_overlay() const float scaled_icons_size = m_overlay_icons_size * m_overlay_scale * inv_zoom; float scaled_stride_y = scaled_icons_size + scaled_gap_y; unsigned int icons_texture_id = m_icons_texture.get_id(); - unsigned int tex_width = m_icons_texture.get_width(); - unsigned int tex_height = m_icons_texture.get_height(); - float inv_tex_width = (tex_width != 0) ? 1.0f / (float)tex_width : 0.0f; - float inv_tex_height = (tex_height != 0) ? 1.0f / (float)tex_height : 0.0f; + int tex_width = m_icons_texture.get_width(); + int tex_height = m_icons_texture.get_height(); + float inv_tex_width = (tex_width != 0) ? 1.0f / tex_width : 0.0f; + float inv_tex_height = (tex_height != 0) ? 1.0f / tex_height : 0.0f; if ((icons_texture_id == 0) || (tex_width <= 0) || (tex_height <= 0)) return; - for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) + for (size_t idx : get_selectable_idxs()) { - if ((it->second == nullptr) || !it->second->is_selectable()) - continue; + GLGizmoBase* gizmo = m_gizmos[idx].get(); - unsigned int sprite_id = it->second->get_sprite_id(); - GLGizmoBase::EState state = it->second->get_state(); + unsigned int sprite_id = gizmo->get_sprite_id(); + int icon_idx = m_current == idx ? 2 : (m_hover == idx ? 1 : 0); float u_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_width; float v_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_height; float v_top = sprite_id * v_icon_size; - float u_left = state * u_icon_size; + float u_left = icon_idx * u_icon_size; float v_bottom = v_top + v_icon_size; float u_right = u_left + u_icon_size; GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + scaled_icons_size, top_y - scaled_icons_size, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } }); - if (it->second->get_state() == GLGizmoBase::On) { - float toolbar_top = (float)cnv_h - m_parent.get_view_toolbar_height(); - it->second->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top); + if (idx == m_current) { + float toolbar_top = cnv_h - m_parent.get_view_toolbar_height(); + gizmo->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top); } top_y -= scaled_stride_y; } @@ -1021,13 +897,14 @@ float GLGizmosManager::get_total_overlay_height() const float scaled_stride_y = scaled_icons_size + scaled_gap_y; float height = 2.0f * scaled_border; - for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) + /*for (size_t idx=0; idx<m_gizmos.size(); ++idx) { - if ((it->second == nullptr) || !it->second->is_selectable()) + if ((m_gizmos[idx] == nullptr) || !m_gizmos[idx]->is_selectable()) continue; height += scaled_stride_y; - } + }*/ + height += get_selectable_idxs().size() * scaled_stride_y; return height - scaled_gap_y; } @@ -1039,19 +916,21 @@ float GLGizmosManager::get_total_overlay_width() const GLGizmoBase* GLGizmosManager::get_current() const { - GizmosMap::const_iterator it = m_gizmos.find(m_current); - return (it != m_gizmos.end()) ? it->second : nullptr; + if (m_current==Undefined || m_gizmos.empty()) + return nullptr; + else + return m_gizmos[m_current].get(); } bool GLGizmosManager::generate_icons_texture() const { std::string path = resources_dir() + "/icons/"; std::vector<std::string> filenames; - for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) + for (size_t idx=0; idx<m_gizmos.size(); ++idx) { - if (it->second != nullptr) + if (m_gizmos[idx] != nullptr) { - const std::string& icon_filename = it->second->get_icon_filename(); + const std::string& icon_filename = m_gizmos[idx]->get_icon_filename(); if (!icon_filename.empty()) filenames.push_back(path + icon_filename); } @@ -1074,74 +953,9 @@ void GLGizmosManager::update_on_off_state(const Vec2d& mouse_pos) if (!m_enabled) return; - float cnv_h = (float)m_parent.get_canvas_size().get_height(); - float height = get_total_overlay_height(); - - float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; - float scaled_border = m_overlay_border * m_overlay_scale; - float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; - float scaled_stride_y = scaled_icons_size + scaled_gap_y; - float top_y = 0.5f * (cnv_h - height) + scaled_border; - - auto inside = [scaled_border, scaled_icons_size, &mouse_pos](float top_y) { - return (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); - }; - - bool could_activate = true; - for (std::pair<const GLGizmosManager::EType, GLGizmoBase*> &type_and_gizmo : m_gizmos) - { - GLGizmoBase *gizmo = type_and_gizmo.second; - if ((gizmo == nullptr) || !gizmo->is_selectable()) - continue; - - if (! (gizmo->is_activable() && inside(top_y))) { - gizmo->set_state(GLGizmoBase::Off); - if (gizmo->get_state() != GLGizmoBase::Off) { - // Gizmo refused to leave it's active state. Don't try to select - could_activate = false; - } - } - - top_y += scaled_stride_y; - } - - // We may change m_current soon. If we did it during following loop, gizmos that take undo/redo snapshots - // in their on_set_state function could snapshot a state with the new gizmo already active. - // Therefore, just remember what needs to be done and actually change m_current afterwards. - EType new_current = m_current; - top_y = 0.5f * (cnv_h - height) + scaled_border; - for (std::pair<const GLGizmosManager::EType, GLGizmoBase*> &type_and_gizmo : m_gizmos) - { - GLGizmoBase *gizmo = type_and_gizmo.second; - if ((gizmo == nullptr) || !gizmo->is_selectable()) - continue; - - if (gizmo->is_activable() && inside(top_y)) - { - if ((gizmo->get_state() == GLGizmoBase::On)) - { - gizmo->set_state(GLGizmoBase::Off); - if (gizmo->get_state() == GLGizmoBase::Off) { - gizmo->set_state(GLGizmoBase::Hover); - new_current = Undefined; - } - } - else if ((gizmo->get_state() == GLGizmoBase::Hover) && could_activate) - { - gizmo->set_state(GLGizmoBase::On); - new_current = type_and_gizmo.first; - } - } - - top_y += scaled_stride_y; - } - m_current = new_current; - - if (could_activate) { - GizmosMap::iterator it = m_gizmos.find(m_current); - if ((it != m_gizmos.end()) && (it->second != nullptr) && (it->second->get_state() != GLGizmoBase::On)) - it->second->set_state(GLGizmoBase::On); - } + size_t idx = get_gizmo_idx_from_mouse(mouse_pos); + if (idx != Undefined && m_gizmos[idx]->is_activable() && m_hover == idx) + activate_gizmo(m_current == idx ? Undefined : (EType)idx); } std::string GLGizmosManager::update_hover_state(const Vec2d& mouse_pos) @@ -1151,60 +965,37 @@ std::string GLGizmosManager::update_hover_state(const Vec2d& mouse_pos) if (!m_enabled) return name; - float cnv_h = (float)m_parent.get_canvas_size().get_height(); - float height = get_total_overlay_height(); - float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; - float scaled_border = m_overlay_border * m_overlay_scale; - float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; - float scaled_stride_y = scaled_icons_size + scaled_gap_y; - float top_y = 0.5f * (cnv_h - height) + scaled_border; - - for (GizmosMap::iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) - { - if ((it->second == nullptr) || !it->second->is_selectable()) - continue; - - bool inside = (scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size); - if (inside) - name = it->second->get_name(); + m_hover = Undefined; - if (it->second->is_activable() && (it->second->get_state() != GLGizmoBase::On)) - it->second->set_state(inside ? GLGizmoBase::Hover : GLGizmoBase::Off); + size_t idx = get_gizmo_idx_from_mouse(mouse_pos); + if (idx != Undefined) { + name = m_gizmos[idx]->get_name(); - top_y += scaled_stride_y; + if (m_gizmos[idx]->is_activable()) + m_hover = (EType)idx; } return name; } -bool GLGizmosManager::overlay_contains_mouse(const Vec2d& mouse_pos) const +void GLGizmosManager::activate_gizmo(EType type) { - if (!m_enabled) - return false; - - float cnv_h = (float)m_parent.get_canvas_size().get_height(); - float height = get_total_overlay_height(); - - float scaled_icons_size = m_overlay_icons_size * m_overlay_scale; - float scaled_border = m_overlay_border * m_overlay_scale; - float scaled_gap_y = m_overlay_gap_y * m_overlay_scale; - float scaled_stride_y = scaled_icons_size + scaled_gap_y; - float top_y = 0.5f * (cnv_h - height) + scaled_border; - - for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) - { - if ((it->second == nullptr) || !it->second->is_selectable()) - continue; - - if ((scaled_border <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size) && (top_y <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= top_y + scaled_icons_size)) - return true; + if (m_gizmos.empty() || m_current == type) + return; - top_y += scaled_stride_y; + if (m_current != Undefined) { + m_gizmos[m_current]->set_state(GLGizmoBase::Off); + if (m_gizmos[m_current]->get_state() != GLGizmoBase::Off) + return; // gizmo refused to be turned off, do nothing. } - return false; + if (type != Undefined) + m_gizmos[type]->set_state(GLGizmoBase::On); + + m_current = type; } + bool GLGizmosManager::grabber_contains_mouse() const { if (!m_enabled) diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index c0adeb957..f649c98b2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -54,25 +54,30 @@ public: enum EType : unsigned char { - Undefined, Move, Scale, Rotate, Flatten, Cut, SlaSupports, - Num_Types + Undefined }; private: GLCanvas3D& m_parent; bool m_enabled; - typedef std::map<EType, GLGizmoBase*> GizmosMap; - GizmosMap m_gizmos; + std::vector<std::unique_ptr<GLGizmoBase>> m_gizmos; mutable GLTexture m_icons_texture; mutable bool m_icons_texture_dirty; BackgroundTexture m_background_texture; EType m_current; + EType m_hover; + + std::vector<size_t> get_selectable_idxs() const; + std::vector<size_t> get_activable_idxs() const; + size_t get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const; + + void activate_gizmo(EType type); float m_overlay_icons_size; float m_overlay_scale; @@ -98,7 +103,6 @@ private: public: explicit GLGizmosManager(GLCanvas3D& parent); - ~GLGizmosManager(); bool init(); @@ -112,16 +116,8 @@ public: ar(m_current); - GLGizmoBase* curr = get_current(); - for (GizmosMap::const_iterator it = m_gizmos.begin(); it != m_gizmos.end(); ++it) { - GLGizmoBase* gizmo = it->second; - if (gizmo != nullptr) { - gizmo->set_hover_id(-1); - gizmo->set_state((it->second == curr) ? GLGizmoBase::On : GLGizmoBase::Off); - if (gizmo == curr) - gizmo->load(ar); - } - } + if (m_current != Undefined) + m_gizmos[m_current]->load(ar); } template<class Archive> @@ -132,9 +128,8 @@ public: ar(m_current); - GLGizmoBase* curr = get_current(); - if (curr != nullptr) - curr->save(ar); + if (m_current != Undefined && !m_gizmos.empty()) + m_gizmos[m_current]->save(ar); } bool is_enabled() const { return m_enabled; } @@ -195,8 +190,6 @@ public: void update_after_undo_redo(const UndoRedo::Snapshot& snapshot); private: - void reset(); - void render_background(float left, float top, float right, float bottom, float border) const; void do_render_overlay() const; @@ -209,7 +202,6 @@ private: void update_on_off_state(const Vec2d& mouse_pos); std::string update_hover_state(const Vec2d& mouse_pos); - bool overlay_contains_mouse(const Vec2d& mouse_pos) const; bool grabber_contains_mouse() const; }; diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index aa1e3d500..28bd0242b 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -50,8 +50,9 @@ class MainFrame : public DPIFrame wxString m_qs_last_input_file = wxEmptyString; wxString m_qs_last_output_file = wxEmptyString; wxString m_last_config = wxEmptyString; - - wxMenuItem* m_menu_item_repeat { nullptr }; +#if 0 + wxMenuItem* m_menu_item_repeat { nullptr }; // doesn't used now +#endif wxMenuItem* m_menu_item_reslice_now { nullptr }; PrintHostQueueDialog *m_printhost_queue_dlg; diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index c17569c11..c032aac72 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -457,8 +457,9 @@ void ConfigOptionsGroup::Show(const bool show) bool ConfigOptionsGroup::update_visibility(ConfigOptionMode mode) { if (m_options_mode.empty()) return true; - if (m_grid_sizer->GetEffectiveRowsCount() != m_options_mode.size() && - m_options_mode.size() == 1) + int opt_mode_size = m_options_mode.size(); + if (m_grid_sizer->GetEffectiveRowsCount() != opt_mode_size && + opt_mode_size == 1) return m_options_mode[0] <= mode; Show(true); @@ -476,7 +477,7 @@ bool ConfigOptionsGroup::update_visibility(ConfigOptionMode mode) { coef+= cols; } - if (hidden_row_cnt == m_options_mode.size()) { + if (hidden_row_cnt == opt_mode_size) { sizer->ShowItems(false); return false; } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ef6a7d3c5..3075d11ed 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -510,24 +510,27 @@ FreqChangedParams::FreqChangedParams(wxWindow* parent) : option.opt.sidetext = ""; line.append_option(option); - auto wiping_dialog_btn = [config, this](wxWindow* parent) { + auto wiping_dialog_btn = [this](wxWindow* parent) { m_wiping_dialog_button = new wxButton(parent, wxID_ANY, _(L("Purging volumes")) + dots, wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT); m_wiping_dialog_button->SetFont(wxGetApp().normal_font()); auto sizer = new wxBoxSizer(wxHORIZONTAL); sizer->Add(m_wiping_dialog_button, 0, wxALIGN_CENTER_VERTICAL); m_wiping_dialog_button->Bind(wxEVT_BUTTON, ([parent](wxCommandEvent& e) { - auto &config = wxGetApp().preset_bundle->project_config; - const std::vector<double> &init_matrix = (config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values; - const std::vector<double> &init_extruders = (config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values; + auto &project_config = wxGetApp().preset_bundle->project_config; + const std::vector<double> &init_matrix = (project_config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values; + const std::vector<double> &init_extruders = (project_config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values; - WipingDialog dlg(parent, cast<float>(init_matrix), cast<float>(init_extruders)); + const DynamicPrintConfig* config = &wxGetApp().preset_bundle->printers.get_edited_preset().config; + const std::vector<std::string> &extruder_colours = (config->option<ConfigOptionStrings>("extruder_colour"))->values; + + WipingDialog dlg(parent, cast<float>(init_matrix), cast<float>(init_extruders), extruder_colours); if (dlg.ShowModal() == wxID_OK) { std::vector<float> matrix = dlg.get_matrix(); std::vector<float> extruders = dlg.get_extruders(); - (config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values = std::vector<double>(matrix.begin(), matrix.end()); - (config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values = std::vector<double>(extruders.begin(), extruders.end()); + (project_config.option<ConfigOptionFloats>("wiping_volumes_matrix"))->values = std::vector<double>(matrix.begin(), matrix.end()); + (project_config.option<ConfigOptionFloats>("wiping_volumes_extruders"))->values = std::vector<double>(extruders.begin(), extruders.end()); wxPostEvent(parent, SimpleEvent(EVT_SCHEDULE_BACKGROUND_PROCESS, parent)); } })); @@ -880,7 +883,7 @@ void Sidebar::init_filament_combo(PresetComboBox **combo, const int extr_idx) { sizer_filaments->Add(combo_and_btn_sizer, 1, wxEXPAND | wxBOTTOM, 1); } -void Sidebar::remove_unused_filament_combos(const int current_extruder_count) +void Sidebar::remove_unused_filament_combos(const size_t current_extruder_count) { if (current_extruder_count >= p->combos_filament.size()) return; @@ -923,9 +926,9 @@ void Sidebar::update_presets(Preset::Type preset_type) switch (preset_type) { case Preset::TYPE_FILAMENT: { - const int extruder_cnt = print_tech != ptFFF ? 1 : + const size_t extruder_cnt = print_tech != ptFFF ? 1 : dynamic_cast<ConfigOptionFloats*>(preset_bundle.printers.get_edited_preset().config.option("nozzle_diameter"))->values.size(); - const int filament_cnt = p->combos_filament.size() > extruder_cnt ? extruder_cnt : p->combos_filament.size(); + const size_t filament_cnt = p->combos_filament.size() > extruder_cnt ? extruder_cnt : p->combos_filament.size(); if (filament_cnt == 1) { // Single filament printer, synchronize the filament presets. @@ -1048,7 +1051,7 @@ wxButton* Sidebar::get_wiping_dialog_button() return p->frequently_changed_parameters->get_wiping_dialog_button(); } -void Sidebar::update_objects_list_extruder_column(int extruders_count) +void Sidebar::update_objects_list_extruder_column(size_t extruders_count) { p->object_list->update_objects_list_extruder_column(extruders_count); } @@ -1072,8 +1075,6 @@ void Sidebar::show_info_sizer() return; } - const ModelInstance* model_instance = !model_object->instances.empty() ? model_object->instances.front() : nullptr; - auto size = model_object->bounding_box().size(); p->object_info->info_size->SetLabel(wxString::Format("%.2f x %.2f x %.2f",size(0), size(1), size(2))); p->object_info->info_materials->SetLabel(wxString::Format("%d", static_cast<int>(model_object->materials_count()))); @@ -1936,6 +1937,7 @@ private: * */ std::string m_last_fff_printer_profile_name; std::string m_last_sla_printer_profile_name; + bool m_update_objects_list_on_loading{ true }; }; const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase); @@ -2285,20 +2287,20 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_ if (! is_project_file) { if (model.looks_like_multipart_object()) { - wxMessageDialog dlg(q, _(L( + wxMessageDialog msg_dlg(q, _(L( "This file contains several objects positioned at multiple heights. " "Instead of considering them as multiple objects, should I consider\n" "this file as a single object having multiple parts?\n" )), _(L("Multi-part object detected")), wxICON_WARNING | wxYES | wxNO); - if (dlg.ShowModal() == wxID_YES) { + if (msg_dlg.ShowModal() == wxID_YES) { model.convert_multipart_object(nozzle_dmrs->values.size()); } } } else if ((wxGetApp().get_mode() == comSimple) && (type_3mf || type_any_amf) && model_has_advanced_features(model)) { - wxMessageDialog dlg(q, _(L("This file cannot be loaded in a simple mode. Do you want to switch to an advanced mode?\n")), + wxMessageDialog msg_dlg(q, _(L("This file cannot be loaded in a simple mode. Do you want to switch to an advanced mode?\n")), _(L("Detected advanced data")), wxICON_WARNING | wxYES | wxNO); - if (dlg.ShowModal() == wxID_YES) + if (msg_dlg.ShowModal() == wxID_YES) { Slic3r::GUI::wxGetApp().save_mode(comAdvanced); view3D->set_as_dirty(); @@ -2337,12 +2339,12 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_ } if (new_model != nullptr && new_model->objects.size() > 1) { - wxMessageDialog dlg(q, _(L( + wxMessageDialog msg_dlg(q, _(L( "Multiple objects were loaded for a multi-material printer.\n" "Instead of considering them as multiple objects, should I consider\n" "these files to represent a single object having multiple parts?\n" )), _(L("Multi-part object detected")), wxICON_WARNING | wxYES | wxNO); - if (dlg.ShowModal() == wxID_YES) { + if (msg_dlg.ShowModal() == wxID_YES) { new_model->convert_multipart_object(nozzle_dmrs->values.size()); } @@ -2460,8 +2462,11 @@ std::vector<size_t> Plater::priv::load_model_objects(const ModelObjectPtrs &mode _(L("Object too large?"))); } - for (const size_t idx : obj_idxs) { - wxGetApp().obj_list()->add_object_to_list(idx); + if (m_update_objects_list_on_loading) + { + for (const size_t idx : obj_idxs) { + wxGetApp().obj_list()->add_object_to_list(idx); + } } update(); @@ -3084,25 +3089,49 @@ void Plater::priv::reload_from_disk() { Plater::TakeSnapshot snapshot(q, _(L("Reload from Disk"))); - const auto &selection = get_selection(); + auto& selection = get_selection(); const auto obj_orig_idx = selection.get_object_idx(); if (selection.is_wipe_tower() || obj_orig_idx == -1) { return; } + int instance_idx = selection.get_instance_idx(); auto *object_orig = model.objects[obj_orig_idx]; std::vector<fs::path> input_paths(1, object_orig->input_file); + // disable render to avoid to show intermediate states + view3D->get_canvas3d()->enable_render(false); + + // disable update of objects list while loading to avoid to show intermediate states + m_update_objects_list_on_loading = false; + const auto new_idxs = load_files(input_paths, true, false); + if (new_idxs.empty()) + { + // error while loading + view3D->get_canvas3d()->enable_render(true); + return; + } - for (const auto idx : new_idxs) { + for (const auto idx : new_idxs) + { ModelObject *object = model.objects[idx]; + object->config.apply(object_orig->config); object->clear_instances(); - for (const ModelInstance *instance : object_orig->instances) { + for (const ModelInstance *instance : object_orig->instances) + { object->add_instance(*instance); } - if (object->volumes.size() == object_orig->volumes.size()) { - for (size_t i = 0; i < object->volumes.size(); i++) { + for (const ModelVolume* v : object_orig->volumes) + { + if (v->is_modifier()) + object->add_volume(*v); + } + + if (object->volumes.size() == object_orig->volumes.size()) + { + for (size_t i = 0; i < object->volumes.size(); i++) + { object->volumes[i]->config.apply(object_orig->volumes[i]->config); } } @@ -3110,7 +3139,33 @@ void Plater::priv::reload_from_disk() // XXX: Restore more: layer_height_ranges, layer_height_profile (?) } + // re-enable update of objects list + m_update_objects_list_on_loading = true; + + // puts the new objects into the list + for (const auto idx : new_idxs) + { + wxGetApp().obj_list()->add_object_to_list(idx); + } + remove(obj_orig_idx); + + // new GLVolumes have been created at this point, so update their printable state + for (size_t i = 0; i < model.objects.size(); ++i) + { + view3D->get_canvas3d()->update_instance_printable_state_for_object(i); + } + + // re-enable render + view3D->get_canvas3d()->enable_render(true); + + // the previous call to remove() clears the selection + // select newly added objects + selection.clear(); + for (const auto idx : new_idxs) + { + selection.add_instance((unsigned int)idx - 1, instance_idx, false); + } } void Plater::priv::fix_through_netfabb(const int obj_idx, const int vol_idx/* = -1*/) @@ -3265,6 +3320,7 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt) else this->update_sla_scene(); break; + default: break; } } else if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_PREVIEW) { // Update the SLA preview. Only called if not RELOAD_SLA_SUPPORT_POINTS, as the block above will refresh the preview anyways. @@ -3284,6 +3340,7 @@ void Plater::priv::on_slicing_completed(wxCommandEvent &) else this->update_sla_scene(); break; + default: break; } } @@ -3330,6 +3387,7 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt) else this->update_sla_scene(); break; + default: break; } if (canceled) { @@ -4655,7 +4713,7 @@ void Plater::undo_redo_topmost_string_getter(const bool is_undo, std::string& ou out_text = ""; } -void Plater::on_extruders_change(int num_extruders) +void Plater::on_extruders_change(size_t num_extruders) { auto& choices = sidebar().combos_filament(); @@ -4664,7 +4722,7 @@ void Plater::on_extruders_change(int num_extruders) wxWindowUpdateLocker noUpdates_scrolled_panel(&sidebar()/*.scrolled_panel()*/); - int i = choices.size(); + size_t i = choices.size(); while ( i < num_extruders ) { PresetComboBox* choice/*{ nullptr }*/; @@ -4851,7 +4909,7 @@ void Plater::changed_objects(const std::vector<size_t>& object_idxs) if (object_idxs.empty()) return; - for (int obj_idx : object_idxs) + for (size_t obj_idx : object_idxs) { if (obj_idx < p->model.objects.size()) // recenter and re - align to Z = 0 diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 2d104bc46..6b488fef1 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -89,7 +89,7 @@ public: ~Sidebar(); void init_filament_combo(PresetComboBox **combo, const int extr_idx); - void remove_unused_filament_combos(const int current_extruder_count); + void remove_unused_filament_combos(const size_t current_extruder_count); void update_all_preset_comboboxes(); void update_presets(Slic3r::Preset::Type preset_type); void update_mode_sizer() const; @@ -105,7 +105,7 @@ public: ConfigOptionsGroup* og_freq_chng_params(const bool is_fff); wxButton* get_wiping_dialog_button(); - void update_objects_list_extruder_column(int extruders_count); + void update_objects_list_extruder_column(size_t extruders_count); void show_info_sizer(); void show_sliced_info_sizer(const bool show); void enable_buttons(bool enable); @@ -209,7 +209,7 @@ public: void enter_gizmos_stack(); void leave_gizmos_stack(); - void on_extruders_change(int extruders_count); + void on_extruders_change(size_t extruders_count); void on_config_change(const DynamicPrintConfig &config); // On activating the parent window. void on_activate(); diff --git a/src/slic3r/GUI/PresetBundle.cpp b/src/slic3r/GUI/PresetBundle.cpp index 79d49f13a..d9e90333c 100644 --- a/src/slic3r/GUI/PresetBundle.cpp +++ b/src/slic3r/GUI/PresetBundle.cpp @@ -366,7 +366,7 @@ void PresetBundle::load_selections(const AppConfig &config, const std::string &p this->filament_presets = { filaments.get_selected_preset_name() }; for (unsigned int i = 1; i < 1000; ++ i) { char name[64]; - sprintf(name, "filament_%d", i); + sprintf(name, "filament_%u", i); if (! config.has("presets", name)) break; this->filament_presets.emplace_back(remove_ini_suffix(config.get("presets", name))); @@ -388,11 +388,12 @@ void PresetBundle::export_selections(AppConfig &config) config.clear_section("presets"); config.set("presets", "print", prints.get_selected_preset_name()); config.set("presets", "filament", filament_presets.front()); - for (int i = 1; i < filament_presets.size(); ++i) { + for (unsigned i = 1; i < filament_presets.size(); ++i) { char name[64]; - sprintf(name, "filament_%d", i); + sprintf(name, "filament_%u", i); config.set("presets", name, filament_presets[i]); } + config.set("presets", "sla_print", sla_prints.get_selected_preset_name()); config.set("presets", "sla_material", sla_materials.get_selected_preset_name()); config.set("presets", "printer", printers.get_selected_preset_name()); @@ -697,6 +698,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool config.option<ConfigOptionString>("default_sla_print_profile", true); config.option<ConfigOptionString>("default_sla_material_profile", true); break; + default: break; } // 1) Create a name from the file name. @@ -778,7 +780,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool Preset *loaded = nullptr; if (is_external) loaded = &this->filaments.load_external_preset(name_or_path, name, - (i < old_filament_profile_names->values.size()) ? old_filament_profile_names->values[i] : "", + (i < int(old_filament_profile_names->values.size())) ? old_filament_profile_names->values[i] : "", std::move(cfg), i == 0); else { // Used by the config wizard when creating a custom setup. @@ -805,6 +807,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool load_preset(this->sla_materials, 1, "sla_material_settings_id"); load_preset(this->printers, 2, "printer_settings_id"); break; + default: break; } this->update_compatible(false); @@ -1260,7 +1263,7 @@ void PresetBundle::update_multi_material_filament_presets() // Now verify if wiping_volumes_matrix has proper size (it is used to deduce number of extruders in wipe tower generator): std::vector<double> old_matrix = this->project_config.option<ConfigOptionFloats>("wiping_volumes_matrix")->values; - size_t old_number_of_extruders = int(sqrt(old_matrix.size())+EPSILON); + size_t old_number_of_extruders = size_t(sqrt(old_matrix.size())+EPSILON); if (num_extruders != old_number_of_extruders) { // First verify if purging volumes presets for each extruder matches number of extruders std::vector<double>& extruders = this->project_config.option<ConfigOptionFloats>("wiping_volumes_extruders")->values; @@ -1349,6 +1352,7 @@ void PresetBundle::update_compatible(bool select_other_if_incompatible) [&prefered_sla_material_profile](const std::string& profile_name){ return profile_name == prefered_sla_material_profile; }); break; } + default: break; } } diff --git a/src/slic3r/GUI/PresetHints.cpp b/src/slic3r/GUI/PresetHints.cpp index 8b7d54538..a02772c4d 100644 --- a/src/slic3r/GUI/PresetHints.cpp +++ b/src/slic3r/GUI/PresetHints.cpp @@ -89,7 +89,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle double bridge_flow_ratio = print_config.opt_float("bridge_flow_ratio"); double perimeter_speed = print_config.opt_float("perimeter_speed"); double external_perimeter_speed = print_config.get_abs_value("external_perimeter_speed", perimeter_speed); - double gap_fill_speed = print_config.opt_float("gap_fill_speed"); + // double gap_fill_speed = print_config.opt_float("gap_fill_speed"); double infill_speed = print_config.opt_float("infill_speed"); double small_perimeter_speed = print_config.get_abs_value("small_perimeter_speed", perimeter_speed); double solid_infill_speed = print_config.get_abs_value("solid_infill_speed", infill_speed); @@ -123,7 +123,7 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle // Current filament values double filament_diameter = filament_config.opt_float("filament_diameter", 0); double filament_crossection = M_PI * 0.25 * filament_diameter * filament_diameter; - double extrusion_multiplier = filament_config.opt_float("extrusion_multiplier", 0); + // double extrusion_multiplier = filament_config.opt_float("extrusion_multiplier", 0); // The following value will be annotated by this hint, so it does not take part in the calculation. // double filament_max_volumetric_speed = filament_config.opt_float("filament_max_volumetric_speed", 0); diff --git a/src/slic3r/GUI/PrintHostDialogs.cpp b/src/slic3r/GUI/PrintHostDialogs.cpp index 09bd65682..d08389537 100644 --- a/src/slic3r/GUI/PrintHostDialogs.cpp +++ b/src/slic3r/GUI/PrintHostDialogs.cpp @@ -250,7 +250,7 @@ void PrintHostQueueDialog::on_list_select() void PrintHostQueueDialog::on_progress(Event &evt) { - wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list"); + wxCHECK_RET(evt.job_id < (size_t)job_list->GetItemCount(), "Out of bounds access to job list"); if (evt.progress < 100) { set_state(evt.job_id, ST_PROGRESS); @@ -265,7 +265,7 @@ void PrintHostQueueDialog::on_progress(Event &evt) void PrintHostQueueDialog::on_error(Event &evt) { - wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list"); + wxCHECK_RET(evt.job_id < (size_t)job_list->GetItemCount(), "Out of bounds access to job list"); set_state(evt.job_id, ST_ERROR); @@ -280,7 +280,7 @@ void PrintHostQueueDialog::on_error(Event &evt) void PrintHostQueueDialog::on_cancel(Event &evt) { - wxCHECK_RET(evt.job_id < job_list->GetItemCount(), "Out of bounds access to job list"); + wxCHECK_RET(evt.job_id < (size_t)job_list->GetItemCount(), "Out of bounds access to job list"); set_state(evt.job_id, ST_CANCELLED); job_list->SetValue(wxVariant(0), evt.job_id, COL_PROGRESS); diff --git a/src/slic3r/GUI/PrintHostDialogs.hpp b/src/slic3r/GUI/PrintHostDialogs.hpp index 427c4f6bf..e5f96f2b6 100644 --- a/src/slic3r/GUI/PrintHostDialogs.hpp +++ b/src/slic3r/GUI/PrintHostDialogs.hpp @@ -37,7 +37,6 @@ public: private: wxTextCtrl *txt_filename; wxCheckBox *box_print; - bool can_start_print; }; diff --git a/src/slic3r/GUI/Selection.cpp b/src/slic3r/GUI/Selection.cpp index 9c1a84615..40fbbbac6 100644 --- a/src/slic3r/GUI/Selection.cpp +++ b/src/slic3r/GUI/Selection.cpp @@ -310,7 +310,7 @@ void Selection::remove_volume(unsigned int object_idx, unsigned int volume_idx) for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { GLVolume* v = (*m_volumes)[i]; - if ((v->object_idx() == object_idx) && (v->volume_idx() == volume_idx)) + if ((v->object_idx() == (int)object_idx) && (v->volume_idx() == (int)volume_idx)) do_remove_volume(i); } @@ -992,7 +992,7 @@ void Selection::translate(unsigned int object_idx, const Vec3d& displacement) for (unsigned int i : m_list) { GLVolume* v = (*m_volumes)[i]; - if (v->object_idx() == object_idx) + if (v->object_idx() == (int)object_idx) v->set_instance_offset(v->get_instance_offset() + displacement); } @@ -1037,7 +1037,7 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co for (unsigned int i : m_list) { GLVolume* v = (*m_volumes)[i]; - if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) + if ((v->object_idx() == (int)object_idx) && (v->instance_idx() == (int)instance_idx)) v->set_instance_offset(v->get_instance_offset() + displacement); } @@ -1063,7 +1063,7 @@ void Selection::translate(unsigned int object_idx, unsigned int instance_idx, co continue; GLVolume* v = (*m_volumes)[j]; - if ((v->object_idx() != object_idx) || (v->instance_idx() != instance_idx)) + if ((v->object_idx() != object_idx) || (v->instance_idx() != (int)instance_idx)) continue; v->set_instance_offset(v->get_instance_offset() + displacement); @@ -1154,7 +1154,7 @@ void Selection::erase() for (const ItemForDelete& i : items_set) { if (i.type == ItemType::itVolume) { const int vol_in_obj_cnt = volumes_in_obj.find(i.obj_idx) == volumes_in_obj.end() ? 0 : volumes_in_obj.at(i.obj_idx); - if (vol_in_obj_cnt == m_model->objects[i.obj_idx]->volumes.size()) { + if (vol_in_obj_cnt == (int)m_model->objects[i.obj_idx]->volumes.size()) { if (i.sub_obj_idx == vol_in_obj_cnt - 1) items.emplace_back(ItemType::itObject, i.obj_idx, 0); continue; @@ -1389,7 +1389,7 @@ std::vector<unsigned int> Selection::get_volume_idxs_from_object(unsigned int ob for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { - if ((*m_volumes)[i]->object_idx() == object_idx) + if ((*m_volumes)[i]->object_idx() == (int)object_idx) idxs.push_back(i); } @@ -1403,7 +1403,7 @@ std::vector<unsigned int> Selection::get_volume_idxs_from_instance(unsigned int for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { const GLVolume* v = (*m_volumes)[i]; - if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) + if ((v->object_idx() == (int)object_idx) && (v->instance_idx() == (int)instance_idx)) idxs.push_back(i); } @@ -1417,9 +1417,9 @@ std::vector<unsigned int> Selection::get_volume_idxs_from_volume(unsigned int ob for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { const GLVolume* v = (*m_volumes)[i]; - if ((v->object_idx() == object_idx) && (v->volume_idx() == volume_idx)) + if ((v->object_idx() == (int)object_idx) && (v->volume_idx() == (int)volume_idx)) { - if ((instance_idx != -1) && (v->instance_idx() == instance_idx)) + if (((int)instance_idx != -1) && (v->instance_idx() == (int)instance_idx)) idxs.push_back(i); } } @@ -1607,7 +1607,7 @@ void Selection::update_type() } else { - int sels_cntr = 0; + unsigned int sels_cntr = 0; for (ObjectIdxsToInstanceIdxsMap::iterator it = m_cache.content.begin(); it != m_cache.content.end(); ++it) { const ModelObject* model_object = m_model->objects[it->first]; @@ -1759,7 +1759,7 @@ void Selection::do_remove_instance(unsigned int object_idx, unsigned int instanc for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { GLVolume* v = (*m_volumes)[i]; - if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) + if ((v->object_idx() == (int)object_idx) && (v->instance_idx() == (int)instance_idx)) do_remove_volume(i); } } @@ -1769,7 +1769,7 @@ void Selection::do_remove_object(unsigned int object_idx) for (unsigned int i = 0; i < (unsigned int)m_volumes->size(); ++i) { GLVolume* v = (*m_volumes)[i]; - if (v->object_idx() == object_idx) + if (v->object_idx() == (int)object_idx) do_remove_volume(i); } } @@ -1836,7 +1836,6 @@ void Selection::render_synchronized_volumes() const { const GLVolume* volume = (*m_volumes)[i]; int object_idx = volume->object_idx(); - int instance_idx = volume->instance_idx(); int volume_idx = volume->volume_idx(); for (unsigned int j = 0; j < (unsigned int)m_volumes->size(); ++j) { diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index d9cf56f03..c87626a48 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -232,7 +232,7 @@ void Tab::create_preset_tab() //! select_preset(m_presets_choice->GetStringSelection().ToUTF8().data()); //! we doing next: int selected_item = m_presets_choice->GetSelection(); - if (m_selected_preset_item == selected_item && !m_presets->current_is_dirty()) + if (m_selected_preset_item == size_t(selected_item) && !m_presets->current_is_dirty()) return; if (selected_item >= 0) { std::string selected_string = m_presets_choice->GetString(selected_item).ToUTF8().data(); @@ -489,7 +489,7 @@ template<class T> void add_correct_opts_to_options_list(const std::string &opt_key, std::map<std::string, int>& map, Tab *tab, const int& value) { T *opt_cur = static_cast<T*>(tab->m_config->option(opt_key)); - for (int i = 0; i < opt_cur->values.size(); i++) + for (size_t i = 0; i < opt_cur->values.size(); i++) map.emplace(opt_key + "#" + std::to_string(i), value); } @@ -995,6 +995,7 @@ void Tab::update_preset_description_line() description_line += "\n\n\t" + _(L("default SLA print profile")) + ": \n\t\t" + default_sla_print_profile; break; } + default: break; } } } @@ -1263,251 +1264,9 @@ void TabPrint::update() if (m_preset_bundle->printers.get_selected_preset().printer_technology() == ptSLA) return; // ys_FIXME - /* - // #ys_FIXME_to_delete - //! Temporary workaround for the correct updates of the TextCtrl (like "layer_height"): - // KillFocus() for the wxSpinCtrl use CallAfter function. So, - // to except the duplicate call of the update() after dialog->ShowModal(), - // let check if this process is already started. - if (is_msg_dlg_already_exist) - return; - */ - m_update_cnt++; // Freeze(); - /* #ys_FIXME_delete_after_testing (refactoring) - * - // layer_height shouldn't be equal to zero - if (m_config->opt_float("layer_height") < EPSILON) - { - const wxString msg_text = _(L("Zero layer height is not valid.\n\nThe layer height will be reset to 0.01.")); - wxMessageDialog dialog(parent(), msg_text, _(L("Layer height")), wxICON_WARNING | wxOK); - DynamicPrintConfig new_conf = *m_config; - is_msg_dlg_already_exist = true; - dialog.ShowModal(); - new_conf.set_key_value("layer_height", new ConfigOptionFloat(0.01)); - load_config(new_conf); - is_msg_dlg_already_exist = false; - } - - if (fabs(m_config->option<ConfigOptionFloatOrPercent>("first_layer_height")->value - 0) < EPSILON) - { - const wxString msg_text = _(L("Zero first layer height is not valid.\n\nThe first layer height will be reset to 0.01.")); - wxMessageDialog dialog(parent(), msg_text, _(L("First layer height")), wxICON_WARNING | wxOK); - DynamicPrintConfig new_conf = *m_config; - is_msg_dlg_already_exist = true; - dialog.ShowModal(); - new_conf.set_key_value("first_layer_height", new ConfigOptionFloatOrPercent(0.01, false)); - load_config(new_conf); - is_msg_dlg_already_exist = false; - } - - double fill_density = m_config->option<ConfigOptionPercent>("fill_density")->value; - - if (m_config->opt_bool("spiral_vase") && - !(m_config->opt_int("perimeters") == 1 && m_config->opt_int("top_solid_layers") == 0 && - fill_density == 0)) { - wxString msg_text = _(L("The Spiral Vase mode requires:\n" - "- one perimeter\n" - "- no top solid layers\n" - "- 0% fill density\n" - "- no support material\n" - "- no ensure_vertical_shell_thickness\n" - "\nShall I adjust those settings in order to enable Spiral Vase?")); - wxMessageDialog dialog(parent(), msg_text, _(L("Spiral Vase")), wxICON_WARNING | wxYES | wxNO); - DynamicPrintConfig new_conf = *m_config; - if (dialog.ShowModal() == wxID_YES) { - new_conf.set_key_value("perimeters", new ConfigOptionInt(1)); - new_conf.set_key_value("top_solid_layers", new ConfigOptionInt(0)); - new_conf.set_key_value("fill_density", new ConfigOptionPercent(0)); - new_conf.set_key_value("support_material", new ConfigOptionBool(false)); - new_conf.set_key_value("support_material_enforce_layers", new ConfigOptionInt(0)); - new_conf.set_key_value("ensure_vertical_shell_thickness", new ConfigOptionBool(false)); - fill_density = 0; - } - else { - new_conf.set_key_value("spiral_vase", new ConfigOptionBool(false)); - } - load_config(new_conf); - on_value_change("fill_density", fill_density); - } - - if (m_config->opt_bool("wipe_tower") && m_config->opt_bool("support_material") && - m_config->opt_float("support_material_contact_distance") > 0. && - (m_config->opt_int("support_material_extruder") != 0 || m_config->opt_int("support_material_interface_extruder") != 0)) { - wxString msg_text = _(L("The Wipe Tower currently supports the non-soluble supports only\n" - "if they are printed with the current extruder without triggering a tool change.\n" - "(both support_material_extruder and support_material_interface_extruder need to be set to 0).\n" - "\nShall I adjust those settings in order to enable the Wipe Tower?")); - wxMessageDialog dialog(parent(), msg_text, _(L("Wipe Tower")), wxICON_WARNING | wxYES | wxNO); - DynamicPrintConfig new_conf = *m_config; - if (dialog.ShowModal() == wxID_YES) { - new_conf.set_key_value("support_material_extruder", new ConfigOptionInt(0)); - new_conf.set_key_value("support_material_interface_extruder", new ConfigOptionInt(0)); - } - else - new_conf.set_key_value("wipe_tower", new ConfigOptionBool(false)); - load_config(new_conf); - } - - if (m_config->opt_bool("wipe_tower") && m_config->opt_bool("support_material") && - m_config->opt_float("support_material_contact_distance") == 0 && - !m_config->opt_bool("support_material_synchronize_layers")) { - wxString msg_text = _(L("For the Wipe Tower to work with the soluble supports, the support layers\n" - "need to be synchronized with the object layers.\n" - "\nShall I synchronize support layers in order to enable the Wipe Tower?")); - wxMessageDialog dialog(parent(), msg_text, _(L("Wipe Tower")), wxICON_WARNING | wxYES | wxNO); - DynamicPrintConfig new_conf = *m_config; - if (dialog.ShowModal() == wxID_YES) { - new_conf.set_key_value("support_material_synchronize_layers", new ConfigOptionBool(true)); - } - else - new_conf.set_key_value("wipe_tower", new ConfigOptionBool(false)); - load_config(new_conf); - } - - if (m_config->opt_bool("support_material")) { - // Ask only once. - if (!m_support_material_overhangs_queried) { - m_support_material_overhangs_queried = true; - if (!m_config->opt_bool("overhangs")/* != 1* /) { - wxString msg_text = _(L("Supports work better, if the following feature is enabled:\n" - "- Detect bridging perimeters\n" - "\nShall I adjust those settings for supports?")); - wxMessageDialog dialog(parent(), msg_text, _(L("Support Generator")), wxICON_WARNING | wxYES | wxNO | wxCANCEL); - DynamicPrintConfig new_conf = *m_config; - auto answer = dialog.ShowModal(); - if (answer == wxID_YES) { - // Enable "detect bridging perimeters". - new_conf.set_key_value("overhangs", new ConfigOptionBool(true)); - } else if (answer == wxID_NO) { - // Do nothing, leave supports on and "detect bridging perimeters" off. - } else if (answer == wxID_CANCEL) { - // Disable supports. - new_conf.set_key_value("support_material", new ConfigOptionBool(false)); - m_support_material_overhangs_queried = false; - } - load_config(new_conf); - } - } - } - else { - m_support_material_overhangs_queried = false; - } - - if (m_config->option<ConfigOptionPercent>("fill_density")->value == 100) { - auto fill_pattern = m_config->option<ConfigOptionEnum<InfillPattern>>("fill_pattern")->value; - std::string str_fill_pattern = ""; - t_config_enum_values map_names = m_config->option<ConfigOptionEnum<InfillPattern>>("fill_pattern")->get_enum_values(); - for (auto it : map_names) { - if (fill_pattern == it.second) { - str_fill_pattern = it.first; - break; - } - } - if (!str_fill_pattern.empty()) { - const std::vector<std::string> &external_fill_pattern = m_config->def()->get("top_fill_pattern")->enum_values; - bool correct_100p_fill = false; - for (const std::string &fill : external_fill_pattern) - { - if (str_fill_pattern == fill) - correct_100p_fill = true; - } - // get fill_pattern name from enum_labels for using this one at dialog_msg - str_fill_pattern = _utf8(m_config->def()->get("fill_pattern")->enum_labels[fill_pattern]); - if (!correct_100p_fill) { - wxString msg_text = GUI::from_u8((boost::format(_utf8(L("The %1% infill pattern is not supposed to work at 100%% density.\n\n" - "Shall I switch to rectilinear fill pattern?"))) % str_fill_pattern).str()); - wxMessageDialog dialog(parent(), msg_text, _(L("Infill")), wxICON_WARNING | wxYES | wxNO); - DynamicPrintConfig new_conf = *m_config; - if (dialog.ShowModal() == wxID_YES) { - new_conf.set_key_value("fill_pattern", new ConfigOptionEnum<InfillPattern>(ipRectilinear)); - fill_density = 100; - } - else - fill_density = m_presets->get_selected_preset().config.option<ConfigOptionPercent>("fill_density")->value; - new_conf.set_key_value("fill_density", new ConfigOptionPercent(fill_density)); - load_config(new_conf); - on_value_change("fill_density", fill_density); - } - } - } - - bool have_perimeters = m_config->opt_int("perimeters") > 0; - for (auto el : {"extra_perimeters", "ensure_vertical_shell_thickness", "thin_walls", "overhangs", - "seam_position", "external_perimeters_first", "external_perimeter_extrusion_width", - "perimeter_speed", "small_perimeter_speed", "external_perimeter_speed" }) - get_field(el)->toggle(have_perimeters); - - bool have_infill = m_config->option<ConfigOptionPercent>("fill_density")->value > 0; - // infill_extruder uses the same logic as in Print::extruders() - for (auto el : {"fill_pattern", "infill_every_layers", "infill_only_where_needed", - "solid_infill_every_layers", "solid_infill_below_area", "infill_extruder" }) - get_field(el)->toggle(have_infill); - - bool have_solid_infill = m_config->opt_int("top_solid_layers") > 0 || m_config->opt_int("bottom_solid_layers") > 0; - // solid_infill_extruder uses the same logic as in Print::extruders() - for (auto el : {"top_fill_pattern", "bottom_fill_pattern", "infill_first", "solid_infill_extruder", - "solid_infill_extrusion_width", "solid_infill_speed" }) - get_field(el)->toggle(have_solid_infill); - - for (auto el : {"fill_angle", "bridge_angle", "infill_extrusion_width", - "infill_speed", "bridge_speed" }) - get_field(el)->toggle(have_infill || have_solid_infill); - - get_field("gap_fill_speed")->toggle(have_perimeters && have_infill); - - bool have_top_solid_infill = m_config->opt_int("top_solid_layers") > 0; - for (auto el : { "top_infill_extrusion_width", "top_solid_infill_speed" }) - get_field(el)->toggle(have_top_solid_infill); - - bool have_default_acceleration = m_config->opt_float("default_acceleration") > 0; - for (auto el : {"perimeter_acceleration", "infill_acceleration", - "bridge_acceleration", "first_layer_acceleration" }) - get_field(el)->toggle(have_default_acceleration); - - bool have_skirt = m_config->opt_int("skirts") > 0 || m_config->opt_float("min_skirt_length") > 0; - for (auto el : { "skirt_distance", "skirt_height" }) - get_field(el)->toggle(have_skirt); - - bool have_brim = m_config->opt_float("brim_width") > 0; - // perimeter_extruder uses the same logic as in Print::extruders() - get_field("perimeter_extruder")->toggle(have_perimeters || have_brim); - - bool have_raft = m_config->opt_int("raft_layers") > 0; - bool have_support_material = m_config->opt_bool("support_material") || have_raft; - bool have_support_material_auto = have_support_material && m_config->opt_bool("support_material_auto"); - bool have_support_interface = m_config->opt_int("support_material_interface_layers") > 0; - bool have_support_soluble = have_support_material && m_config->opt_float("support_material_contact_distance") == 0; - for (auto el : {"support_material_pattern", "support_material_with_sheath", - "support_material_spacing", "support_material_angle", "support_material_interface_layers", - "dont_support_bridges", "support_material_extrusion_width", "support_material_contact_distance", - "support_material_xy_spacing" }) - get_field(el)->toggle(have_support_material); - get_field("support_material_threshold")->toggle(have_support_material_auto); - - for (auto el : {"support_material_interface_spacing", "support_material_interface_extruder", - "support_material_interface_speed", "support_material_interface_contact_loops" }) - get_field(el)->toggle(have_support_material && have_support_interface); - get_field("support_material_synchronize_layers")->toggle(have_support_soluble); - - get_field("perimeter_extrusion_width")->toggle(have_perimeters || have_skirt || have_brim); - get_field("support_material_extruder")->toggle(have_support_material || have_skirt); - get_field("support_material_speed")->toggle(have_support_material || have_brim || have_skirt); - - bool have_sequential_printing = m_config->opt_bool("complete_objects"); - for (auto el : { "extruder_clearance_radius", "extruder_clearance_height" }) - get_field(el)->toggle(have_sequential_printing); - - bool have_ooze_prevention = m_config->opt_bool("ooze_prevention"); - get_field("standby_temperature_delta")->toggle(have_ooze_prevention); - - bool have_wipe_tower = m_config->opt_bool("wipe_tower"); - for (auto el : { "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_bridging"}) - get_field(el)->toggle(have_wipe_tower); - */ - m_config_manipulation.update_print_fff_config(m_config, true); m_recommended_thin_wall_thickness_description_line->SetText( @@ -2049,7 +1808,7 @@ void TabPrinter::build_fff() optgroup->append_single_option_line("single_extruder_multi_material"); optgroup->m_on_change = [this, optgroup](t_config_option_key opt_key, boost::any value) { - size_t extruders_count = boost::any_cast<int>(optgroup->get_value("extruders_count")); + size_t extruders_count = boost::any_cast<size_t>(optgroup->get_value("extruders_count")); wxTheApp->CallAfter([this, opt_key, value, extruders_count]() { if (opt_key == "extruders_count" || opt_key == "single_extruder_multi_material") { extruders_count_changed(extruders_count); @@ -2469,7 +2228,7 @@ void TabPrinter::build_unregular_pages() // Add/delete Kinematics page according to is_marlin_flavor size_t existed_page = 0; - for (int i = n_before_extruders; i < m_pages.size(); ++i) // first make sure it's not there already + for (size_t i = n_before_extruders; i < m_pages.size(); ++i) // first make sure it's not there already if (m_pages[i]->title().find(_(L("Machine limits"))) != std::string::npos) { if (!is_marlin_flavor || m_rebuild_kinematics_page) m_pages.erase(m_pages.begin() + i); @@ -2494,7 +2253,7 @@ void TabPrinter::build_unregular_pages() (m_has_single_extruder_MM_page && m_extruders_count == 1)) { // if we have a single extruder MM setup, add a page with configuration options: - for (int i = 0; i < m_pages.size(); ++i) // first make sure it's not there already + for (size_t i = 0; i < m_pages.size(); ++i) // first make sure it's not there already if (m_pages[i]->title().find(_(L("Single extruder MM setup"))) != std::string::npos) { m_pages.erase(m_pages.begin() + i); break; @@ -2640,8 +2399,8 @@ void TabPrinter::on_preset_loaded() { // update the extruders count field auto *nozzle_diameter = dynamic_cast<const ConfigOptionFloats*>(m_config->option("nozzle_diameter")); - int extruders_count = nozzle_diameter->values.size(); - set_value("extruders_count", extruders_count); + size_t extruders_count = nozzle_diameter->values.size(); + set_value("extruders_count", int(extruders_count)); // update the GUI field according to the number of nozzle diameters supplied extruders_count_changed(extruders_count); } @@ -2779,7 +2538,7 @@ void TabPrinter::update_fff() DynamicPrintConfig new_conf = *m_config; if (dialog.ShowModal() == wxID_YES) { auto wipe = static_cast<ConfigOptionBools*>(m_config->option("wipe")->clone()); - for (int w = 0; w < wipe->values.size(); w++) + for (size_t w = 0; w < wipe->values.size(); w++) wipe->values[w] = false; new_conf.set_key_value("wipe", wipe); } @@ -3826,84 +3585,6 @@ void TabSLAPrint::update() m_update_cnt++; - /* #ys_FIXME_delete_after_testing (refactoring) - * - bool supports_en = m_config->opt_bool("supports_enable"); - - get_field("support_head_front_diameter")->toggle(supports_en); - get_field("support_head_penetration")->toggle(supports_en); - get_field("support_head_width")->toggle(supports_en); - get_field("support_pillar_diameter")->toggle(supports_en); - get_field("support_pillar_connection_mode")->toggle(supports_en); - get_field("support_buildplate_only")->toggle(supports_en); - get_field("support_base_diameter")->toggle(supports_en); - get_field("support_base_height")->toggle(supports_en); - get_field("support_base_safety_distance")->toggle(supports_en); - get_field("support_critical_angle")->toggle(supports_en); - get_field("support_max_bridge_length")->toggle(supports_en); - get_field("support_max_pillar_link_distance")->toggle(supports_en); - get_field("support_points_density_relative")->toggle(supports_en); - get_field("support_points_minimal_distance")->toggle(supports_en); - - bool pad_en = m_config->opt_bool("pad_enable"); - - get_field("pad_wall_thickness")->toggle(pad_en); - get_field("pad_wall_height")->toggle(pad_en); - get_field("pad_max_merge_distance")->toggle(pad_en); - // get_field("pad_edge_radius")->toggle(supports_en); - get_field("pad_wall_slope")->toggle(pad_en); - get_field("pad_around_object")->toggle(pad_en); - - double head_penetration = m_config->opt_float("support_head_penetration"); - double head_width = m_config->opt_float("support_head_width"); - if (head_penetration > head_width) { - wxString msg_text = _( - L("Head penetration should not be greater than the head width.")); - - wxMessageDialog dialog(parent(), - msg_text, - _(L("Invalid Head penetration")), - wxICON_WARNING | wxOK); - - DynamicPrintConfig new_conf = *m_config; - if (dialog.ShowModal() == wxID_OK) { - new_conf.set_key_value("support_head_penetration", - new ConfigOptionFloat(head_width)); - } - - load_config(new_conf); - } - - double pinhead_d = m_config->opt_float("support_head_front_diameter"); - double pillar_d = m_config->opt_float("support_pillar_diameter"); - if (pinhead_d > pillar_d) { - wxString msg_text = _(L( - "Pinhead diameter should be smaller than the pillar diameter.")); - - wxMessageDialog dialog (parent(), - msg_text, - _(L("Invalid pinhead diameter")), - wxICON_WARNING | wxOK); - - DynamicPrintConfig new_conf = *m_config; - if (dialog.ShowModal() == wxID_OK) { - new_conf.set_key_value("support_head_front_diameter", - new ConfigOptionFloat(pillar_d / 2.0)); - } - - load_config(new_conf); - } - - bool has_suppad = pad_en && supports_en; - bool zero_elev = m_config->opt_bool("pad_around_object") && has_suppad; - - get_field("support_object_elevation")->toggle(supports_en && !zero_elev); - get_field("pad_object_gap")->toggle(zero_elev); - get_field("pad_object_connector_stride")->toggle(zero_elev); - get_field("pad_object_connector_width")->toggle(zero_elev); - get_field("pad_object_connector_penetration")->toggle(zero_elev); -*/ - m_config_manipulation.update_print_sla_config(m_config, true); m_update_cnt--; diff --git a/src/slic3r/GUI/Tab.hpp b/src/slic3r/GUI/Tab.hpp index f57558c45..ee6937652 100644 --- a/src/slic3r/GUI/Tab.hpp +++ b/src/slic3r/GUI/Tab.hpp @@ -321,7 +321,6 @@ protected: class TabPrint : public Tab { - bool is_msg_dlg_already_exist {false}; public: TabPrint(wxNotebook* parent) : // Tab(parent, _(L("Print Settings")), L("print")) {} diff --git a/src/slic3r/GUI/UpdateDialogs.cpp b/src/slic3r/GUI/UpdateDialogs.cpp index a1a113066..d2a5e185a 100644 --- a/src/slic3r/GUI/UpdateDialogs.cpp +++ b/src/slic3r/GUI/UpdateDialogs.cpp @@ -34,10 +34,8 @@ static const std::string CONFIG_UPDATE_WIKI_URL("https://github.com/prusa3d/Prus // MsgUpdateSlic3r -MsgUpdateSlic3r::MsgUpdateSlic3r(const Semver &ver_current, const Semver &ver_online) : - MsgDialog(nullptr, _(L("Update available")), wxString::Format(_(L("New version of %s is available")), SLIC3R_APP_NAME)), - ver_current(ver_current), - ver_online(ver_online) +MsgUpdateSlic3r::MsgUpdateSlic3r(const Semver &ver_current, const Semver &ver_online) + : MsgDialog(nullptr, _(L("Update available")), wxString::Format(_(L("New version of %s is available")), SLIC3R_APP_NAME)) { const bool dev_version = ver_online.prerelease() != nullptr; diff --git a/src/slic3r/GUI/UpdateDialogs.hpp b/src/slic3r/GUI/UpdateDialogs.hpp index 4b61b84c2..a916e0145 100644 --- a/src/slic3r/GUI/UpdateDialogs.hpp +++ b/src/slic3r/GUI/UpdateDialogs.hpp @@ -31,8 +31,6 @@ public: bool disable_version_check() const; private: - const Semver &ver_current; - const Semver &ver_online; wxCheckBox *cbox; }; diff --git a/src/slic3r/GUI/WipeTowerDialog.cpp b/src/slic3r/GUI/WipeTowerDialog.cpp index 894b1ee62..460683f77 100644 --- a/src/slic3r/GUI/WipeTowerDialog.cpp +++ b/src/slic3r/GUI/WipeTowerDialog.cpp @@ -1,6 +1,7 @@ #include <algorithm> #include <sstream> #include "WipeTowerDialog.hpp" +#include "PresetBundle.hpp" #include "GUI.hpp" #include "I18N.hpp" #include "GUI_App.hpp" @@ -137,11 +138,11 @@ std::string RammingPanel::get_parameters() // Parent dialog for purging volume adjustments - it fathers WipingPanel widget (that contains all controls) and a button to toggle simple/advanced mode: -WipingDialog::WipingDialog(wxWindow* parent,const std::vector<float>& matrix, const std::vector<float>& extruders) +WipingDialog::WipingDialog(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, const std::vector<std::string>& extruder_colours) : wxDialog(parent, wxID_ANY, _(L("Wipe tower - Purging volume adjustment")), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE/* | wxRESIZE_BORDER*/) { auto widget_button = new wxButton(this,wxID_ANY,"-",wxPoint(0,0),wxDefaultSize); - m_panel_wiping = new WipingPanel(this,matrix,extruders, widget_button); + m_panel_wiping = new WipingPanel(this,matrix,extruders, extruder_colours, widget_button); auto main_sizer = new wxBoxSizer(wxVERTICAL); @@ -180,7 +181,7 @@ void WipingPanel::format_sizer(wxSizer* sizer, wxPanel* page, wxGridSizer* grid_ } // This panel contains all control widgets for both simple and advanced mode (these reside in separate sizers) -WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, wxButton* widget_button) +WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, const std::vector<std::string>& extruder_colours, wxButton* widget_button) : wxPanel(parent,wxID_ANY, wxDefaultPosition, wxDefaultSize/*,wxBORDER_RAISED*/) { m_widget_button = widget_button; // pointer to the button in parent dialog @@ -188,6 +189,12 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, con m_number_of_extruders = (int)(sqrt(matrix.size())+0.001); + for (const std::string& color : extruder_colours) { + unsigned char rgb[3]; + Slic3r::PresetBundle::parse_color(color, rgb); + m_colours.push_back(wxColor(rgb[0], rgb[1], rgb[2])); + } + // Create two switched panels with their own sizers m_sizer_simple = new wxBoxSizer(wxVERTICAL); m_sizer_advanced = new wxBoxSizer(wxVERTICAL); @@ -211,14 +218,36 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, con edit_boxes[i][j]->SetValue(wxString("") << int(matrix[m_number_of_extruders*j + i])); } } + + const int clr_icon_side = edit_boxes.front().front()->GetSize().y; + const auto icon_size = wxSize(clr_icon_side, clr_icon_side); + m_gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString(""))); - for (unsigned int i = 0; i < m_number_of_extruders; ++i) - m_gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); for (unsigned int i = 0; i < m_number_of_extruders; ++i) { - m_gridsizer_advanced->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); - for (unsigned int j = 0; j < m_number_of_extruders; ++j) - m_gridsizer_advanced->Add(edit_boxes[j][i], 0); - } + auto hsizer = new wxBoxSizer(wxHORIZONTAL); + hsizer->AddSpacer(20); + hsizer->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER); + wxWindow* w = new wxWindow(m_page_advanced, wxID_ANY, wxDefaultPosition, icon_size, wxBORDER_SIMPLE); + w->SetCanFocus(false); + w->SetBackgroundColour(m_colours[i]); + hsizer->AddStretchSpacer(); + hsizer->Add(w); + m_gridsizer_advanced->Add(hsizer, 1, wxEXPAND); + } + for (unsigned int i = 0; i < m_number_of_extruders; ++i) { + auto hsizer = new wxBoxSizer(wxHORIZONTAL); + wxWindow* w = new wxWindow(m_page_advanced, wxID_ANY, wxDefaultPosition, icon_size, wxBORDER_SIMPLE); + w->SetCanFocus(false); + w->SetBackgroundColour(m_colours[i]); + hsizer->AddSpacer(20); + hsizer->Add(new wxStaticText(m_page_advanced, wxID_ANY, wxString("") << i + 1), 0, wxALIGN_CENTER | wxALIGN_CENTER_VERTICAL); + hsizer->AddStretchSpacer(); + hsizer->Add(w); + m_gridsizer_advanced->Add(hsizer, 1, wxEXPAND); + + for (unsigned int j = 0; j < m_number_of_extruders; ++j) + m_gridsizer_advanced->Add(edit_boxes[j][i], 0); + } // collect and format sizer format_sizer(m_sizer_advanced, m_page_advanced, m_gridsizer_advanced, @@ -237,7 +266,16 @@ WipingPanel::WipingPanel(wxWindow* parent, const std::vector<float>& matrix, con for (unsigned int i=0;i<m_number_of_extruders;++i) { m_old.push_back(new wxSpinCtrl(m_page_simple,wxID_ANY,wxEmptyString,wxDefaultPosition, wxSize(ITEM_WIDTH(), -1),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i])); m_new.push_back(new wxSpinCtrl(m_page_simple,wxID_ANY,wxEmptyString,wxDefaultPosition, wxSize(ITEM_WIDTH(), -1),wxSP_ARROW_KEYS|wxALIGN_RIGHT,0,300,extruders[2*i+1])); - gridsizer_simple->Add(new wxStaticText(m_page_simple, wxID_ANY, wxString(_(L("Tool #"))) << i + 1 << ": "), 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); + + auto hsizer = new wxBoxSizer(wxHORIZONTAL); + wxWindow* w = new wxWindow(m_page_simple, wxID_ANY, wxDefaultPosition, icon_size, wxBORDER_SIMPLE); + w->SetCanFocus(false); + w->SetBackgroundColour(m_colours[i]); + hsizer->Add(w, wxALIGN_CENTER_VERTICAL); + hsizer->AddSpacer(10); + hsizer->Add(new wxStaticText(m_page_simple, wxID_ANY, wxString(_(L("Tool #"))) << i + 1 << ": "), 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); + + gridsizer_simple->Add(hsizer, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL); gridsizer_simple->Add(m_old.back(),0); gridsizer_simple->Add(m_new.back(),0); } diff --git a/src/slic3r/GUI/WipeTowerDialog.hpp b/src/slic3r/GUI/WipeTowerDialog.hpp index d858062da..84b9921de 100644 --- a/src/slic3r/GUI/WipeTowerDialog.hpp +++ b/src/slic3r/GUI/WipeTowerDialog.hpp @@ -46,7 +46,7 @@ private: class WipingPanel : public wxPanel { public: - WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, wxButton* widget_button); + WipingPanel(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, const std::vector<std::string>& extruder_colours, wxButton* widget_button); std::vector<float> read_matrix_values(); std::vector<float> read_extruders_values(); void toggle_advanced(bool user_action = false); @@ -59,6 +59,7 @@ private: std::vector<wxSpinCtrl*> m_old; std::vector<wxSpinCtrl*> m_new; std::vector<std::vector<wxTextCtrl*>> edit_boxes; + std::vector<wxColour> m_colours; unsigned int m_number_of_extruders = 0; bool m_advanced = false; wxPanel* m_page_simple = nullptr; @@ -76,7 +77,7 @@ private: class WipingDialog : public wxDialog { public: - WipingDialog(wxWindow* parent,const std::vector<float>& matrix, const std::vector<float>& extruders); + WipingDialog(wxWindow* parent, const std::vector<float>& matrix, const std::vector<float>& extruders, const std::vector<std::string>& extruder_colours); std::vector<float> get_matrix() const { return m_output_matrix; } std::vector<float> get_extruders() const { return m_output_extruders; } diff --git a/src/slic3r/GUI/wxExtensions.cpp b/src/slic3r/GUI/wxExtensions.cpp index 06e9a7f90..5f1e779d6 100644 --- a/src/slic3r/GUI/wxExtensions.cpp +++ b/src/slic3r/GUI/wxExtensions.cpp @@ -752,7 +752,7 @@ static bool append_root_node(ObjectDataViewModelNode *parent_node, if (inst_root_id < 0) { if ((root_type&itInstanceRoot) || - (root_type&itLayerRoot) && get_root_idx(parent_node, itInstanceRoot)<0) + ( (root_type&itLayerRoot) && get_root_idx(parent_node, itInstanceRoot)<0) ) parent_node->Append(*root_node); else if (root_type&itLayerRoot) parent_node->Insert(*root_node, static_cast<unsigned int>(get_root_idx(parent_node, itInstanceRoot))); @@ -940,7 +940,7 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item) // node can be deleted by the Delete, let's check its type while we safely can bool is_instance_root = (node->m_type & itInstanceRoot); - for (int i = node->GetChildCount() - 1; i >= (is_instance_root ? 1 : 0); i--) + for (int i = int(node->GetChildCount() - 1); i >= (is_instance_root ? 1 : 0); i--) Delete(wxDataViewItem(node->GetNthChild(i))); return parent; @@ -1020,7 +1020,7 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item) { int vol_cnt = 0; int vol_idx = 0; - for (int i = 0; i < node_parent->GetChildCount(); ++i) { + for (size_t i = 0; i < node_parent->GetChildCount(); ++i) { if (node_parent->GetNthChild(i)->GetType() == itVolume) { vol_idx = i; vol_cnt++; @@ -1059,7 +1059,7 @@ wxDataViewItem ObjectDataViewModel::Delete(const wxDataViewItem &item) else { auto it = find(m_objects.begin(), m_objects.end(), node); - auto id = it - m_objects.begin(); + size_t id = it - m_objects.begin(); if (it != m_objects.end()) { // Delete all sub-items @@ -1230,7 +1230,7 @@ void ObjectDataViewModel::DeleteSettings(const wxDataViewItem& parent) wxDataViewItem ObjectDataViewModel::GetItemById(int obj_idx) { - if (obj_idx >= m_objects.size()) + if (size_t(obj_idx) >= m_objects.size()) { printf("Error! Out of objects range.\n"); return wxDataViewItem(0); @@ -1241,7 +1241,7 @@ wxDataViewItem ObjectDataViewModel::GetItemById(int obj_idx) wxDataViewItem ObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volume_idx) { - if (obj_idx >= m_objects.size() || obj_idx < 0) { + if (size_t(obj_idx) >= m_objects.size()) { printf("Error! Out of objects range.\n"); return wxDataViewItem(0); } @@ -1265,7 +1265,7 @@ wxDataViewItem ObjectDataViewModel::GetItemByVolumeId(int obj_idx, int volume_id wxDataViewItem ObjectDataViewModel::GetItemById(const int obj_idx, const int sub_obj_idx, const ItemType parent_type) { - if (obj_idx >= m_objects.size() || obj_idx < 0) { + if (size_t(obj_idx) >= m_objects.size()) { printf("Error! Out of objects range.\n"); return wxDataViewItem(0); } @@ -1294,7 +1294,7 @@ wxDataViewItem ObjectDataViewModel::GetItemByLayerId(int obj_idx, int layer_idx) wxDataViewItem ObjectDataViewModel::GetItemByLayerRange(const int obj_idx, const t_layer_height_range& layer_range) { - if (obj_idx >= m_objects.size() || obj_idx < 0) { + if (size_t(obj_idx) >= m_objects.size()) { printf("Error! Out of objects range.\n"); return wxDataViewItem(0); } @@ -1379,7 +1379,12 @@ void ObjectDataViewModel::GetItemInfo(const wxDataViewItem& item, ItemType& type type = itUndef; ObjectDataViewModelNode *node = (ObjectDataViewModelNode*)item.GetID(); - if (!node || node->GetIdx() <-1 || node->GetIdx() == -1 && !(node->GetType() & (itObject | itSettings | itInstanceRoot | itLayerRoot/* | itLayer*/))) + if (!node || + node->GetIdx() <-1 || + ( node->GetIdx() == -1 && + !(node->GetType() & (itObject | itSettings | itInstanceRoot | itLayerRoot/* | itLayer*/)) + ) + ) return; idx = node->GetIdx(); @@ -1406,13 +1411,13 @@ int ObjectDataViewModel::GetRowByItem(const wxDataViewItem& item) const int row_num = 0; - for (int i = 0; i < m_objects.size(); i++) + for (size_t i = 0; i < m_objects.size(); i++) { row_num++; if (item == wxDataViewItem(m_objects[i])) return row_num; - for (int j = 0; j < m_objects[i]->GetChildCount(); j++) + for (size_t j = 0; j < m_objects[i]->GetChildCount(); j++) { row_num++; ObjectDataViewModelNode* cur_node = m_objects[i]->GetNthChild(j); @@ -1424,7 +1429,7 @@ int ObjectDataViewModel::GetRowByItem(const wxDataViewItem& item) const if (cur_node->m_type == itInstanceRoot) { row_num++; - for (int t = 0; t < cur_node->GetChildCount(); t++) + for (size_t t = 0; t < cur_node->GetChildCount(); t++) { row_num++; if (item == wxDataViewItem(cur_node->GetNthChild(t))) @@ -1498,7 +1503,7 @@ bool ObjectDataViewModel::SetValue(const wxVariant &variant, const wxDataViewIte bool ObjectDataViewModel::SetValue(const wxVariant &variant, const int item_idx, unsigned int col) { - if (item_idx < 0 || item_idx >= m_objects.size()) + if (size_t(item_idx) >= m_objects.size()) return false; return m_objects[item_idx]->SetValue(variant, col); @@ -1657,7 +1662,7 @@ wxDataViewItem ObjectDataViewModel::GetItemByType(const wxDataViewItem &parent_i if (node->GetChildCount() == 0) return wxDataViewItem(0); - for (int i = 0; i < node->GetChildCount(); i++) { + for (size_t i = 0; i < node->GetChildCount(); i++) { if (node->GetNthChild(i)->m_type == type) return wxDataViewItem((void*)node->GetNthChild(i)); } @@ -2140,7 +2145,7 @@ void DoubleSlider::draw_scroll_line(wxDC& dc, const int lower_pos, const int hig wxCoord segm_end_x = is_horizontal() ? higher_pos : width*0.5 - 1; wxCoord segm_end_y = is_horizontal() ? height*0.5 - 1 : higher_pos-1; - for (int id = 0; id < m_line_pens.size(); id++) + for (size_t id = 0; id < m_line_pens.size(); id++) { dc.SetPen(*m_line_pens[id]); dc.DrawLine(line_beg_x, line_beg_y, line_end_x, line_end_y); @@ -2187,9 +2192,9 @@ double DoubleSlider::get_double_value(const SelectedSlider& selection) return 0.0; if (m_values.size() <= m_higher_value) { correct_higher_value(); - return m_values.back().second; + return m_values.back(); } - return m_values[selection == ssLower ? m_lower_value : m_higher_value].second; + return m_values[selection == ssLower ? m_lower_value : m_higher_value]; } std::vector<double> DoubleSlider::GetTicksValues() const @@ -2201,7 +2206,7 @@ std::vector<double> DoubleSlider::GetTicksValues() const for (int tick : m_ticks) { if (tick > val_size) break; - values.push_back(m_values[tick].second); + values.push_back(m_values[tick]); } return values; @@ -2215,14 +2220,13 @@ void DoubleSlider::SetTicksValues(const std::vector<double>& heights) const bool was_empty = m_ticks.empty(); m_ticks.clear(); - unsigned int i = 0; for (auto h : heights) { - while (i < m_values.size() && m_values[i].second - epsilon()/*1e-6*/ < h) - ++i; - // don't miss last layer if it is - if (i == m_values.size() && fabs(m_values[i-1].second - h) > epsilon()) - return; - m_ticks.insert(i-1); + auto it = std::lower_bound(m_values.begin(), m_values.end(), h - epsilon()); + + if (it == m_values.end()) + continue; + + m_ticks.insert(it-m_values.begin()); } if (!was_empty && m_ticks.empty()) @@ -2342,13 +2346,14 @@ wxString DoubleSlider::get_label(const SelectedSlider& selection) const const wxString str = m_values.empty() ? wxNumberFormatter::ToString(m_label_koef*value, 2, wxNumberFormatter::Style_None) : - wxNumberFormatter::ToString(m_values[value].second, 2, wxNumberFormatter::Style_None); - return wxString::Format("%s\n(%d)", str, m_values.empty() ? value : m_values[value].first); + wxNumberFormatter::ToString(m_values[value], 2, wxNumberFormatter::Style_None); + return wxString::Format("%s\n(%d)", str, m_values.empty() ? value : value+1); } void DoubleSlider::draw_thumb_text(wxDC& dc, const wxPoint& pos, const SelectedSlider& selection) const { - if ((m_is_one_layer || m_higher_value==m_lower_value) && selection != m_selection || !selection) + if ( selection == ssUndef || + ((m_is_one_layer || m_higher_value==m_lower_value) && selection != m_selection) ) return; wxCoord text_width, text_height; const wxString label = get_label(selection); @@ -2489,7 +2494,7 @@ void DoubleSlider::draw_colored_band(wxDC& dc) dc.SetBrush(clr); dc.DrawRectangle(main_band); - int i = 1; + size_t i = 1; for (auto tick : m_ticks) { if (i == colors_cnt) @@ -2680,7 +2685,7 @@ void DoubleSlider::correct_lower_value() else if (m_lower_value > m_max_value) m_lower_value = m_max_value; - if (m_lower_value >= m_higher_value && m_lower_value <= m_max_value || m_is_one_layer) + if ((m_lower_value >= m_higher_value && m_lower_value <= m_max_value) || m_is_one_layer) m_higher_value = m_lower_value; } @@ -2691,7 +2696,7 @@ void DoubleSlider::correct_higher_value() else if (m_higher_value < m_min_value) m_higher_value = m_min_value; - if (m_higher_value <= m_lower_value && m_higher_value >= m_min_value || m_is_one_layer) + if ((m_higher_value <= m_lower_value && m_higher_value >= m_min_value) || m_is_one_layer) m_lower_value = m_higher_value; } diff --git a/src/slic3r/GUI/wxExtensions.hpp b/src/slic3r/GUI/wxExtensions.hpp index 51faffda7..54d1bf7cb 100644 --- a/src/slic3r/GUI/wxExtensions.hpp +++ b/src/slic3r/GUI/wxExtensions.hpp @@ -385,9 +385,9 @@ class ObjectDataViewModel :public wxDataViewModel { std::vector<ObjectDataViewModelNode*> m_objects; std::vector<wxBitmap*> m_volume_bmps; - wxBitmap* m_warning_bmp; + wxBitmap* m_warning_bmp { nullptr }; - wxDataViewCtrl* m_ctrl{ nullptr }; + wxDataViewCtrl* m_ctrl { nullptr }; public: ObjectDataViewModel(); @@ -720,15 +720,17 @@ public: const wxString& name = wxEmptyString); ~DoubleSlider() {} - // permissible error for layer height + /* For exporting GCode in GCodeWriter is used XYZF_NUM(val) = PRECISION(val, 3) for XYZ values. + * So, let use same value as a permissible error for layer height. + */ static double epsilon() { return 0.0011;} void msw_rescale(); int GetMinValue() const { return m_min_value; } int GetMaxValue() const { return m_max_value; } - double GetMinValueD() { return m_values.empty() ? 0. : m_values[m_min_value].second; } - double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value].second; } + double GetMinValueD() { return m_values.empty() ? 0. : m_values[m_min_value]; } + double GetMaxValueD() { return m_values.empty() ? 0. : m_values[m_max_value]; } int GetLowerValue() const { return m_lower_value; } int GetHigherValue() const { return m_higher_value; } int GetActiveValue() const; @@ -744,7 +746,7 @@ public: void SetKoefForLabels(const double koef) { m_label_koef = koef; } - void SetSliderValues(const std::vector<std::pair<int, double>>& values) { + void SetSliderValues(const std::vector<double>& values) { m_values = values; } void ChangeOneLayerLock(); @@ -865,7 +867,7 @@ private: std::vector<wxPen*> m_line_pens; std::vector<wxPen*> m_segm_pens; std::set<int> m_ticks; - std::vector<std::pair<int,double>> m_values; + std::vector<double> m_values; }; diff --git a/src/slic3r/Utils/UndoRedo.cpp b/src/slic3r/Utils/UndoRedo.cpp index b3b2628e2..9c8d7a8c6 100644 --- a/src/slic3r/Utils/UndoRedo.cpp +++ b/src/slic3r/Utils/UndoRedo.cpp @@ -363,12 +363,14 @@ private: MutableHistoryInterval& operator=(const MutableHistoryInterval &rhs); }; +#ifdef SLIC3R_UNDOREDO_DEBUG static inline std::string ptr_to_string(const void* ptr) { char buf[64]; sprintf(buf, "%p", ptr); return buf; } +#endif // Smaller objects (Model, ModelObject, ModelInstance, ModelVolume, DynamicPrintConfig) // are mutable and there is not tracking of the changes, therefore a snapshot needs to be |