///|/ Copyright (c) Prusa Research 2023 Tomáš Mészáros @tamasmeszaros ///|/ ///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher ///|/ #ifndef ARRANGEBASE_HPP #define ARRANGEBASE_HPP #include #include #include #include #include namespace Slic3r { namespace arr2 { namespace detail_is_const_it { template struct IsConstIt_ { static constexpr bool value = false; }; template using iterator_category_t = typename std::iterator_traits::iterator_category; template using iterator_reference_t = typename std::iterator_traits::reference; template struct IsConstIt_ >> > { static constexpr bool value = std::is_const_v>>; }; } // namespace detail_is_const_it template static constexpr bool IsConstIterator = detail_is_const_it::IsConstIt_::value; template constexpr bool is_const_iterator(const It &it) noexcept { return IsConstIterator; } // The pack() function will use tag dispatching, based on the given strategy // object that is used as its first argument. // This tag is derived for a packing strategy as default, and will be used // to cast a compile error. struct UnimplementedPacking {}; // PackStrategyTag_ needs to be specialized for any valid packing strategy class template struct PackStrategyTag_ { using Tag = UnimplementedPacking; }; // Helper metafunc to derive packing strategy tag from a strategy object. template using PackStrategyTag = typename PackStrategyTag_>::Tag; template struct PackStrategyTraits_ { template using Context = DefaultPackingContext; template static Context create_context(PackStrategy &ps, const Bed &bed, int bed_index) { return {}; } }; template using PackStrategyTraits = PackStrategyTraits_>; template using PackStrategyContext = typename PackStrategyTraits::template Context>; template PackStrategyContext create_context(PackStrategy &&ps, const Bed &bed, int bed_index) { return PackStrategyTraits::template create_context< StripCVRef>(ps, bed, bed_index); } // Function to pack one item into a bed. // strategy parameter holds clue to what packing strategy to use. This function // needs to be overloaded for the strategy tag belonging to the given // strategy. // 'bed' parameter is the type of bed into which the new item should be packed. // See beds.hpp for valid bed classes. // 'item' parameter is the item to be packed. After succesful arrangement // (see return value) the item will have it's translation and rotation // set correctly. If the function returns false, the translation and // rotation of the input item might be changed to arbitrary values. // 'fixed_items' paramter holds a range of ArrItem type objects that are already // on the bed and need to be avoided by the newly packed item. // 'remaining_items' is a range of ArrItem type objects that are intended to be // packed in the future. This information can be leveradged by // the packing strategy to make more intelligent placement // decisions for the input item. template bool pack(Strategy &&strategy, const Bed &bed, ArrItem &item, const PackStrategyContext &context, const Range &remaining_items) { static_assert(IsConstIterator, "Remaining item iterator is not const!"); // Dispatch: return pack(std::forward(strategy), bed, item, context, remaining_items, PackStrategyTag{}); } // Overload without fixed items: template bool pack(Strategy &&strategy, const Bed &bed, ArrItem &item) { std::vector dummy; auto context = create_context(strategy, bed, PhysicalBedId); return pack(std::forward(strategy), bed, item, context, crange(dummy)); } // Overload when strategy is unkown, yields compile error: template bool pack(Strategy &&strategy, const Bed &bed, ArrItem &item, const PackStrategyContext &context, const Range &remaining_items, const UnimplementedPacking &) { static_assert(always_false::value, "Packing unimplemented for this placement strategy"); return false; } // Helper function to remove unpackable items from the input container. template void remove_unpackable_items(PackStrategy &&ps, Container &c, const Bed &bed, const StopCond &stopcond) { // Safety test: try to pack each item into an empty bed. If it fails // then it should be removed from the list auto it = c.begin(); while (it != c.end() && !stopcond()) { StripCVRef &itm = *it; auto cpy{itm}; if (!pack(ps, bed, cpy)) { set_bed_index(itm, Unarranged); it = c.erase(it); } else it++; } } // arrange() function will use tag dispatching based on the selection strategy // given as its first argument. // This tag is derived for a selection strategy as default, and will be used // to cast a compile error. struct UnimplementedSelection {}; // SelStrategyTag_ needs to be specialized for any valid selection strategy class template struct SelStrategyTag_ { using Tag = UnimplementedSelection; }; // Helper metafunc to derive the selection strategy tag from a strategy object. template using SelStrategyTag = typename SelStrategyTag_>::Tag; // Main function to start the arrangement. Takes a selection and a packing // strategy object as the first two parameters. An implementation // (function overload) must exist for this function that takes the coresponding // selection strategy tag belonging to the given selstrategy argument. // // items parameter is a range of arrange items to arrange. // fixed parameter is a range of arrange items that have fixed position and will // not move during the arrangement but need to be avoided by the // moving items. // bed parameter is the type of bed into which the items need to fit. template void arrange(SelectionStrategy &&selstrategy, PackStrategy &&packingstrategy, const Range &items, const Range &fixed, const TBed &bed) { static_assert(IsConstIterator, "Fixed item iterator is not const!"); // Dispatch: arrange(std::forward(selstrategy), std::forward(packingstrategy), items, fixed, bed, SelStrategyTag{}); } template void arrange(SelectionStrategy &&selstrategy, PackStrategy &&packingstrategy, const Range &items, const TBed &bed) { std::vector::value_type> dummy; arrange(std::forward(selstrategy), std::forward(packingstrategy), items, crange(dummy), bed); } // Overload for unimplemented selection strategy, yields compile error: template void arrange(SelectionStrategy &&selstrategy, PackStrategy &&packingstrategy, const Range &items, const Range &fixed, const TBed &bed, const UnimplementedSelection &) { static_assert(always_false::value, "Arrange unimplemented for this selection strategy"); } template std::vector get_bed_indices(const Range &items) { auto bed_indices = reserve_vector(items.size()); for (auto &itm : items) bed_indices.emplace_back(get_bed_index(itm)); std::sort(bed_indices.begin(), bed_indices.end()); auto endit = std::unique(bed_indices.begin(), bed_indices.end()); bed_indices.erase(endit, bed_indices.end()); return bed_indices; } template std::vector get_bed_indices(const Range &items, const Range &fixed) { std::vector ret; auto iitems = get_bed_indices(items); auto ifixed = get_bed_indices(fixed); ret.reserve(std::max(iitems.size(), ifixed.size())); std::set_union(iitems.begin(), iitems.end(), ifixed.begin(), ifixed.end(), std::back_inserter(ret)); return ret; } template size_t get_bed_count(const Range &items) { return get_bed_indices(items).size(); } template int get_max_bed_index(const Range &items) { auto it = std::max_element(items.begin(), items.end(), [](auto &i1, auto &i2) { return get_bed_index(i1) < get_bed_index(i2); }); int ret = Unarranged; if (it != items.end()) ret = get_bed_index(*it); return ret; } struct DefaultStopCondition { constexpr bool operator()() const noexcept { return false; } }; }} // namespace Slic3r::arr2 #endif // ARRANGEBASE_HPP