diff --git a/src/libnest2d/include/libnest2d/nester.hpp b/src/libnest2d/include/libnest2d/nester.hpp index 9038a9d63..13281c389 100644 --- a/src/libnest2d/include/libnest2d/nester.hpp +++ b/src/libnest2d/include/libnest2d/nester.hpp @@ -69,13 +69,14 @@ class _Item { Box bb; bool valid; BBCache(): valid(false) {} } bb_cache_; - + int binid_{BIN_ID_UNSET}, priority_{0}; bool fixed_{false}; public: int itemid_{ 0 }; std::vector extrude_ids; + int filament_temp_type = -1; // -1 means unset. otherwise should be {0,1,2} double height{ 0 }; double print_temp{ 0 }; double bed_temp{ 0 }; @@ -140,7 +141,7 @@ public: inline _Item(TContour&& contour, THolesContainer&& holes): sh_(sl::create(std::move(contour), std::move(holes))) {} - + inline bool isFixed() const noexcept { return fixed_; } inline void markAsFixedInBin(int binid) { @@ -150,7 +151,7 @@ public: inline void binId(int idx) { binid_ = idx; } inline int binId() const noexcept { return binid_; } - + inline void priority(int p) { priority_ = p; } inline int priority() const noexcept { return priority_; } @@ -303,18 +304,18 @@ public: { rotation(rotation() + rads); } - + inline void inflation(Coord distance) BP2D_NOEXCEPT { inflation_ = distance; has_inflation_ = true; invalidateCache(); } - + inline Coord inflation() const BP2D_NOEXCEPT { return inflation_; } - + inline void inflate(Coord distance) BP2D_NOEXCEPT { inflation(inflation() + distance); @@ -780,7 +781,7 @@ template class _Nester { using TSel = SelectionStrategyLike; TSel selector_; - + public: using Item = typename PlacementStrategy::Item; using ShapeType = typename Item::ShapeType; @@ -805,7 +806,7 @@ private: StopCondition stopfn_; template using TVal = remove_ref_t; - + template using ItemIteratorOnly = enable_if_t&, TPItem&>::value, Out>; @@ -863,14 +864,14 @@ public: if(infl > 0) std::for_each(from, to, [infl](Item& item) { item.inflate(infl); }); - + selector_.template packItems( from, to, bin_, pconfig_); - + if(min_obj_distance_ > 0) std::for_each(from, to, [infl](Item& item) { item.inflate(-infl); }); - + return selector_.getResult().size(); } diff --git a/src/libslic3r/Arrange.cpp b/src/libslic3r/Arrange.cpp index 1e22eb303..8c14c537b 100644 --- a/src/libslic3r/Arrange.cpp +++ b/src/libslic3r/Arrange.cpp @@ -1,5 +1,5 @@ #include "Arrange.hpp" - +#include "Print.hpp" #include "BoundingBox.hpp" #include @@ -260,7 +260,7 @@ Points get_shrink_bedpts(const DynamicPrintConfig* print_cfg, const ArrangeParam // Slic3r. template void fill_config(PConf& pcfg, const ArrangeParams ¶ms) { - + if (params.is_seq_print) { // Start placing the items from the center of the print bed pcfg.starting_point = PConf::Alignment::BOTTOM_LEFT; @@ -269,7 +269,7 @@ void fill_config(PConf& pcfg, const ArrangeParams ¶ms) { // Start placing the items from the center of the print bed pcfg.starting_point = PConf::Alignment::TOP_RIGHT; } - + if (params.do_final_align) { // Align the arranged pile into the center of the bin pcfg.alignment = PConf::Alignment::CENTER; @@ -286,7 +286,7 @@ void fill_config(PConf& pcfg, const ArrangeParams ¶ms) { // The accuracy of optimization. // Goes from 0.0 to 1.0 and scales performance as well pcfg.accuracy = params.accuracy; - + // Allow parallel execution. pcfg.parallel = params.parallel; @@ -312,7 +312,7 @@ static double fixed_overfit(const std::tuple& result, const Box &bi Box fullbb = sl::boundingBox(pilebb, binbb); auto diff = double(fullbb.area()) - binbb.area(); if(diff > 0) score += diff; - + return score; } @@ -379,7 +379,7 @@ protected: std::vector m_excluded_and_extruCali_regions; // excluded and extrusion calib regions size_t m_item_count = 0; // Number of all items to be packed ArrangeParams params; - + template ArithmeticOnly norm(T val) { return double(val) / m_norm; @@ -417,18 +417,18 @@ protected: const double bin_area = m_bin_area; const SpatIndex& spatindex = m_rtree; const SpatIndex& smalls_spatindex = m_smallsrtree; - + // We will treat big items (compared to the print bed) differently auto isBig = [bin_area](double a) { return a/bin_area > BIG_ITEM_TRESHOLD ; }; - + // Candidate item bounding box auto ibb = item.boundingBox(); - + // Calculate the full bounding box of the pile with the candidate item auto fullbb = sl::boundingBox(m_pilebb, ibb); - + // The bounding box of the big items (they will accumulate in the center // of the pile Box bigbb; @@ -437,31 +437,31 @@ protected: auto boostbb = spatindex.bounds(); boost::geometry::convert(boostbb, bigbb); } - + // Will hold the resulting score double score = 0; - + // Density is the pack density: how big is the arranged pile double density = 0; - + // Distinction of cases for the arrangement scene enum e_cases { // This branch is for big items in a mixed (big and small) scene // OR for all items in a small-only scene. BIG_ITEM, - + // This branch is for the last big item in a mixed scene LAST_BIG_ITEM, - + // For small items in a mixed scene. SMALL_ITEM } compute_case; - + bool bigitems = isBig(item.area()) || spatindex.empty(); if(!params.is_seq_print && bigitems && !m_remaining.empty()) compute_case = BIG_ITEM; // do not use so complicated logic for sequential printing else if (bigitems && m_remaining.empty()) compute_case = LAST_BIG_ITEM; else compute_case = SMALL_ITEM; - + switch (compute_case) { case BIG_ITEM: { const Point& minc = ibb.minCorner(); // bottom left corner @@ -566,7 +566,7 @@ protected: } break; - } + } } @@ -603,9 +603,11 @@ protected: for (int i = 0; i < m_items.size(); i++) { Item& p = m_items[i]; if (p.is_virt_object) continue; - score += lambda3 * (item.bed_temp - p.vitrify_temp > VITRIFY_TEMP_DIFF_THRSH); + //score += lambda3 * (item.bed_temp - p.vitrify_temp > VITRIFY_TEMP_DIFF_THRSH); + if (!Print::is_filaments_compatible({item.filament_temp_type,p.filament_temp_type})) + score += lambda3; } - score += lambda3 * (item.bed_temp - item.vitrify_temp > VITRIFY_TEMP_DIFF_THRSH); + //score += lambda3 * (item.bed_temp - item.vitrify_temp > VITRIFY_TEMP_DIFF_THRSH); score += lambda4 * hasRowHeightConflict + lambda4 * hasLidHeightConflict; } else { @@ -625,7 +627,9 @@ protected: // 高度接近的件尽量摆到一起 score += (1- std::abs(item.height - p.height) / params.printable_height) * norm(pl::distance(ibb.center(), p.boundingBox().center())); - score += LARGE_COST_TO_REJECT * (item.bed_temp - p.bed_temp != 0); + //score += LARGE_COST_TO_REJECT * (item.bed_temp - p.bed_temp != 0); + if (!Print::is_filaments_compatible({ item.filament_temp_type,p.filament_temp_type })) + score += LARGE_COST_TO_REJECT; } } } @@ -656,9 +660,9 @@ protected: return std::make_tuple(score, fullbb); } - + std::function get_objfn(); - + public: AutoArranger(const TBin & bin, const ArrangeParams ¶ms, @@ -708,7 +712,7 @@ public: m_rtree.clear(); m_smallsrtree.clear(); - + // We will treat big items (compared to the print bed) differently auto isBig = [this](double a) { return a / m_bin_area > BIG_ITEM_TRESHOLD ; @@ -721,7 +725,7 @@ public: m_smallsrtree.insert({itm.boundingBox(), idx}); } }; - + m_pconf.object_function = get_objfn(); // preload fixed items (and excluded regions) on plate @@ -746,7 +750,7 @@ public: }; auto on_packed = params.on_packed; - + if (progressind || on_packed) m_pck.progressIndicator( [this, progressind, on_packed](unsigned num_finished) { @@ -760,7 +764,8 @@ public: if (on_packed) on_packed(ap); BOOST_LOG_TRIVIAL(debug) << "arrange " + last_packed.name + " succeed!" - << ", plate id=" << ap.bed_idx << ", pos=" << last_packed.translation(); + << ", plate id=" << ap.bed_idx << ", pos=" << last_packed.translation() + << ", temp_type=" << last_packed.filament_temp_type; } }); @@ -783,21 +788,21 @@ public: (i1.extrude_ids != i2.extrude_ids ? (i1.extrude_ids.front() < i2.extrude_ids.front()) : (i1.area() > i2.area())); } }; - + m_pck.configure(m_pconf); } - + template inline void operator()(It from, It to) { m_rtree.clear(); m_item_count += size_t(to - from); m_pck.execute(from, to); m_item_count = 0; } - + PConfig& config() { return m_pconf; } const PConfig& config() const { return m_pconf; } - - inline void preload(std::vector& fixeditems) { + + inline void preload(std::vector& fixeditems) { for(unsigned idx = 0; idx < fixeditems.size(); ++idx) { Item& itm = fixeditems[idx]; itm.markAsFixedInBin(itm.binId()); @@ -813,7 +818,7 @@ template<> std::function AutoArranger(result); auto& fullbb = std::get<1>(result); @@ -840,15 +845,15 @@ template<> std::function AutoArranger(result); - + auto isBig = [this](const Item& itm) { return itm.area() / m_bin_area > BIG_ITEM_TRESHOLD ; }; - + if(isBig(item)) { auto mp = m_merged_pile; mp.push_back(item.transformedShape()); @@ -857,7 +862,7 @@ template<> std::function AutoArranger AutoArranger::g auto result = objfunc(itm, origin_pack); double score = std::get<0>(result); - + auto mp = m_merged_pile; mp.emplace_back(itm.transformedShape()); auto chull = sl::convexHull(mp); @@ -930,14 +935,14 @@ void _arrange( // Integer ceiling the min distance from the bed perimeters coord_t md = params.min_obj_distance; md = md / 2; - + auto corrected_bin = bin; //sl::offset(corrected_bin, md); ArrangeParams mod_params = params; mod_params.min_obj_distance = 0; // items are already inflated AutoArranger arranger{corrected_bin, mod_params, progressfn, stopfn}; - + remove_large_items(excludes, corrected_bin); // If there is something on the plate @@ -947,7 +952,7 @@ void _arrange( inp.reserve(shapes.size() + excludes.size()); for (auto &itm : shapes ) inp.emplace_back(itm); for (auto &itm : excludes) inp.emplace_back(itm); - + // Use the minimum bounding box rotation as a starting point. // TODO: This only works for convex hull. If we ever switch to concave // polygon nesting, a convex hull needs to be calculated. @@ -987,16 +992,16 @@ inline double distance_to(const Point& p1, const Point& p2) static CircleBed to_circle(const Point ¢er, const Points& points) { std::vector vertex_distances; double avg_dist = 0; - + for (auto pt : points) { double distance = distance_to(center, pt); vertex_distances.push_back(distance); avg_dist += distance; } - + avg_dist /= vertex_distances.size(); - + CircleBed ret(center, avg_dist); for(auto el : vertex_distances) { @@ -1005,7 +1010,7 @@ static CircleBed to_circle(const Point ¢er, const Points& points) { break; } } - + return ret; } @@ -1039,6 +1044,7 @@ static void process_arrangeable(const ArrangePolygon &arrpoly, item.print_temp = arrpoly.print_temp; item.vitrify_temp = arrpoly.vitrify_temp; item.inflation(arrpoly.inflation); + item.filament_temp_type = arrpoly.filament_temp_type; } template auto call_with_bed(const Points &bed, Fn &&fn) @@ -1079,20 +1085,20 @@ void arrange(ArrangePolygons & arrangables, const ArrangeParams & params) { namespace clppr = Slic3r::ClipperLib; - + std::vector items, fixeditems; items.reserve(arrangables.size()); - + for (ArrangePolygon &arrangeable : arrangables) process_arrangeable(arrangeable, items); - + for (const ArrangePolygon &fixed: excludes) process_arrangeable(fixed, fixeditems); - + for (Item &itm : fixeditems) itm.inflate(scaled(-2. * EPSILON)); - + _arrange(items, fixeditems, to_nestbin(bed), params, params.progressind, params.stopcondition); - + for(size_t i = 0; i < items.size(); ++i) { Point tr = items[i].translation(); arrangables[i].translation = {coord_t(tr.x()), coord_t(tr.y())}; diff --git a/src/libslic3r/Arrange.hpp b/src/libslic3r/Arrange.hpp index 63f180859..bf9815be6 100644 --- a/src/libslic3r/Arrange.hpp +++ b/src/libslic3r/Arrange.hpp @@ -40,12 +40,12 @@ static const constexpr int UNARRANGED = -1; /// be modified during arrangement. Instead, the translation and rotation fields /// will mark the needed transformation for the polygon to be in the arranged /// position. These can also be set to an initial offset and rotation. -/// +/// /// The bed_idx field will indicate the logical bed into which the /// polygon belongs: UNARRANGED means no place for the polygon /// (also the initial state before arrange), 0..N means the index of the bed. /// Zero is the physical bed, larger than zero means a virtual bed. -struct ArrangePolygon { +struct ArrangePolygon { ExPolygon poly; /// The 2D silhouette to be arranged Vec2crd translation{0, 0}; /// The translation of the poly double rotation{0.0}; /// The rotation of the poly in radians @@ -62,6 +62,7 @@ struct ArrangePolygon { int row{0}; int col{0}; std::vector extrude_ids{}; /// extruder_id for least extruder switch + int filament_temp_type{ -1 }; int bed_temp{0}; ///bed temperature for different material judge int print_temp{0}; ///print temperature for different material judge int first_bed_temp{ 0 }; ///first layer bed temperature for different material judge @@ -69,20 +70,20 @@ struct ArrangePolygon { int vitrify_temp{ 0 }; // max bed temperature for material compatibility, which is usually the filament vitrification temp int itemid{ 0 }; // item id in the vector, used for accessing all possible params like extrude_id int is_applied{ 0 }; // transform has been applied - double height{ 0 }; // item height + double height{ 0 }; // item height double brim_width{ 0 }; // brim width std::string name; - + // If empty, any rotation is allowed (currently unsupported) // If only a zero is there, no rotation is allowed std::vector allowed_rotations = {0.}; - + /// Optional setter function which can store arbitrary data in its closure std::function setter = nullptr; - + /// Helper function to call the setter with the arrange data arguments void apply() { - if (setter && !is_applied) { + if (setter && !is_applied) { setter(*this); is_applied = 1; } @@ -104,15 +105,15 @@ struct ArrangePolygon { using ArrangePolygons = std::vector; struct ArrangeParams { - - /// The minimum distance which is allowed for any + + /// The minimum distance which is allowed for any /// pair of items on the print bed in any direction. coord_t min_obj_distance = 0; - + /// The accuracy of optimization. /// Goes from 0.0 to 1.0 and scales performance as well float accuracy = 1.f; - + /// Allow parallel execution. bool parallel = true; @@ -136,18 +137,18 @@ struct ArrangeParams { ArrangePolygons excluded_regions; // regions cant't be used ArrangePolygons nonprefered_regions; // regions can be used but not prefered - - /// Progress indicator callback called when an object gets packed. + + /// Progress indicator callback called when an object gets packed. /// The unsigned argument is the number of items remaining to pack. std::function progressind = [](unsigned st, std::string str = "") { std::cout << "st=" << st << ", " << str << std::endl; }; std::function on_packed; - + /// A predicate returning true if abort is needed. std::function stopcondition; - + ArrangeParams() = default; explicit ArrangeParams(coord_t md) : min_obj_distance(md) {} // to json format @@ -186,11 +187,11 @@ Points get_shrink_bedpts(const DynamicPrintConfig* print_cfg, const ArrangeParam /** * \brief Arranges the input polygons. * - * WARNING: Currently, only convex polygons are supported by the libnest2d + * WARNING: Currently, only convex polygons are supported by the libnest2d * library which is used to do the arrangement. This might change in the future * this is why the interface contains a general polygon capable to have holes. * - * \param items Input vector of ArrangePolygons. The transformation, rotation + * \param items Input vector of ArrangePolygons. The transformation, rotation * and bin_idx fields will be changed after the call finished and can be used * to apply the result on the input polygon. */ diff --git a/src/libslic3r/ModelArrange.cpp b/src/libslic3r/ModelArrange.cpp index 710b031fe..50622a3b5 100644 --- a/src/libslic3r/ModelArrange.cpp +++ b/src/libslic3r/ModelArrange.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "MTUtils.hpp" namespace Slic3r { @@ -10,7 +11,7 @@ arrangement::ArrangePolygons get_arrange_polys(const Model &model, ModelInstance { size_t count = 0; for (auto obj : model.objects) count += obj->instances.size(); - + ArrangePolygons input; input.reserve(count); instances.clear(); instances.reserve(count); @@ -21,21 +22,21 @@ arrangement::ArrangePolygons get_arrange_polys(const Model &model, ModelInstance input.emplace_back(ap); instances.emplace_back(minst); } - + return input; } bool apply_arrange_polys(ArrangePolygons &input, ModelInstancePtrs &instances, VirtualBedFn vfn) { bool ret = true; - + for(size_t i = 0; i < input.size(); ++i) { if (input[i].bed_idx != 0) { ret = false; if (vfn) vfn(input[i]); } if (input[i].bed_idx >= 0) instances[i]->apply_arrange_result(input[i].translation.cast(), input[i].rotation); } - + return ret; } @@ -52,7 +53,7 @@ Slic3r::arrangement::ArrangePolygon get_arrange_poly(const Model &model) const Points &pts = obj_ap.poly.contour.points; std::copy(pts.begin(), pts.end(), std::back_inserter(apts)); } - + apts = std::move(Geometry::convex_hull(apts).points); return ap; } @@ -118,7 +119,7 @@ arrangement::ArrangePolygon get_arrange_poly(ModelInstance* inst, const Slic3r:: ArrangePolygon get_instance_arrange_poly(ModelInstance* instance, const Slic3r::DynamicPrintConfig& config) { ArrangePolygon ap = get_arrange_poly(PtrWrapper{ instance }, config); - + //BBS: add temperature information if (config.has("curr_bed_type")) { ap.bed_temp = 0; @@ -138,11 +139,23 @@ ArrangePolygon get_instance_arrange_poly(ModelInstance* instance, const Slic3r:: ap.print_temp = config.opt_int("nozzle_temperature", ap.extrude_ids.front() - 1); if (config.has("nozzle_temperature_initial_layer")) //get the nozzle_temperature_initial_layer ap.first_print_temp = config.opt_int("nozzle_temperature_initial_layer", ap.extrude_ids.front() - 1); - + if (config.has("temperature_vitrification")) { ap.vitrify_temp = config.opt_int("temperature_vitrification", ap.extrude_ids.front() - 1); } + // get filament temp types + auto* filament_types_opt = dynamic_cast(config.option("filament_type")); + if (filament_types_opt) { + std::set filament_temp_types; + for (auto i : ap.extrude_ids) { + std::string type_str = filament_types_opt->get_at(i-1); + int temp_type = Print::get_filament_temp_type(type_str); + filament_temp_types.insert(temp_type); + } + ap.filament_temp_type = Print::get_compatible_filament_type(filament_temp_types); + } + // get brim width auto obj = instance->get_object(); @@ -162,7 +175,7 @@ ArrangePolygon get_instance_arrange_poly(ModelInstance* instance, const Slic3r:: ap.brim_width = 24.0; // 2*MAX_BRANCH_RADIUS_FIRST_LAYER ap.has_tree_support = true; } - + ap.height = obj->bounding_box().size().z(); ap.name = obj->name; return ap; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 9e73d76a3..45bcd3ac8 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -916,6 +916,44 @@ bool Print::check_multi_filaments_compatibility(const std::vector& return true; } +bool Print::is_filaments_compatible(const std::vector& filament_types) +{ + bool has_high_temperature_filament = false; + bool has_low_temperature_filament = false; + + for (const auto& type : filament_types) { + if (type == FilamentTempType::HighTemp) + has_high_temperature_filament = true; + else if (type == FilamentTempType::LowTemp) + has_low_temperature_filament = true; + } + + if (has_high_temperature_filament && has_low_temperature_filament) + return false; + + return true; +} +int Print::get_compatible_filament_type(const std::set& filament_types) +{ + bool has_high_temperature_filament = false; + bool has_low_temperature_filament = false; + + for (const auto& type : filament_types) { + if (type == FilamentTempType::HighTemp) + has_high_temperature_filament = true; + else if (type == FilamentTempType::LowTemp) + has_low_temperature_filament = true; + } + + if (has_high_temperature_filament && has_low_temperature_filament) + return HighLowCompatible; + else if (has_high_temperature_filament) + return HighTemp; + else if (has_low_temperature_filament) + return LowTemp; + return HighLowCompatible; +} + //BBS: this function is used to check whether multi filament can be printed StringObjectException Print::check_multi_filament_valid(const Print& print) { diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 7cfb67156..1c328b67f 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -830,6 +830,15 @@ public: static FilamentTempType get_filament_temp_type(const std::string& filament_type); static int get_hrc_by_nozzle_type(const NozzleType& type); static bool check_multi_filaments_compatibility(const std::vector& filament_types); + // similar to check_multi_filaments_compatibility, but the input is int, and may be negative (means unset) + static bool is_filaments_compatible(const std::vector& types); + // get the compatible filament type of a multi-material object + // Rule: + // 1. LowTemp+HighLowCompatible=LowTemp + // 2. HighTemp++HighLowCompatible=HighTemp + // 3. LowTemp+HighTemp+...=HighLowCompatible + // Unset types are just ignored. + static int get_compatible_filament_type(const std::set& types); protected: // Invalidates the step, and its depending steps in Print. diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index 3e744c712..c9f3dc3d9 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -518,9 +518,9 @@ void ArrangeJob::process() { BOOST_LOG_TRIVIAL(warning)<< "Arrange full params: "<< params.to_json(); BOOST_LOG_TRIVIAL(info) << boost::format("arrange: items selected before arranging: %1%") % m_selected.size(); - for (auto selected : m_selected) - BOOST_LOG_TRIVIAL(debug) << selected.name << ", extruder: " << selected.extrude_ids.back() << ", bed: " << selected.bed_idx - << ", bed_temp: " << selected.first_bed_temp << ", print_temp: " << selected.print_temp; + for (auto selected : m_selected) { + BOOST_LOG_TRIVIAL(debug) << selected.name << ", extruder: " << selected.extrude_ids.back() << ", bed: " << selected.bed_idx<<", filemant_type:" << selected.filament_temp_type; + } BOOST_LOG_TRIVIAL(debug) << "items unselected before arrange: "; for (auto item : m_unselected) BOOST_LOG_TRIVIAL(debug) << item.name << ", bed: " << item.bed_idx << ", trans: " << item.translation.transpose();