mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-19 11:23:42 +00:00
Refactor folder (#10475)
Move many third-party components' source codes from the src folder to a new folder called deps_src. The goal is to make the code structure clearer and easier to navigate.
This commit is contained in:
@@ -0,0 +1,434 @@
|
||||
#ifndef BOTTOMLEFT_HPP
|
||||
#define BOTTOMLEFT_HPP
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "placer_boilerplate.hpp"
|
||||
|
||||
namespace libnest2d { namespace placers {
|
||||
|
||||
template<class T, class = T> struct DefaultEpsilon {};
|
||||
|
||||
template<class T>
|
||||
struct DefaultEpsilon<T, enable_if_t<std::is_integral<T>::value, T> > {
|
||||
static const T Value = 1;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct DefaultEpsilon<T, enable_if_t<std::is_floating_point<T>::value, T> > {
|
||||
static const T Value = 1e-3;
|
||||
};
|
||||
|
||||
template<class RawShape>
|
||||
struct BLConfig {
|
||||
DECLARE_MAIN_TYPES(RawShape);
|
||||
|
||||
Coord min_obj_distance = 0;
|
||||
Coord epsilon = DefaultEpsilon<Coord>::Value;
|
||||
bool allow_rotations = false;
|
||||
//BBS: sort function for selector
|
||||
std::function<bool(_Item<RawShape>& i1, _Item<RawShape>& i2)> sortfunc;
|
||||
//BBS: excluded region for V4 bed
|
||||
std::vector<_Item<RawShape> > m_excluded_regions;
|
||||
_ItemGroup<RawShape> m_excluded_items;
|
||||
std::vector < _Item<RawShape> > m_nonprefered_regions;
|
||||
};
|
||||
|
||||
template<class RawShape>
|
||||
class _BottomLeftPlacer: public PlacerBoilerplate<
|
||||
_BottomLeftPlacer<RawShape>,
|
||||
RawShape, _Box<TPoint<RawShape>>,
|
||||
BLConfig<RawShape> >
|
||||
{
|
||||
using Base = PlacerBoilerplate<_BottomLeftPlacer<RawShape>, RawShape,
|
||||
_Box<TPoint<RawShape>>, BLConfig<RawShape>>;
|
||||
DECLARE_PLACER(Base)
|
||||
|
||||
public:
|
||||
|
||||
explicit _BottomLeftPlacer(const BinType& bin): Base(bin) {}
|
||||
|
||||
template<class Range = ConstItemRange<typename Base::DefaultIter>>
|
||||
PackResult trypack(Item& item,
|
||||
const Range& = Range())
|
||||
{
|
||||
auto r = _trypack(item);
|
||||
if(!r && Base::config_.allow_rotations) {
|
||||
|
||||
item.rotate(Degrees(90));
|
||||
r =_trypack(item);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
enum class Dir {
|
||||
LEFT,
|
||||
DOWN
|
||||
};
|
||||
|
||||
inline RawShape leftPoly(const Item& item) const {
|
||||
return toWallPoly(item, Dir::LEFT);
|
||||
}
|
||||
|
||||
inline RawShape downPoly(const Item& item) const {
|
||||
return toWallPoly(item, Dir::DOWN);
|
||||
}
|
||||
|
||||
inline Coord availableSpaceLeft(const Item& item) {
|
||||
return availableSpace(item, Dir::LEFT);
|
||||
}
|
||||
|
||||
inline Coord availableSpaceDown(const Item& item) {
|
||||
return availableSpace(item, Dir::DOWN);
|
||||
}
|
||||
|
||||
double score() const { return score_; }
|
||||
//BBS
|
||||
void plateID(int id) { plate_id = id; }
|
||||
int plateID() { return plate_id; }
|
||||
|
||||
protected:
|
||||
double score_ = 0;
|
||||
int plate_id = 0; // BBS
|
||||
|
||||
PackResult _trypack(Item& item) {
|
||||
|
||||
// Get initial position for item in the top right corner
|
||||
setInitialPosition(item);
|
||||
|
||||
Coord d = availableSpaceDown(item);
|
||||
auto eps = config_.epsilon;
|
||||
bool can_move = d > eps;
|
||||
bool can_be_packed = can_move;
|
||||
bool left = true;
|
||||
|
||||
while(can_move) {
|
||||
if(left) { // write previous down move and go down
|
||||
item.translate({0, -d+eps});
|
||||
d = availableSpaceLeft(item);
|
||||
can_move = d > eps;
|
||||
left = false;
|
||||
} else { // write previous left move and go down
|
||||
item.translate({-d+eps, 0});
|
||||
d = availableSpaceDown(item);
|
||||
can_move = d > eps;
|
||||
left = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(can_be_packed) {
|
||||
Item trsh(item.transformedShape());
|
||||
for(auto& v : trsh) can_be_packed = can_be_packed &&
|
||||
getX(v) < bin_.width() &&
|
||||
getY(v) < bin_.height();
|
||||
}
|
||||
|
||||
return can_be_packed? PackResult(item) : PackResult();
|
||||
}
|
||||
|
||||
void setInitialPosition(Item& item) {
|
||||
auto bb = item.boundingBox();
|
||||
|
||||
Vertex v = { getX(bb.maxCorner()), getY(bb.minCorner()) };
|
||||
|
||||
|
||||
Coord dx = getX(bin_.maxCorner()) - getX(v);
|
||||
Coord dy = getY(bin_.maxCorner()) - getY(v);
|
||||
|
||||
item.translate({dx, dy});
|
||||
}
|
||||
|
||||
template<class C = Coord>
|
||||
static enable_if_t<std::is_floating_point<C>::value, bool>
|
||||
isInTheWayOf( const Item& item,
|
||||
const Item& other,
|
||||
const RawShape& scanpoly)
|
||||
{
|
||||
auto tsh = other.transformedShape();
|
||||
return ( sl::intersects(tsh, scanpoly) ||
|
||||
sl::isInside(tsh, scanpoly) ) &&
|
||||
( !sl::intersects(tsh, item.rawShape()) &&
|
||||
!sl::isInside(tsh, item.rawShape()) );
|
||||
}
|
||||
|
||||
template<class C = Coord>
|
||||
static enable_if_t<std::is_integral<C>::value, bool>
|
||||
isInTheWayOf( const Item& item,
|
||||
const Item& other,
|
||||
const RawShape& scanpoly)
|
||||
{
|
||||
auto tsh = other.transformedShape();
|
||||
|
||||
bool inters_scanpoly = sl::intersects(tsh, scanpoly) &&
|
||||
!sl::touches(tsh, scanpoly);
|
||||
bool inters_item = sl::intersects(tsh, item.rawShape()) &&
|
||||
!sl::touches(tsh, item.rawShape());
|
||||
|
||||
return ( inters_scanpoly ||
|
||||
sl::isInside(tsh, scanpoly)) &&
|
||||
( !inters_item &&
|
||||
!sl::isInside(tsh, item.rawShape())
|
||||
);
|
||||
}
|
||||
|
||||
ItemGroup itemsInTheWayOf(const Item& item, const Dir dir) {
|
||||
// Get the left or down polygon, that has the same area as the shadow
|
||||
// of input item reflected to the left or downwards
|
||||
auto&& scanpoly = dir == Dir::LEFT? leftPoly(item) :
|
||||
downPoly(item);
|
||||
|
||||
ItemGroup ret; // packed items 'in the way' of item
|
||||
ret.reserve(items_.size());
|
||||
|
||||
// Predicate to find items that are 'in the way' for left (down) move
|
||||
auto predicate = [&scanpoly, &item](const Item& it) {
|
||||
return isInTheWayOf(item, it, scanpoly);
|
||||
};
|
||||
|
||||
// Get the items that are in the way for the left (or down) movement
|
||||
std::copy_if(items_.begin(), items_.end(),
|
||||
std::back_inserter(ret), predicate);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Coord availableSpace(const Item& _item, const Dir dir) {
|
||||
|
||||
Item item (_item.transformedShape());
|
||||
|
||||
|
||||
std::function<Coord(const Vertex&)> getCoord;
|
||||
std::function< std::pair<Coord, bool>(const Segment&, const Vertex&) >
|
||||
availableDistanceSV;
|
||||
|
||||
std::function< std::pair<Coord, bool>(const Vertex&, const Segment&) >
|
||||
availableDistance;
|
||||
|
||||
if(dir == Dir::LEFT) {
|
||||
getCoord = [](const Vertex& v) { return getX(v); };
|
||||
availableDistance = pointlike::horizontalDistance<Vertex>;
|
||||
availableDistanceSV = [](const Segment& s, const Vertex& v) {
|
||||
auto ret = pointlike::horizontalDistance<Vertex>(v, s);
|
||||
if(ret.second) ret.first = -ret.first;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
else {
|
||||
getCoord = [](const Vertex& v) { return getY(v); };
|
||||
availableDistance = pointlike::verticalDistance<Vertex>;
|
||||
availableDistanceSV = [](const Segment& s, const Vertex& v) {
|
||||
auto ret = pointlike::verticalDistance<Vertex>(v, s);
|
||||
if(ret.second) ret.first = -ret.first;
|
||||
return ret;
|
||||
};
|
||||
}
|
||||
|
||||
auto&& items_in_the_way = itemsInTheWayOf(item, dir);
|
||||
|
||||
// Comparison function for finding min vertex
|
||||
auto cmp = [&getCoord](const Vertex& v1, const Vertex& v2) {
|
||||
return getCoord(v1) < getCoord(v2);
|
||||
};
|
||||
|
||||
// find minimum left or down coordinate of item
|
||||
auto minvertex_it = std::min_element(item.begin(),
|
||||
item.end(),
|
||||
cmp);
|
||||
|
||||
// Get the initial distance in floating point
|
||||
Coord m = getCoord(*minvertex_it);
|
||||
|
||||
// Check available distance for every vertex of item to the objects
|
||||
// in the way for the nearest intersection
|
||||
if(!items_in_the_way.empty()) { // This is crazy, should be optimized...
|
||||
for(Item& pleft : items_in_the_way) {
|
||||
// For all segments in items_to_left
|
||||
|
||||
assert(pleft.vertexCount() > 0);
|
||||
|
||||
auto trpleft_poly = pleft.transformedShape();
|
||||
auto& trpleft = sl::contour(trpleft_poly);
|
||||
auto first = sl::begin(trpleft);
|
||||
auto next = first + 1;
|
||||
auto endit = sl::end(trpleft);
|
||||
|
||||
while(next != endit) {
|
||||
Segment seg(*(first++), *(next++));
|
||||
for(auto& v : item) { // For all vertices in item
|
||||
|
||||
auto d = availableDistance(v, seg);
|
||||
|
||||
if(d.second && d.first < m) m = d.first;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto first = item.begin();
|
||||
auto next = first + 1;
|
||||
auto endit = item.end();
|
||||
|
||||
// For all edges in item:
|
||||
while(next != endit) {
|
||||
Segment seg(*(first++), *(next++));
|
||||
|
||||
// for all shapes in items_to_left
|
||||
for(Item& sh : items_in_the_way) {
|
||||
assert(sh.vertexCount() > 0);
|
||||
|
||||
Item tsh(sh.transformedShape());
|
||||
for(auto& v : tsh) { // For all vertices in item
|
||||
|
||||
auto d = availableDistanceSV(seg, v);
|
||||
|
||||
if(d.second && d.first < m) m = d.first;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of the left (and down) polygon as described by
|
||||
* [López-Camacho et al. 2013]\
|
||||
* (http://www.cs.stir.ac.uk/~goc/papers/EffectiveHueristic2DAOR2013.pdf)
|
||||
* see algorithm 8 for details...
|
||||
*/
|
||||
RawShape toWallPoly(const Item& _item, const Dir dir) const {
|
||||
// The variable names reflect the case of left polygon calculation.
|
||||
//
|
||||
// We will iterate through the item's vertices and search for the top
|
||||
// and bottom vertices (or right and left if dir==Dir::DOWN).
|
||||
// Save the relevant vertices and their indices into `bottom` and
|
||||
// `top` vectors. In case of left polygon construction these will
|
||||
// contain the top and bottom polygons which have the same vertical
|
||||
// coordinates (in case there is more of them).
|
||||
//
|
||||
// We get the leftmost (or downmost) vertex from the `bottom` and `top`
|
||||
// vectors and construct the final polygon.
|
||||
|
||||
Item item (_item.transformedShape());
|
||||
|
||||
auto getCoord = [dir](const Vertex& v) {
|
||||
return dir == Dir::LEFT? getY(v) : getX(v);
|
||||
};
|
||||
|
||||
Coord max_y = std::numeric_limits<Coord>::min();
|
||||
Coord min_y = std::numeric_limits<Coord>::max();
|
||||
|
||||
using El = std::pair<size_t, std::reference_wrapper<const Vertex>>;
|
||||
|
||||
std::function<bool(const El&, const El&)> cmp;
|
||||
|
||||
if(dir == Dir::LEFT)
|
||||
cmp = [](const El& e1, const El& e2) {
|
||||
return getX(e1.second.get()) < getX(e2.second.get());
|
||||
};
|
||||
else
|
||||
cmp = [](const El& e1, const El& e2) {
|
||||
return getY(e1.second.get()) < getY(e2.second.get());
|
||||
};
|
||||
|
||||
std::vector< El > top;
|
||||
std::vector< El > bottom;
|
||||
|
||||
size_t idx = 0;
|
||||
for(auto& v : item) { // Find the bottom and top vertices and save them
|
||||
auto vref = std::cref(v);
|
||||
auto vy = getCoord(v);
|
||||
|
||||
if( vy > max_y ) {
|
||||
max_y = vy;
|
||||
top.clear();
|
||||
top.emplace_back(idx, vref);
|
||||
}
|
||||
else if(vy == max_y) { top.emplace_back(idx, vref); }
|
||||
|
||||
if(vy < min_y) {
|
||||
min_y = vy;
|
||||
bottom.clear();
|
||||
bottom.emplace_back(idx, vref);
|
||||
}
|
||||
else if(vy == min_y) { bottom.emplace_back(idx, vref); }
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
// Get the top and bottom leftmost vertices, or the right and left
|
||||
// downmost vertices (if dir == Dir::DOWN)
|
||||
auto topleft_it = std::min_element(top.begin(), top.end(), cmp);
|
||||
auto bottomleft_it =
|
||||
std::min_element(bottom.begin(), bottom.end(), cmp);
|
||||
|
||||
auto& topleft_vertex = topleft_it->second.get();
|
||||
auto& bottomleft_vertex = bottomleft_it->second.get();
|
||||
|
||||
// Start and finish positions for the vertices that will be part of the
|
||||
// new polygon
|
||||
auto start = std::min(topleft_it->first, bottomleft_it->first);
|
||||
auto finish = std::max(topleft_it->first, bottomleft_it->first);
|
||||
|
||||
RawShape ret;
|
||||
|
||||
// the return shape
|
||||
auto& rsh = sl::contour(ret);
|
||||
|
||||
// reserve for all vertices plus 2 for the left horizontal wall, 2 for
|
||||
// the additional vertices for maintaning min object distance
|
||||
sl::reserve(rsh, finish-start+4);
|
||||
|
||||
auto addOthers_ = [&rsh, finish, start, &item](){
|
||||
for(size_t i = start+1; i < finish; i++)
|
||||
sl::addVertex(rsh, item.vertex(i));
|
||||
};
|
||||
|
||||
auto reverseAddOthers_ = [&rsh, finish, start, &item](){
|
||||
for(auto i = finish-1; i > start; i--)
|
||||
sl::addVertex(rsh, item.vertex(static_cast<unsigned long>(i)));
|
||||
};
|
||||
|
||||
auto addOthers = [&addOthers_, &reverseAddOthers_]() {
|
||||
if constexpr (!is_clockwise<RawShape>())
|
||||
addOthers_();
|
||||
else
|
||||
reverseAddOthers_();
|
||||
};
|
||||
|
||||
// Final polygon construction...
|
||||
|
||||
// Clockwise polygon construction
|
||||
|
||||
sl::addVertex(rsh, topleft_vertex);
|
||||
|
||||
if(dir == Dir::LEFT) addOthers();
|
||||
else {
|
||||
sl::addVertex(rsh, {getX(topleft_vertex), 0});
|
||||
sl::addVertex(rsh, {getX(bottomleft_vertex), 0});
|
||||
}
|
||||
|
||||
sl::addVertex(rsh, bottomleft_vertex);
|
||||
|
||||
if(dir == Dir::LEFT) {
|
||||
sl::addVertex(rsh, {0, getY(bottomleft_vertex)});
|
||||
sl::addVertex(rsh, {0, getY(topleft_vertex)});
|
||||
}
|
||||
else addOthers();
|
||||
|
||||
|
||||
// Close the polygon
|
||||
if constexpr (ClosureTypeV<RawShape> == Closure::CLOSED)
|
||||
sl::addVertex(rsh, topleft_vertex);
|
||||
|
||||
if constexpr (!is_clockwise<RawShape>())
|
||||
std::reverse(rsh.begin(), rsh.end());
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif //BOTTOMLEFT_HPP
|
||||
1219
deps_src/libnest2d/include/libnest2d/placers/nfpplacer.hpp
Normal file
1219
deps_src/libnest2d/include/libnest2d/placers/nfpplacer.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,151 @@
|
||||
#ifndef PLACER_BOILERPLATE_HPP
|
||||
#define PLACER_BOILERPLATE_HPP
|
||||
|
||||
#include <libnest2d/nester.hpp>
|
||||
|
||||
namespace libnest2d { namespace placers {
|
||||
|
||||
struct EmptyConfig {};
|
||||
|
||||
template<class Subclass, class RawShape, class TBin, class Cfg = EmptyConfig>
|
||||
class PlacerBoilerplate {
|
||||
mutable bool farea_valid_ = false;
|
||||
mutable double farea_ = 0.0;
|
||||
public:
|
||||
using ShapeType = RawShape;
|
||||
using Item = _Item<RawShape>;
|
||||
using Vertex = TPoint<RawShape>;
|
||||
using Segment = _Segment<Vertex>;
|
||||
using BinType = TBin;
|
||||
using Coord = TCoord<Vertex>;
|
||||
using Config = Cfg;
|
||||
using ItemGroup = _ItemGroup<RawShape>;
|
||||
using DefaultIter = typename ItemGroup::const_iterator;
|
||||
|
||||
class PackResult {
|
||||
public:
|
||||
Item *item_ptr_;
|
||||
Vertex move_;
|
||||
Radians rot_;
|
||||
double overfit_;
|
||||
friend class PlacerBoilerplate;
|
||||
friend Subclass;
|
||||
|
||||
PackResult(Item& item):
|
||||
item_ptr_(&item),
|
||||
move_(item.translation()),
|
||||
rot_(item.rotation()),
|
||||
overfit_(1.0) {}
|
||||
|
||||
PackResult(double overfit = 1.0):
|
||||
item_ptr_(nullptr), overfit_(overfit) {}
|
||||
|
||||
public:
|
||||
operator bool() { return item_ptr_ != nullptr; }
|
||||
double overfit() const { return overfit_; }
|
||||
double score_ = -1.11;
|
||||
double score() { return score_; }
|
||||
int plate_id = 0; // BBS
|
||||
};
|
||||
|
||||
inline PlacerBoilerplate(const BinType& bin, unsigned cap = 50): bin_(bin)
|
||||
{
|
||||
items_.reserve(cap);
|
||||
}
|
||||
|
||||
inline const BinType& bin() const BP2D_NOEXCEPT { return bin_; }
|
||||
|
||||
template<class TB> inline void bin(TB&& b) {
|
||||
bin_ = std::forward<BinType>(b);
|
||||
}
|
||||
|
||||
inline void configure(const Config& config) BP2D_NOEXCEPT {
|
||||
config_ = config;
|
||||
}
|
||||
|
||||
template<class Range = ConstItemRange<DefaultIter>>
|
||||
PackResult pack(Item& item, const Range& rem = Range()) {
|
||||
auto&& r = static_cast<Subclass*>(this)->trypack(item, rem);
|
||||
return r;
|
||||
}
|
||||
|
||||
void preload(const ItemGroup& packeditems) {
|
||||
items_.insert(items_.end(), packeditems.begin(), packeditems.end());
|
||||
farea_valid_ = false;
|
||||
}
|
||||
|
||||
void accept(PackResult& r) {
|
||||
if(r) {
|
||||
//r.item_ptr_->translation(r.move_);
|
||||
//r.item_ptr_->rotation(r.rot_);
|
||||
//items_.emplace_back(*(r.item_ptr_));
|
||||
static_cast<Subclass*>(this)->accept(r);
|
||||
farea_valid_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void unpackLast() {
|
||||
items_.pop_back();
|
||||
farea_valid_ = false;
|
||||
}
|
||||
|
||||
inline const ItemGroup& getItems() const { return items_; }
|
||||
|
||||
inline void clearItems() {
|
||||
items_.clear();
|
||||
farea_valid_ = false;
|
||||
}
|
||||
|
||||
//clearFunc: will be cleared if return true
|
||||
inline void clearItems(const std::function<bool(const Item &itm)> &clearFunc)
|
||||
{
|
||||
ItemGroup newGroup;
|
||||
for (auto &i : items_) {
|
||||
if (clearFunc(i.get()) == false) { newGroup.push_back(i); }
|
||||
}
|
||||
std::swap(newGroup, items_);
|
||||
farea_valid_ = false;
|
||||
}
|
||||
|
||||
inline double filledArea() const {
|
||||
if(farea_valid_) return farea_;
|
||||
else {
|
||||
farea_ = .0;
|
||||
std::for_each(items_.begin(), items_.end(),
|
||||
[this] (Item& item) {
|
||||
farea_ += item.area();
|
||||
});
|
||||
farea_valid_ = true;
|
||||
}
|
||||
|
||||
return farea_;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
BinType bin_;
|
||||
ItemGroup items_;
|
||||
Cfg config_;
|
||||
};
|
||||
|
||||
|
||||
#define DECLARE_PLACER(Base) \
|
||||
using Base::bin_; \
|
||||
using Base::items_; \
|
||||
using Base::config_; \
|
||||
public: \
|
||||
using typename Base::ShapeType; \
|
||||
using typename Base::Item; \
|
||||
using typename Base::ItemGroup; \
|
||||
using typename Base::BinType; \
|
||||
using typename Base::Config; \
|
||||
using typename Base::Vertex; \
|
||||
using typename Base::Segment; \
|
||||
using typename Base::PackResult; \
|
||||
using typename Base::Coord; \
|
||||
private:
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // PLACER_BOILERPLATE_HPP
|
||||
Reference in New Issue
Block a user