1 #ifndef BOTTOMLEFT_HPP 2 #define BOTTOMLEFT_HPP 3 4 #include <limits> 5 6 #include "placer_boilerplate.hpp" 7 8 namespace libnest2d { namespace placers { 9 10 template<class T, class = T> struct DefaultEpsilon {}; 11 12 template<class T> 13 struct DefaultEpsilon<T, enable_if_t<std::is_integral<T>::value, T> > { 14 static const T Value = 1; 15 }; 16 17 template<class T> 18 struct DefaultEpsilon<T, enable_if_t<std::is_floating_point<T>::value, T> > { 19 static const T Value = 1e-3; 20 }; 21 22 template<class RawShape> 23 struct BLConfig { 24 DECLARE_MAIN_TYPES(RawShape); 25 26 Coord min_obj_distance = 0; 27 Coord epsilon = DefaultEpsilon<Coord>::Value; 28 bool allow_rotations = false; 29 }; 30 31 template<class RawShape> 32 class _BottomLeftPlacer: public PlacerBoilerplate< 33 _BottomLeftPlacer<RawShape>, 34 RawShape, _Box<TPoint<RawShape>>, 35 BLConfig<RawShape> > 36 { 37 using Base = PlacerBoilerplate<_BottomLeftPlacer<RawShape>, RawShape, 38 _Box<TPoint<RawShape>>, BLConfig<RawShape>>; 39 DECLARE_PLACER(Base) 40 41 public: 42 _BottomLeftPlacer(const BinType & bin)43 explicit _BottomLeftPlacer(const BinType& bin): Base(bin) {} 44 45 template<class Range = ConstItemRange<typename Base::DefaultIter>> trypack(Item & item,const Range &=Range ())46 PackResult trypack(Item& item, 47 const Range& = Range()) 48 { 49 auto r = _trypack(item); 50 if(!r && Base::config_.allow_rotations) { 51 52 item.rotate(Degrees(90)); 53 r =_trypack(item); 54 } 55 return r; 56 } 57 58 enum class Dir { 59 LEFT, 60 DOWN 61 }; 62 leftPoly(const Item & item) const63 inline RawShape leftPoly(const Item& item) const { 64 return toWallPoly(item, Dir::LEFT); 65 } 66 downPoly(const Item & item) const67 inline RawShape downPoly(const Item& item) const { 68 return toWallPoly(item, Dir::DOWN); 69 } 70 availableSpaceLeft(const Item & item)71 inline Coord availableSpaceLeft(const Item& item) { 72 return availableSpace(item, Dir::LEFT); 73 } 74 availableSpaceDown(const Item & item)75 inline Coord availableSpaceDown(const Item& item) { 76 return availableSpace(item, Dir::DOWN); 77 } 78 79 protected: 80 _trypack(Item & item)81 PackResult _trypack(Item& item) { 82 83 // Get initial position for item in the top right corner 84 setInitialPosition(item); 85 86 Coord d = availableSpaceDown(item); 87 auto eps = config_.epsilon; 88 bool can_move = d > eps; 89 bool can_be_packed = can_move; 90 bool left = true; 91 92 while(can_move) { 93 if(left) { // write previous down move and go down 94 item.translate({0, -d+eps}); 95 d = availableSpaceLeft(item); 96 can_move = d > eps; 97 left = false; 98 } else { // write previous left move and go down 99 item.translate({-d+eps, 0}); 100 d = availableSpaceDown(item); 101 can_move = d > eps; 102 left = true; 103 } 104 } 105 106 if(can_be_packed) { 107 Item trsh(item.transformedShape()); 108 for(auto& v : trsh) can_be_packed = can_be_packed && 109 getX(v) < bin_.width() && 110 getY(v) < bin_.height(); 111 } 112 113 return can_be_packed? PackResult(item) : PackResult(); 114 } 115 setInitialPosition(Item & item)116 void setInitialPosition(Item& item) { 117 auto bb = item.boundingBox(); 118 119 Vertex v = { getX(bb.maxCorner()), getY(bb.minCorner()) }; 120 121 122 Coord dx = getX(bin_.maxCorner()) - getX(v); 123 Coord dy = getY(bin_.maxCorner()) - getY(v); 124 125 item.translate({dx, dy}); 126 } 127 128 template<class C = Coord> 129 static enable_if_t<std::is_floating_point<C>::value, bool> isInTheWayOf(const Item & item,const Item & other,const RawShape & scanpoly)130 isInTheWayOf( const Item& item, 131 const Item& other, 132 const RawShape& scanpoly) 133 { 134 auto tsh = other.transformedShape(); 135 return ( sl::intersects(tsh, scanpoly) || 136 sl::isInside(tsh, scanpoly) ) && 137 ( !sl::intersects(tsh, item.rawShape()) && 138 !sl::isInside(tsh, item.rawShape()) ); 139 } 140 141 template<class C = Coord> 142 static enable_if_t<std::is_integral<C>::value, bool> isInTheWayOf(const Item & item,const Item & other,const RawShape & scanpoly)143 isInTheWayOf( const Item& item, 144 const Item& other, 145 const RawShape& scanpoly) 146 { 147 auto tsh = other.transformedShape(); 148 149 bool inters_scanpoly = sl::intersects(tsh, scanpoly) && 150 !sl::touches(tsh, scanpoly); 151 bool inters_item = sl::intersects(tsh, item.rawShape()) && 152 !sl::touches(tsh, item.rawShape()); 153 154 return ( inters_scanpoly || 155 sl::isInside(tsh, scanpoly)) && 156 ( !inters_item && 157 !sl::isInside(tsh, item.rawShape()) 158 ); 159 } 160 itemsInTheWayOf(const Item & item,const Dir dir)161 ItemGroup itemsInTheWayOf(const Item& item, const Dir dir) { 162 // Get the left or down polygon, that has the same area as the shadow 163 // of input item reflected to the left or downwards 164 auto&& scanpoly = dir == Dir::LEFT? leftPoly(item) : 165 downPoly(item); 166 167 ItemGroup ret; // packed items 'in the way' of item 168 ret.reserve(items_.size()); 169 170 // Predicate to find items that are 'in the way' for left (down) move 171 auto predicate = [&scanpoly, &item](const Item& it) { 172 return isInTheWayOf(item, it, scanpoly); 173 }; 174 175 // Get the items that are in the way for the left (or down) movement 176 std::copy_if(items_.begin(), items_.end(), 177 std::back_inserter(ret), predicate); 178 179 return ret; 180 } 181 availableSpace(const Item & _item,const Dir dir)182 Coord availableSpace(const Item& _item, const Dir dir) { 183 184 Item item (_item.transformedShape()); 185 186 187 std::function<Coord(const Vertex&)> getCoord; 188 std::function< std::pair<Coord, bool>(const Segment&, const Vertex&) > 189 availableDistanceSV; 190 191 std::function< std::pair<Coord, bool>(const Vertex&, const Segment&) > 192 availableDistance; 193 194 if(dir == Dir::LEFT) { 195 getCoord = [](const Vertex& v) { return getX(v); }; 196 availableDistance = pointlike::horizontalDistance<Vertex>; 197 availableDistanceSV = [](const Segment& s, const Vertex& v) { 198 auto ret = pointlike::horizontalDistance<Vertex>(v, s); 199 if(ret.second) ret.first = -ret.first; 200 return ret; 201 }; 202 } 203 else { 204 getCoord = [](const Vertex& v) { return getY(v); }; 205 availableDistance = pointlike::verticalDistance<Vertex>; 206 availableDistanceSV = [](const Segment& s, const Vertex& v) { 207 auto ret = pointlike::verticalDistance<Vertex>(v, s); 208 if(ret.second) ret.first = -ret.first; 209 return ret; 210 }; 211 } 212 213 auto&& items_in_the_way = itemsInTheWayOf(item, dir); 214 215 // Comparison function for finding min vertex 216 auto cmp = [&getCoord](const Vertex& v1, const Vertex& v2) { 217 return getCoord(v1) < getCoord(v2); 218 }; 219 220 // find minimum left or down coordinate of item 221 auto minvertex_it = std::min_element(item.begin(), 222 item.end(), 223 cmp); 224 225 // Get the initial distance in floating point 226 Coord m = getCoord(*minvertex_it); 227 228 // Check available distance for every vertex of item to the objects 229 // in the way for the nearest intersection 230 if(!items_in_the_way.empty()) { // This is crazy, should be optimized... 231 for(Item& pleft : items_in_the_way) { 232 // For all segments in items_to_left 233 234 assert(pleft.vertexCount() > 0); 235 236 auto trpleft_poly = pleft.transformedShape(); 237 auto& trpleft = sl::contour(trpleft_poly); 238 auto first = sl::begin(trpleft); 239 auto next = first + 1; 240 auto endit = sl::end(trpleft); 241 242 while(next != endit) { 243 Segment seg(*(first++), *(next++)); 244 for(auto& v : item) { // For all vertices in item 245 246 auto d = availableDistance(v, seg); 247 248 if(d.second && d.first < m) m = d.first; 249 } 250 } 251 } 252 253 auto first = item.begin(); 254 auto next = first + 1; 255 auto endit = item.end(); 256 257 // For all edges in item: 258 while(next != endit) { 259 Segment seg(*(first++), *(next++)); 260 261 // for all shapes in items_to_left 262 for(Item& sh : items_in_the_way) { 263 assert(sh.vertexCount() > 0); 264 265 Item tsh(sh.transformedShape()); 266 for(auto& v : tsh) { // For all vertices in item 267 268 auto d = availableDistanceSV(seg, v); 269 270 if(d.second && d.first < m) m = d.first; 271 } 272 } 273 } 274 } 275 276 return m; 277 } 278 279 /** 280 * Implementation of the left (and down) polygon as described by 281 * [López-Camacho et al. 2013]\ 282 * (http://www.cs.stir.ac.uk/~goc/papers/EffectiveHueristic2DAOR2013.pdf) 283 * see algorithm 8 for details... 284 */ toWallPoly(const Item & _item,const Dir dir) const285 RawShape toWallPoly(const Item& _item, const Dir dir) const { 286 // The variable names reflect the case of left polygon calculation. 287 // 288 // We will iterate through the item's vertices and search for the top 289 // and bottom vertices (or right and left if dir==Dir::DOWN). 290 // Save the relevant vertices and their indices into `bottom` and 291 // `top` vectors. In case of left polygon construction these will 292 // contain the top and bottom polygons which have the same vertical 293 // coordinates (in case there is more of them). 294 // 295 // We get the leftmost (or downmost) vertex from the `bottom` and `top` 296 // vectors and construct the final polygon. 297 298 Item item (_item.transformedShape()); 299 300 auto getCoord = [dir](const Vertex& v) { 301 return dir == Dir::LEFT? getY(v) : getX(v); 302 }; 303 304 Coord max_y = std::numeric_limits<Coord>::min(); 305 Coord min_y = std::numeric_limits<Coord>::max(); 306 307 using El = std::pair<size_t, std::reference_wrapper<const Vertex>>; 308 309 std::function<bool(const El&, const El&)> cmp; 310 311 if(dir == Dir::LEFT) 312 cmp = [](const El& e1, const El& e2) { 313 return getX(e1.second.get()) < getX(e2.second.get()); 314 }; 315 else 316 cmp = [](const El& e1, const El& e2) { 317 return getY(e1.second.get()) < getY(e2.second.get()); 318 }; 319 320 std::vector< El > top; 321 std::vector< El > bottom; 322 323 size_t idx = 0; 324 for(auto& v : item) { // Find the bottom and top vertices and save them 325 auto vref = std::cref(v); 326 auto vy = getCoord(v); 327 328 if( vy > max_y ) { 329 max_y = vy; 330 top.clear(); 331 top.emplace_back(idx, vref); 332 } 333 else if(vy == max_y) { top.emplace_back(idx, vref); } 334 335 if(vy < min_y) { 336 min_y = vy; 337 bottom.clear(); 338 bottom.emplace_back(idx, vref); 339 } 340 else if(vy == min_y) { bottom.emplace_back(idx, vref); } 341 342 idx++; 343 } 344 345 // Get the top and bottom leftmost vertices, or the right and left 346 // downmost vertices (if dir == Dir::DOWN) 347 auto topleft_it = std::min_element(top.begin(), top.end(), cmp); 348 auto bottomleft_it = 349 std::min_element(bottom.begin(), bottom.end(), cmp); 350 351 auto& topleft_vertex = topleft_it->second.get(); 352 auto& bottomleft_vertex = bottomleft_it->second.get(); 353 354 // Start and finish positions for the vertices that will be part of the 355 // new polygon 356 auto start = std::min(topleft_it->first, bottomleft_it->first); 357 auto finish = std::max(topleft_it->first, bottomleft_it->first); 358 359 RawShape ret; 360 361 // the return shape 362 auto& rsh = sl::contour(ret); 363 364 // reserve for all vertices plus 2 for the left horizontal wall, 2 for 365 // the additional vertices for maintaning min object distance 366 sl::reserve(rsh, finish-start+4); 367 368 /*auto addOthers = [&rsh, finish, start, &item](){ 369 for(size_t i = start+1; i < finish; i++) 370 sl::addVertex(rsh, item.vertex(i)); 371 };*/ 372 373 auto reverseAddOthers = [&rsh, finish, start, &item](){ 374 for(auto i = finish-1; i > start; i--) 375 sl::addVertex(rsh, item.vertex( 376 static_cast<unsigned long>(i))); 377 }; 378 379 // Final polygon construction... 380 381 static_assert(OrientationType<RawShape>::Value == 382 Orientation::CLOCKWISE, 383 "Counter clockwise toWallPoly() Unimplemented!"); 384 385 // Clockwise polygon construction 386 387 sl::addVertex(rsh, topleft_vertex); 388 389 if(dir == Dir::LEFT) reverseAddOthers(); 390 else { 391 sl::addVertex(rsh, getX(topleft_vertex), 0); 392 sl::addVertex(rsh, getX(bottomleft_vertex), 0); 393 } 394 395 sl::addVertex(rsh, bottomleft_vertex); 396 397 if(dir == Dir::LEFT) { 398 sl::addVertex(rsh, 0, getY(bottomleft_vertex)); 399 sl::addVertex(rsh, 0, getY(topleft_vertex)); 400 } 401 else reverseAddOthers(); 402 403 404 // Close the polygon 405 sl::addVertex(rsh, topleft_vertex); 406 407 return ret; 408 } 409 410 }; 411 412 } 413 } 414 415 #endif //BOTTOMLEFT_HPP 416