Option<Rect> sb::Guillotine::tryPack(const Vec2& r, sb::Guillotine::Context& c) { if ((c.packedArea + r.area()) > c.totalArea) return nullptr; Option<Rect> previousF = nullptr; for(auto& freeF : c.freeRectangles) { //Fits? if (freeF.containsRect(newRect(freeF.bottomLeft(), r))) { if (previousF && isBestFit(c.FitPolicy, freeF, previousF.value(), r)) previousF = freeF; else if (!previousF) previousF = freeF; } } if (previousF == nullptr) //rectangle can't be packed return nullptr; else { c.packedRectangles.push_back(newRect(previousF.value().bottomLeft(), r)); c.freeRectangles.erase(previousF.value()); Rect r1, r2; performSplit(previousF.value(), r, bestSplit(c.SplitPolicy, previousF.value(), r), r1, r2); if (r1.area() != 0.0) c.freeRectangles.insert(r1); if (r2.area() != 0.0) c.freeRectangles.insert(r2); c.packedArea += r.area(); return newRect(previousF.value().bottomLeft(), r); } }
void sb::MaxRects::initializeContext(Context* c, const Vec2& freeRectangle, FitPolicy FitPolicy) { c->totalArea = freeRectangle.area(); c->packedArea = 0.0; c->fp = FitPolicy; c->freeRectangles.clear(); c->freeRectangles.insert(newRect(Vec2::zero, freeRectangle)); c->packedRectangles.clear(); }
void sb::Guillotine::initializeContext(sb::Guillotine::Context* ctx, const Vec2& freeRectangle, FitPolicy FitPolicy, SplitPolicy SplitPolicy) { assert(ctx); ctx->totalArea = freeRectangle.area(); ctx->packedArea = 0; ctx->FitPolicy = FitPolicy; ctx->SplitPolicy = SplitPolicy; ctx->freeRectangles.clear(); ctx->freeRectangles.insert(newRect(Vec2::zero, freeRectangle)); ctx->packedRectangles.clear(); }
void sb::Skyline::initializeContext(Context* c, const Vec2& freeRectangle, bool wasteImprovement) { c->totalArea = freeRectangle.area(); c->packedArea = 0.0; c->guillotineContexts.clear(); c->Skyline = createSkyline(freeRectangle); c->packedRectangles.clear(); c->freeRectangle = freeRectangle; c->wasteImprovement = wasteImprovement; }
Option<Rect> sb::Skyline::tryPack(const Vec2& r, Context& c) { Option<Rect> packedInWastedArea = nullptr; for(auto& gc : c.guillotineContexts) { auto gpacked = Guillotine::tryPack(r, gc.second); if (gpacked) { auto gpacked2 = gpacked.value(); Matrix3x3::fromTranslation(gc.first.bottomLeft()).transformRect(&gpacked2); packedInWastedArea = gpacked2; c.packedRectangles.push_back(gpacked2); } } if (packedInWastedArea) { return packedInWastedArea; } if ((c.packedArea + r.area()) > c.totalArea) return nullptr; std::vector<Rect> lostAreas; auto bestPlace = bestPlaceToPack(c.Skyline, c.freeRectangle.height(), r, lostAreas); if (!bestPlace) //rectangle can't be packed return nullptr; else { auto packed = packIntoPlace(c.Skyline, r, bestPlace.value()); c.packedRectangles.push_back(packed); if (lostAreas.size() != 0 && c.wasteImprovement) { for(auto& lostArea : lostAreas) { Guillotine::Context guiCtx; Guillotine::initializeContext(&guiCtx, lostArea.size(), Guillotine::FitPolicy::BSSF, Guillotine::SplitPolicy::MINAS); c.guillotineContexts.insert({ lostArea, guiCtx }); c.packedArea += lostArea.area(); } } c.packedArea += r.area(); return packed; } }
Option<Rect> sb::MaxRects::tryPack(const Vec2& r, Context& c) { std::vector<Rect> splitted; if ((c.packedArea + r.area()) > c.totalArea) return nullptr; Option<Rect> previousF = nullptr; for(auto& freeF : c.freeRectangles) { //Fits? if (freeF.containsRect(newRect(freeF.bottomLeft(), r))) { if (previousF && isBestFit(c.fp, freeF, previousF.value(), r)) previousF = freeF; else if (!previousF) previousF = freeF; } } if (!previousF) //rectangle can't be packed return nullptr; else { auto packed = newRect(previousF->bottomLeft(), r); c.packedRectangles.push_back(packed); c.freeRectangles.erase(previousF.value()); split(previousF.value(), packed, splitted); for(auto& sr : splitted) c.freeRectangles.insert(sr); std::vector<Rect> frCopy(c.freeRectangles.begin(), c.freeRectangles.end()); for(auto& fr : frCopy) { split(fr, packed, splitted); if (splitted.size() != 0) { c.freeRectangles.erase(fr); for(auto& sr : splitted) c.freeRectangles.insert(sr); } } frCopy = std::vector<Rect>(c.freeRectangles.begin(), c.freeRectangles.end()); auto pairs = allPairs(frCopy); for (auto& p : pairs) { if (p.first.containsRect(p.second)) { c.freeRectangles.erase(p.second); } else if (p.second.containsRect(p.first)) { c.freeRectangles.erase(p.first); } } } return newRect(previousF->bottomLeft(), r); }
//Some Internal Methods bool isBestFit(Guillotine::FitPolicy FitPolicy, const Rect& freeRectangle, const Rect& previousFreeRectangle, const Vec2& rectangle) { float a1 = 0, a2 = 0; switch (FitPolicy) { case Guillotine::FitPolicy::BAF: a1 = rectangle.area() / freeRectangle.area(); a2 = rectangle.area() / previousFreeRectangle.area(); return a1 > a2; case Guillotine::FitPolicy::BSSF: a1 = std::min(freeRectangle.width() - rectangle.width(), freeRectangle.height() - rectangle.height()); a2 = std::min(previousFreeRectangle.width() - rectangle.width(), previousFreeRectangle.height() - rectangle.height()); return a1 < a2; case Guillotine::FitPolicy::BLSF: a1 = std::max(freeRectangle.width() - rectangle.width(), freeRectangle.height() - rectangle.height()); a2 = std::max(previousFreeRectangle.width() - rectangle.width(), previousFreeRectangle.height() - rectangle.height()); return a1 < a2; default: throw "should never get here"; } }
bool isBestFit(MaxRects::FitPolicy FitPolicy, const Rect& freeRectangle, const Rect& previousFreeRectangle, const Vec2& rectangle) { float a1, a2, b1, b2, c1, c2; switch (FitPolicy) { case MaxRects::FitPolicy::BL: if (freeRectangle.bottom() == previousFreeRectangle.bottom()) return freeRectangle.left() <= previousFreeRectangle.left(); else return freeRectangle.bottom() <= previousFreeRectangle.bottom(); case MaxRects::FitPolicy::BAF: a1 = rectangle.area() / freeRectangle.area(); a2 = rectangle.area() / previousFreeRectangle.area(); return a1 > a2; case MaxRects::FitPolicy::BSSF: b1 = std::min(freeRectangle.width() - rectangle.width(), freeRectangle.height() - rectangle.height()); b2 = std::min(previousFreeRectangle.width() - rectangle.width(), previousFreeRectangle.height() - rectangle.height()); return b1 < b2; case MaxRects::FitPolicy::BLSF: c1 = std::max(freeRectangle.width() - rectangle.width(), freeRectangle.height() - rectangle.height()); c2 = std::max(previousFreeRectangle.width() - rectangle.width(), previousFreeRectangle.height() - rectangle.height()); return c1 < c2; default: throw "should never get here."; } }