Partial arrange starts to work again.

This commit is contained in:
tamasmeszaros 2019-07-02 15:24:40 +02:00
parent 914bf63228
commit 87c5e9bbaa
4 changed files with 123 additions and 278 deletions

View file

@ -875,6 +875,28 @@ inline _Box<TPoint<S>> boundingBox(const S& sh)
return boundingBox(sh, Tag<S>() ); return boundingBox(sh, Tag<S>() );
} }
template<class P> _Box<P> boundingBox(const _Box<P>& bb1, const _Box<P>& bb2 )
{
auto& pminc = bb1.minCorner();
auto& pmaxc = bb1.maxCorner();
auto& iminc = bb2.minCorner();
auto& imaxc = bb2.maxCorner();
P minc, maxc;
setX(minc, std::min(getX(pminc), getX(iminc)));
setY(minc, std::min(getY(pminc), getY(iminc)));
setX(maxc, std::max(getX(pmaxc), getX(imaxc)));
setY(maxc, std::max(getY(pmaxc), getY(imaxc)));
return _Box<P>(minc, maxc);
}
template<class S1, class S2>
_Box<TPoint<S1>> boundingBox(const S1 &s1, const S2 &s2)
{
return boundingBox(boundingBox(s1), boundingBox(s2));
}
template<class Box> template<class Box>
inline double area(const Box& box, const BoxTag& ) inline double area(const Box& box, const BoxTag& )
{ {
@ -1060,8 +1082,8 @@ template<class TB, class TC>
inline bool isInside(const TB& box, const TC& circ, inline bool isInside(const TB& box, const TC& circ,
const BoxTag&, const CircleTag&) const BoxTag&, const CircleTag&)
{ {
return isInside(box.minCorner(), circ, BoxTag(), CircleTag()) && return isInside(box.minCorner(), circ, PointTag(), CircleTag()) &&
isInside(box.maxCorner(), circ, BoxTag(), CircleTag()); isInside(box.maxCorner(), circ, PointTag(), CircleTag());
} }
template<class TBGuest, class TBHost> template<class TBGuest, class TBHost>

View file

@ -895,7 +895,10 @@ private:
template<class TIter> inline void __execute(TIter from, TIter to) template<class TIter> inline void __execute(TIter from, TIter to)
{ {
if(min_obj_distance_ > 0) std::for_each(from, to, [this](Item& item) { if(min_obj_distance_ > 0) std::for_each(from, to, [this](Item& item) {
item.addOffset(static_cast<Coord>(std::ceil(min_obj_distance_/2.0))); auto offs = min_obj_distance_;
if (item.isFixed()) offs *= 0.99;
item.addOffset(static_cast<Coord>(std::ceil(offs/2.0)));
}); });
selector_.template packItems<PlacementStrategy>( selector_.template packItems<PlacementStrategy>(

View file

@ -801,7 +801,6 @@ private:
// optimize // optimize
config_.object_function = prev_func; config_.object_function = prev_func;
} }
} }
struct Optimum { struct Optimum {
@ -816,29 +815,14 @@ private:
class Optimizer: public opt::TOptimizer<opt::Method::L_SUBPLEX> { class Optimizer: public opt::TOptimizer<opt::Method::L_SUBPLEX> {
public: public:
Optimizer() { Optimizer(float accuracy = 1.f) {
opt::StopCriteria stopcr; opt::StopCriteria stopcr;
stopcr.max_iterations = 200; stopcr.max_iterations = unsigned(std::floor(1000 * accuracy));
stopcr.relative_score_difference = 1e-20; stopcr.relative_score_difference = 1e-20;
this->stopcr_ = stopcr; this->stopcr_ = stopcr;
} }
}; };
static Box boundingBox(const Box& pilebb, const Box& ibb ) {
auto& pminc = pilebb.minCorner();
auto& pmaxc = pilebb.maxCorner();
auto& iminc = ibb.minCorner();
auto& imaxc = ibb.maxCorner();
Vertex minc, maxc;
setX(minc, std::min(getX(pminc), getX(iminc)));
setY(minc, std::min(getY(pminc), getY(iminc)));
setX(maxc, std::max(getX(pmaxc), getX(imaxc)));
setY(maxc, std::max(getY(pmaxc), getY(imaxc)));
return Box(minc, maxc);
}
using Edges = EdgeCache<RawShape>; using Edges = EdgeCache<RawShape>;
template<class Range = ConstItemRange<typename Base::DefaultIter>> template<class Range = ConstItemRange<typename Base::DefaultIter>>
@ -935,7 +919,7 @@ private:
_objfunc = [norm, binbb, pbb, ins_check](const Item& item) _objfunc = [norm, binbb, pbb, ins_check](const Item& item)
{ {
auto ibb = item.boundingBox(); auto ibb = item.boundingBox();
auto fullbb = boundingBox(pbb, ibb); auto fullbb = sl::boundingBox(pbb, ibb);
double score = pl::distance(ibb.center(), double score = pl::distance(ibb.center(),
binbb.center()); binbb.center());
@ -1005,14 +989,15 @@ private:
auto& rofn = rawobjfunc; auto& rofn = rawobjfunc;
auto& nfpoint = getNfpPoint; auto& nfpoint = getNfpPoint;
float accuracy = config_.accuracy;
__parallel::enumerate( __parallel::enumerate(
cache.corners().begin(), cache.corners().begin(),
cache.corners().end(), cache.corners().end(),
[&results, &item, &rofn, &nfpoint, ch] [&results, &item, &rofn, &nfpoint, ch, accuracy]
(double pos, size_t n) (double pos, size_t n)
{ {
Optimizer solver; Optimizer solver(accuracy);
Item itemcpy = item; Item itemcpy = item;
auto contour_ofn = [&rofn, &nfpoint, ch, &itemcpy] auto contour_ofn = [&rofn, &nfpoint, ch, &itemcpy]
@ -1059,10 +1044,10 @@ private:
__parallel::enumerate(cache.corners(hidx).begin(), __parallel::enumerate(cache.corners(hidx).begin(),
cache.corners(hidx).end(), cache.corners(hidx).end(),
[&results, &item, &nfpoint, [&results, &item, &nfpoint,
&rofn, ch, hidx] &rofn, ch, hidx, accuracy]
(double pos, size_t n) (double pos, size_t n)
{ {
Optimizer solver; Optimizer solver(accuracy);
Item itmcpy = item; Item itmcpy = item;
auto hole_ofn = auto hole_ofn =

View file

@ -65,89 +65,6 @@ using Segment = _Segment<clppr::IntPoint>;
using MultiPolygon = TMultiShape<clppr::Polygon>; using MultiPolygon = TMultiShape<clppr::Polygon>;
using PackGroup = _PackGroup<clppr::Polygon>; using PackGroup = _PackGroup<clppr::Polygon>;
// Only for debugging. Prints the model object vertices on stdout.
//std::string toString(const Model& model, bool holes = true) {
// std::stringstream ss;
// ss << "{\n";
// for(auto objptr : model.objects) {
// if(!objptr) continue;
// auto rmesh = objptr->raw_mesh();
// for(auto objinst : objptr->instances) {
// if(!objinst) continue;
// Slic3r::TriangleMesh tmpmesh = rmesh;
// // CHECK_ME -> Is the following correct ?
// tmpmesh.scale(objinst->get_scaling_factor());
// objinst->transform_mesh(&tmpmesh);
// ExPolygons expolys = tmpmesh.horizontal_projection();
// for(auto& expoly_complex : expolys) {
// ExPolygons tmp = expoly_complex.simplify(scaled<double>(1.));
// if(tmp.empty()) continue;
// ExPolygon expoly = tmp.front();
// expoly.contour.make_clockwise();
// for(auto& h : expoly.holes) h.make_counter_clockwise();
// ss << "\t{\n";
// ss << "\t\t{\n";
// for(auto v : expoly.contour.points) ss << "\t\t\t{"
// << v(0) << ", "
// << v(1) << "},\n";
// {
// auto v = expoly.contour.points.front();
// ss << "\t\t\t{" << v(0) << ", " << v(1) << "},\n";
// }
// ss << "\t\t},\n";
// // Holes:
// ss << "\t\t{\n";
// if(holes) for(auto h : expoly.holes) {
// ss << "\t\t\t{\n";
// for(auto v : h.points) ss << "\t\t\t\t{"
// << v(0) << ", "
// << v(1) << "},\n";
// {
// auto v = h.points.front();
// ss << "\t\t\t\t{" << v(0) << ", " << v(1) << "},\n";
// }
// ss << "\t\t\t},\n";
// }
// ss << "\t\t},\n";
// ss << "\t},\n";
// }
// }
// }
// ss << "}\n";
// return ss.str();
//}
// Debugging: Save model to svg file.
//void toSVG(SVG& svg, const Model& model) {
// for(auto objptr : model.objects) {
// if(!objptr) continue;
// auto rmesh = objptr->raw_mesh();
// for(auto objinst : objptr->instances) {
// if(!objinst) continue;
// Slic3r::TriangleMesh tmpmesh = rmesh;
// tmpmesh.scale(objinst->get_scaling_factor());
// objinst->transform_mesh(&tmpmesh);
// ExPolygons expolys = tmpmesh.horizontal_projection();
// svg.draw(expolys);
// }
// }
//}
namespace bgi = boost::geometry::index; namespace bgi = boost::geometry::index;
using SpatElement = std::pair<Box, unsigned>; using SpatElement = std::pair<Box, unsigned>;
@ -156,21 +73,6 @@ using ItemGroup = std::vector<std::reference_wrapper<Item>>;
const double BIG_ITEM_TRESHOLD = 0.02; const double BIG_ITEM_TRESHOLD = 0.02;
Box boundingBox(const Box& pilebb, const Box& ibb ) {
auto& pminc = pilebb.minCorner();
auto& pmaxc = pilebb.maxCorner();
auto& iminc = ibb.minCorner();
auto& imaxc = ibb.maxCorner();
PointImpl minc, maxc;
setX(minc, std::min(getX(pminc), getX(iminc)));
setY(minc, std::min(getY(pminc), getY(iminc)));
setX(maxc, std::max(getX(pmaxc), getX(imaxc)));
setY(maxc, std::max(getY(pmaxc), getY(imaxc)));
return Box(minc, maxc);
}
// Fill in the placer algorithm configuration with values carefully chosen for // Fill in the placer algorithm configuration with values carefully chosen for
// Slic3r. // Slic3r.
template<class PConf> template<class PConf>
@ -194,13 +96,18 @@ void fillConfig(PConf& pcfg) {
pcfg.parallel = true; pcfg.parallel = true;
} }
// Type trait for an arranger class for different bin types (box, circle, // Apply penality to object function result. This is used only when alignment
// polygon, etc...) // after arrange is explicitly disabled (PConfig::Alignment::DONT_ALIGN)
//template<class TBin> double fixed_overfit(const std::tuple<double, Box>& result, const Box &binbb)
//class AutoArranger {}; {
double score = std::get<0>(result);
template<class Bin> clppr::IntPoint center(const Bin& bin) { return bin.center(); } Box pilebb = std::get<1>(result);
template<> clppr::IntPoint center(const clppr::Polygon &bin) { return sl::boundingBox(bin).center(); } Box fullbb = sl::boundingBox(pilebb, binbb);
double diff = fullbb.area() - binbb.area();
if(diff > 0) score += diff;
return score;
}
// A class encapsulating the libnest2d Nester class and extending it with other // A class encapsulating the libnest2d Nester class and extending it with other
// management and spatial index structures for acceleration. // management and spatial index structures for acceleration.
@ -218,8 +125,7 @@ protected:
Packer m_pck; Packer m_pck;
PConfig m_pconf; // Placement configuration PConfig m_pconf; // Placement configuration
TBin m_bin; TBin m_bin;
double m_bin_area; // caching double m_bin_area;
PointImpl m_bincenter; // caching
SpatIndex m_rtree; // spatial index for the normal (bigger) objects SpatIndex m_rtree; // spatial index for the normal (bigger) objects
SpatIndex m_smallsrtree; // spatial index for only the smaller items SpatIndex m_smallsrtree; // spatial index for only the smaller items
double m_norm; // A coefficient to scale distances double m_norm; // A coefficient to scale distances
@ -234,7 +140,7 @@ protected:
// as it possibly can be but at the same time, it has to provide // as it possibly can be but at the same time, it has to provide
// reasonable results. // reasonable results.
std::tuple<double /*score*/, Box /*farthest point from bin center*/> std::tuple<double /*score*/, Box /*farthest point from bin center*/>
objfunc(const Item &item ) objfunc(const Item &item, const clppr::IntPoint &bincenter)
{ {
const double bin_area = m_bin_area; const double bin_area = m_bin_area;
const SpatIndex& spatindex = m_rtree; const SpatIndex& spatindex = m_rtree;
@ -250,7 +156,7 @@ protected:
auto ibb = sl::boundingBox(item.transformedShape()); auto ibb = sl::boundingBox(item.transformedShape());
// Calculate the full bounding box of the pile with the candidate item // Calculate the full bounding box of the pile with the candidate item
auto fullbb = boundingBox(m_pilebb, ibb); auto fullbb = sl::boundingBox(m_pilebb, ibb);
// The bounding box of the big items (they will accumulate in the center // The bounding box of the big items (they will accumulate in the center
// of the pile // of the pile
@ -286,7 +192,7 @@ protected:
// The smalles distance from the arranged pile center: // The smalles distance from the arranged pile center:
double dist = *(std::min_element(dists.begin(), dists.end())) / m_norm; double dist = *(std::min_element(dists.begin(), dists.end())) / m_norm;
double bindist = pl::distance(ibb.center(), m_bincenter) / m_norm; double bindist = pl::distance(ibb.center(), bincenter) / m_norm;
dist = 0.8*dist + 0.2*bindist; dist = 0.8*dist + 0.2*bindist;
// Density is the pack density: how big is the arranged pile // Density is the pack density: how big is the arranged pile
@ -334,7 +240,7 @@ protected:
Item& p = m_items[idx]; Item& p = m_items[idx];
auto parea = p.area(); auto parea = p.area();
if(std::abs(1.0 - parea/item.area()) < 1e-6) { if(std::abs(1.0 - parea/item.area()) < 1e-6) {
auto bb = boundingBox(p.boundingBox(), ibb); auto bb = sl::boundingBox(p.boundingBox(), ibb);
auto bbarea = bb.area(); auto bbarea = bb.area();
auto ascore = 1.0 - (item.area() + parea)/bbarea; auto ascore = 1.0 - (item.area() + parea)/bbarea;
@ -370,9 +276,7 @@ public:
std::function<bool(void)> stopcond) std::function<bool(void)> stopcond)
: m_pck(bin, dist) : m_pck(bin, dist)
, m_bin(bin) , m_bin(bin)
, m_bin_area(sl::area(bin)) , m_norm(std::sqrt(sl::area(bin)))
, m_bincenter(center(bin))
, m_norm(std::sqrt(m_bin_area))
{ {
fillConfig(m_pconf); fillConfig(m_pconf);
@ -391,10 +295,10 @@ public:
m_rtree.clear(); m_rtree.clear();
m_smallsrtree.clear(); m_smallsrtree.clear();
// We will treat big items (compared to the print bed) differently // We will treat big items (compared to the print bed) differently
auto isBig = [this](double a) { auto isBig = [this](double a) {
return a/m_bin_area > BIG_ITEM_TRESHOLD ; return a / m_bin_area > BIG_ITEM_TRESHOLD ;
}; };
for(unsigned idx = 0; idx < items.size(); ++idx) { for(unsigned idx = 0; idx < items.size(); ++idx) {
@ -419,8 +323,11 @@ public:
inline void preload(std::vector<Item>& fixeditems) { inline void preload(std::vector<Item>& fixeditems) {
m_pconf.alignment = PConfig::Alignment::DONT_ALIGN; m_pconf.alignment = PConfig::Alignment::DONT_ALIGN;
// m_pconf.object_function = nullptr; // drop the special objectfunction auto bb = sl::boundingBox(m_bin);
// m_pck.preload(pg); auto bbcenter = bb.center();
m_pconf.object_function = [this, bb, bbcenter](const Item &item) {
return fixed_overfit(objfunc(item, bbcenter), bb);
};
// Build the rtree for queries to work // Build the rtree for queries to work
@ -442,34 +349,39 @@ public:
} }
}; };
template<> std::function<double(const Item&)> AutoArranger<Box>::get_objfn() template<> std::function<double(const Item&)> AutoArranger<Box>::get_objfn()
{ {
return [this](const Item &itm) { auto bincenter = m_bin.center();
auto result = objfunc(itm);
return [this, bincenter](const Item &itm) {
auto result = objfunc(itm, bincenter);
double score = std::get<0>(result); double score = std::get<0>(result);
auto& fullbb = std::get<1>(result); auto& fullbb = std::get<1>(result);
double miss = Placer::overfit(fullbb, m_bin); double miss = Placer::overfit(fullbb, m_bin);
miss = miss > 0? miss : 0; miss = miss > 0? miss : 0;
score += miss*miss; score += miss*miss;
return score; return score;
}; };
} }
template<> std::function<double(const Item&)> AutoArranger<Circle>::get_objfn() template<> std::function<double(const Item&)> AutoArranger<Circle>::get_objfn()
{ {
return [this](const Item &item) { auto bincenter = m_bin.center();
return [this, bincenter](const Item &item) {
auto result = objfunc(item, bincenter);
auto result = objfunc(item);
double score = std::get<0>(result); double score = std::get<0>(result);
auto isBig = [this](const Item& itm) { auto isBig = [this](const Item& itm) {
return itm.area()/m_bin_area > BIG_ITEM_TRESHOLD ; return itm.area() / m_bin_area > BIG_ITEM_TRESHOLD ;
}; };
if(isBig(item)) { if(isBig(item)) {
auto mp = m_merged_pile; auto mp = m_merged_pile;
mp.push_back(item.transformedShape()); mp.push_back(item.transformedShape());
@ -478,109 +390,26 @@ template<> std::function<double(const Item&)> AutoArranger<Circle>::get_objfn()
if(miss < 0) miss = 0; if(miss < 0) miss = 0;
score += miss*miss; score += miss*miss;
} }
return score; return score;
}; };
} }
template<> std::function<double(const Item&)> AutoArranger<clppr::Polygon>::get_objfn() // Specialization for a generalized polygon.
// Warning: this is unfinished business. It may or may not work.
template<>
std::function<double(const Item &)> AutoArranger<clppr::Polygon>::get_objfn()
{ {
return [this] (const Item &item) { return std::get<0>(objfunc(item)); }; auto bincenter = sl::boundingBox(m_bin).center();
return [this, bincenter](const Item &item) {
return std::get<0>(objfunc(item, bincenter));
};
} }
// Arranger specialization for a Box shaped bin.
//template<> class AutoArranger<Box>: public _ArrBase<Box> {
//public:
// AutoArranger(const Box& bin, Distance dist,
// std::function<void(unsigned)> progressind = [](unsigned){},
// std::function<bool(void)> stopcond = [](){return false;}):
// _ArrBase<Box>(bin, dist, progressind, stopcond)
// {
// // Here we set up the actual object function that calls the common
// // object function for all bin shapes than does an additional inside
// // check for the arranged pile.
// m_pconf.object_function = [this, bin](const Item &item) {
// auto result = objfunc(bin.center(), item);
// double score = std::get<0>(result);
// auto& fullbb = std::get<1>(result);
// double miss = Placer::overfit(fullbb, bin);
// miss = miss > 0? miss : 0;
// score += miss*miss;
// return score;
// };
// m_pck.configure(m_pconf);
// }
//};
inline Circle to_lnCircle(const CircleBed& circ) { inline Circle to_lnCircle(const CircleBed& circ) {
return Circle({circ.center()(0), circ.center()(1)}, circ.radius()); return Circle({circ.center()(0), circ.center()(1)}, circ.radius());
} }
//// Arranger specialization for circle shaped bin.
//template<> class AutoArranger<Circle>: public _ArrBase<Circle> {
//public:
// AutoArranger(const Circle& bin, Distance dist,
// std::function<void(unsigned)> progressind = [](unsigned){},
// std::function<bool(void)> stopcond = [](){return false;}):
// _ArrBase<Circle>(bin, dist, progressind, stopcond) {
// // As with the box, only the inside check is different.
// m_pconf.object_function = [this, &bin](const Item &item) {
// auto result = objfunc(bin.center(), item);
// double score = std::get<0>(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());
// auto chull = sl::convexHull(mp);
// double miss = Placer::overfit(chull, bin);
// if(miss < 0) miss = 0;
// score += miss*miss;
// }
// return score;
// };
// m_pck.configure(m_pconf);
// }
//};
// Arranger specialization for a generalized polygon.
// Warning: this is unfinished business. It may or may not work.
//template<> class AutoArranger<PolygonImpl>: public _ArrBase<PolygonImpl> {
//public:
// AutoArranger(const PolygonImpl& bin, Distance dist,
// std::function<void(unsigned)> progressind = [](unsigned){},
// std::function<bool(void)> stopcond = [](){return false;}):
// _ArrBase<PolygonImpl>(bin, dist, progressind, stopcond)
// {
// m_pconf.object_function = [this, &bin] (const Item &item) {
// auto binbb = sl::boundingBox(bin);
// auto result = objfunc(binbb.center(), item);
// double score = std::get<0>(result);
// return score;
// };
// m_pck.configure(m_pconf);
// }
//};
// Get the type of bed geometry from a simple vector of points. // Get the type of bed geometry from a simple vector of points.
BedShapeHint bedShape(const Polyline &bed) { BedShapeHint bedShape(const Polyline &bed) {
BedShapeHint ret; BedShapeHint ret;
@ -670,40 +499,46 @@ PackGroup _arrange(std::vector<Item> & shapes,
{ {
AutoArranger<BinT> arranger{bin, minobjd, prind, stopfn}; AutoArranger<BinT> arranger{bin, minobjd, prind, stopfn};
for(auto it = excludes.begin(); it != excludes.end(); ++it) auto it = excludes.begin();
if (!sl::isInside(it->transformedShape(), bin)) while (it != excludes.end())
it = excludes.erase(it); sl::isInside(it->transformedShape(), bin) ?
++it : it = excludes.erase(it);
// If there is something on the plate
if(!excludes.empty()) {
// arranger.preload(preshapes);
auto binbb = sl::boundingBox(bin);
// Try to put the first item to the center, as the arranger will not
// do this for us.
for (auto it = shapes.begin(); it != shapes.end(); ++it) {
Item &itm = *it;
auto ibb = itm.boundingBox();
auto d = binbb.center() - ibb.center();
itm.translate(d);
if (!arranger.is_colliding(itm)) { // If there is something on the plate
itm.markAsFixed(); if(!excludes.empty())
// arranger.preload({{itm}}); {
arranger.preload(excludes);
// Write the transformation data into the item. The callback auto binbb = sl::boundingBox(bin);
// was set on the instantiation of Item and calls the
// Arrangeable interface. // Try to put the first item to the center, as the arranger
it->callApplyFunction(0); // will not do this for us.
for (auto it = shapes.begin(); it != shapes.end(); ++it) {
// Remove this item, as it is arranged now Item &itm = *it;
it = shapes.erase(it); auto ibb = itm.boundingBox();
break; auto d = binbb.center() - ibb.center();
itm.translate(d);
if (!arranger.is_colliding(itm)) {
itm.markAsFixed();
// Write the transformation data into the item. The
// callback was set on the instantiation of Item and
// calls the Arrangeable interface.
it->callApplyFunction(0);
// Remove this item, as it is arranged now
it = shapes.erase(it);
break;
}
} }
} }
}
std::vector<std::reference_wrapper<Item>> inp;
inp.reserve(shapes.size() + excludes.size());
for (auto &itm : shapes ) inp.emplace_back(itm);
for (auto &itm : excludes) inp.emplace_back(itm);
return arranger(shapes.begin(), shapes.end()); return arranger(inp.begin(), inp.end());
} }
inline SLIC3R_CONSTEXPR coord_t stride_padding(coord_t w) inline SLIC3R_CONSTEXPR coord_t stride_padding(coord_t w)
@ -713,8 +548,8 @@ inline SLIC3R_CONSTEXPR coord_t stride_padding(coord_t w)
// The final client function for arrangement. A progress indicator and // The final client function for arrangement. A progress indicator and
// a stop predicate can be also be passed to control the process. // a stop predicate can be also be passed to control the process.
bool arrange(ArrangeablePtrs & arrangables, bool arrange(ArrangeablePtrs & arrangables,
const ArrangeablePtrs & excludes, const ArrangeablePtrs & excludes,
coord_t min_obj_distance, coord_t min_obj_distance,
const BedShapeHint & bedhint, const BedShapeHint & bedhint,
std::function<void(unsigned)> progressind, std::function<void(unsigned)> progressind,