parallel nesting can be enabled but fails with the current objectfunction.
This commit is contained in:
parent
e678368b23
commit
8617b0a409
@ -93,6 +93,8 @@ if(LIBNEST2D_BUILD_EXAMPLES)
|
||||
add_executable(example examples/main.cpp
|
||||
# tools/libnfpglue.hpp
|
||||
# tools/libnfpglue.cpp
|
||||
tools/nfp_svgnest.hpp
|
||||
tools/nfp_svgnest_glue.hpp
|
||||
tools/svgtools.hpp
|
||||
tests/printer_parts.cpp
|
||||
tests/printer_parts.h
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
//#define DEBUG_EXPORT_NFP
|
||||
|
||||
#include <libnest2d.h>
|
||||
@ -12,6 +11,8 @@
|
||||
#include "libnest2d/rotfinder.hpp"
|
||||
|
||||
//#include "tools/libnfpglue.hpp"
|
||||
//#include "tools/nfp_svgnest_glue.hpp"
|
||||
|
||||
|
||||
using namespace libnest2d;
|
||||
using ItemGroup = std::vector<std::reference_wrapper<Item>>;
|
||||
@ -53,10 +54,50 @@ void arrangeRectangles() {
|
||||
|
||||
const int SCALE = 1000000;
|
||||
|
||||
std::vector<Rectangle> rects = {
|
||||
{60*SCALE, 200*SCALE},
|
||||
{60*SCALE, 200*SCALE}
|
||||
};
|
||||
std::vector<Item> rects(100, {
|
||||
{-9945219, -3065619},
|
||||
{-9781479, -2031780},
|
||||
{-9510560, -1020730},
|
||||
{-9135450, -43529},
|
||||
{-2099999, 14110899},
|
||||
{2099999, 14110899},
|
||||
{9135450, -43529},
|
||||
{9510560, -1020730},
|
||||
{9781479, -2031780},
|
||||
{9945219, -3065619},
|
||||
{10000000, -4110899},
|
||||
{9945219, -5156179},
|
||||
{9781479, -6190019},
|
||||
{9510560, -7201069},
|
||||
{9135450, -8178270},
|
||||
{8660249, -9110899},
|
||||
{8090169, -9988750},
|
||||
{7431449, -10802209},
|
||||
{6691309, -11542349},
|
||||
{5877850, -12201069},
|
||||
{5000000, -12771149},
|
||||
{4067369, -13246350},
|
||||
{3090169, -13621459},
|
||||
{2079119, -13892379},
|
||||
{1045279, -14056119},
|
||||
{0, -14110899},
|
||||
{-1045279, -14056119},
|
||||
{-2079119, -13892379},
|
||||
{-3090169, -13621459},
|
||||
{-4067369, -13246350},
|
||||
{-5000000, -12771149},
|
||||
{-5877850, -12201069},
|
||||
{-6691309, -11542349},
|
||||
{-7431449, -10802209},
|
||||
{-8090169, -9988750},
|
||||
{-8660249, -9110899},
|
||||
{-9135450, -8178270},
|
||||
{-9510560, -7201069},
|
||||
{-9781479, -6190019},
|
||||
{-9945219, -5156179},
|
||||
{-10000000, -4110899},
|
||||
{-9945219, -3065619},
|
||||
});
|
||||
|
||||
std::vector<Item> input;
|
||||
input.insert(input.end(), prusaParts().begin(), prusaParts().end());
|
||||
@ -84,7 +125,7 @@ void arrangeRectangles() {
|
||||
|
||||
// _Circle<PointImpl> bin({0, 0}, 125*SCALE);
|
||||
|
||||
auto min_obj_distance = static_cast<Coord>(0*SCALE);
|
||||
auto min_obj_distance = static_cast<Coord>(1.5*SCALE);
|
||||
|
||||
using Placer = strategies::_NofitPolyPlacer<PolygonImpl, decltype(bin)>;
|
||||
using Packer = Nester<Placer, FirstFitSelection>;
|
||||
@ -95,14 +136,15 @@ void arrangeRectangles() {
|
||||
pconf.alignment = Placer::Config::Alignment::CENTER;
|
||||
pconf.starting_point = Placer::Config::Alignment::CENTER;
|
||||
pconf.rotations = {0.0/*, Pi/2.0, Pi, 3*Pi/2*/};
|
||||
pconf.accuracy = 1.0f;
|
||||
pconf.accuracy = 0.5f;
|
||||
pconf.parallel = true;
|
||||
|
||||
Packer::SelectionConfig sconf;
|
||||
// sconf.allow_parallel = false;
|
||||
// sconf.force_parallel = false;
|
||||
// sconf.try_triplets = true;
|
||||
// sconf.try_reverse_order = true;
|
||||
// sconf.waste_increment = 0.005;
|
||||
// sconf.waste_increment = 0.01;
|
||||
|
||||
arrange.configure(pconf, sconf);
|
||||
|
||||
@ -175,7 +217,6 @@ void arrangeRectangles() {
|
||||
SVGWriter svgw(conf);
|
||||
svgw.setSize(Box(250*SCALE, 210*SCALE));
|
||||
svgw.writePackGroup(result);
|
||||
// std::for_each(input.begin(), input.end(), [&svgw](Item& item){ svgw.writeItem(item);});
|
||||
svgw.save("out");
|
||||
}
|
||||
|
||||
|
@ -356,8 +356,7 @@ inline double area(const PolygonImpl& shape, const PolygonTag&)
|
||||
#endif
|
||||
|
||||
template<>
|
||||
inline bool isInside<PolygonImpl>(const PointImpl& point,
|
||||
const PolygonImpl& shape)
|
||||
inline bool isInside(const PointImpl& point, const PolygonImpl& shape)
|
||||
{
|
||||
return boost::geometry::within(point, shape);
|
||||
}
|
||||
@ -390,7 +389,8 @@ inline bp2d::Box boundingBox(const PolygonImpl& sh, const PolygonTag&)
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bp2d::Box boundingBox<PolygonImpl>(const bp2d::Shapes& shapes)
|
||||
inline bp2d::Box boundingBox<bp2d::Shapes>(const bp2d::Shapes& shapes,
|
||||
const MultiPolygonTag&)
|
||||
{
|
||||
bp2d::Box b;
|
||||
boost::geometry::envelope(shapes, b);
|
||||
@ -400,7 +400,7 @@ inline bp2d::Box boundingBox<PolygonImpl>(const bp2d::Shapes& shapes)
|
||||
|
||||
#ifndef DISABLE_BOOST_CONVEX_HULL
|
||||
template<>
|
||||
inline PolygonImpl convexHull(const PolygonImpl& sh)
|
||||
inline PolygonImpl convexHull(const PolygonImpl& sh, const PolygonTag&)
|
||||
{
|
||||
PolygonImpl ret;
|
||||
boost::geometry::convex_hull(sh, ret);
|
||||
@ -408,7 +408,8 @@ inline PolygonImpl convexHull(const PolygonImpl& sh)
|
||||
}
|
||||
|
||||
template<>
|
||||
inline PolygonImpl convexHull(const bp2d::Shapes& shapes)
|
||||
inline PolygonImpl convexHull(const TMultiShape<PolygonImpl>& shapes,
|
||||
const MultiPolygonTag&)
|
||||
{
|
||||
PolygonImpl ret;
|
||||
boost::geometry::convex_hull(shapes, ret);
|
||||
@ -416,34 +417,6 @@ inline PolygonImpl convexHull(const bp2d::Shapes& shapes)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_BOOST_ROTATE
|
||||
template<>
|
||||
inline void rotate(PolygonImpl& sh, const Radians& rads)
|
||||
{
|
||||
namespace trans = boost::geometry::strategy::transform;
|
||||
|
||||
PolygonImpl cpy = sh;
|
||||
trans::rotate_transformer<boost::geometry::radian, Radians, 2, 2>
|
||||
rotate(rads);
|
||||
|
||||
boost::geometry::transform(cpy, sh, rotate);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_BOOST_TRANSLATE
|
||||
template<>
|
||||
inline void translate(PolygonImpl& sh, const PointImpl& offs)
|
||||
{
|
||||
namespace trans = boost::geometry::strategy::transform;
|
||||
|
||||
PolygonImpl cpy = sh;
|
||||
trans::translate_transformer<bp2d::Coord, 2, 2> translate(
|
||||
bp2d::getX(offs), bp2d::getY(offs));
|
||||
|
||||
boost::geometry::transform(cpy, sh, translate);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_BOOST_OFFSET
|
||||
template<>
|
||||
inline void offset(PolygonImpl& sh, bp2d::Coord distance)
|
||||
@ -515,14 +488,24 @@ template<> inline std::pair<bool, std::string> isValid(const PolygonImpl& sh)
|
||||
namespace nfp {
|
||||
|
||||
#ifndef DISABLE_BOOST_NFP_MERGE
|
||||
|
||||
// Warning: I could not get boost union_ to work. Geometries will overlap.
|
||||
template<>
|
||||
inline bp2d::Shapes Nfp::merge(const bp2d::Shapes& shapes,
|
||||
inline bp2d::Shapes nfp::merge(const bp2d::Shapes& shapes,
|
||||
const PolygonImpl& sh)
|
||||
{
|
||||
bp2d::Shapes retv;
|
||||
boost::geometry::union_(shapes, sh, retv);
|
||||
return retv;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bp2d::Shapes nfp::merge(const bp2d::Shapes& shapes)
|
||||
{
|
||||
bp2d::Shapes retv;
|
||||
boost::geometry::union_(shapes, shapes.back(), retv);
|
||||
return retv;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
@ -21,9 +21,6 @@ struct PolygonImpl {
|
||||
PathImpl Contour;
|
||||
HoleStore Holes;
|
||||
|
||||
using Tag = libnest2d::PolygonTag;
|
||||
using PointType = PointImpl;
|
||||
|
||||
inline PolygonImpl() = default;
|
||||
|
||||
inline explicit PolygonImpl(const PathImpl& cont): Contour(cont) {}
|
||||
@ -102,41 +99,49 @@ template<> struct PointType<PolygonImpl> {
|
||||
using Type = PointImpl;
|
||||
};
|
||||
|
||||
// Type of vertex iterator used by Clipper
|
||||
template<> struct VertexIteratorType<PolygonImpl> {
|
||||
using Type = ClipperLib::Path::iterator;
|
||||
};
|
||||
|
||||
// Type of vertex iterator used by Clipper
|
||||
template<> struct VertexConstIteratorType<PolygonImpl> {
|
||||
using Type = ClipperLib::Path::const_iterator;
|
||||
template<> struct PointType<PointImpl> {
|
||||
using Type = PointImpl;
|
||||
};
|
||||
|
||||
template<> struct CountourType<PolygonImpl> {
|
||||
using Type = PathImpl;
|
||||
};
|
||||
|
||||
template<> struct ShapeTag<PolygonImpl> { using Type = PolygonTag; };
|
||||
|
||||
template<> struct ShapeTag<TMultiShape<PolygonImpl>> {
|
||||
using Type = MultiPolygonTag;
|
||||
};
|
||||
|
||||
template<> struct PointType<TMultiShape<PolygonImpl>> {
|
||||
using Type = PointImpl;
|
||||
};
|
||||
|
||||
template<> struct HolesContainer<PolygonImpl> {
|
||||
using Type = ClipperLib::Paths;
|
||||
};
|
||||
|
||||
namespace pointlike {
|
||||
|
||||
// Tell binpack2d how to extract the X coord from a ClipperPoint object
|
||||
// Tell libnest2d how to extract the X coord from a ClipperPoint object
|
||||
template<> inline TCoord<PointImpl> x(const PointImpl& p)
|
||||
{
|
||||
return p.X;
|
||||
}
|
||||
|
||||
// Tell binpack2d how to extract the Y coord from a ClipperPoint object
|
||||
// Tell libnest2d how to extract the Y coord from a ClipperPoint object
|
||||
template<> inline TCoord<PointImpl> y(const PointImpl& p)
|
||||
{
|
||||
return p.Y;
|
||||
}
|
||||
|
||||
// Tell binpack2d how to extract the X coord from a ClipperPoint object
|
||||
// Tell libnest2d how to extract the X coord from a ClipperPoint object
|
||||
template<> inline TCoord<PointImpl>& x(PointImpl& p)
|
||||
{
|
||||
return p.X;
|
||||
}
|
||||
|
||||
// Tell binpack2d how to extract the Y coord from a ClipperPoint object
|
||||
// Tell libnest2d how to extract the Y coord from a ClipperPoint object
|
||||
template<> inline TCoord<PointImpl>& y(PointImpl& p)
|
||||
{
|
||||
return p.Y;
|
||||
@ -178,10 +183,6 @@ inline double area<Orientation::COUNTER_CLOCKWISE>(const PolygonImpl& sh) {
|
||||
|
||||
}
|
||||
|
||||
template<> struct HolesContainer<PolygonImpl> {
|
||||
using Type = ClipperLib::Paths;
|
||||
};
|
||||
|
||||
namespace shapelike {
|
||||
|
||||
template<> inline void reserve(PolygonImpl& sh, size_t vertex_capacity)
|
||||
@ -189,7 +190,7 @@ template<> inline void reserve(PolygonImpl& sh, size_t vertex_capacity)
|
||||
return sh.Contour.reserve(vertex_capacity);
|
||||
}
|
||||
|
||||
// Tell binpack2d how to make string out of a ClipperPolygon object
|
||||
// Tell libnest2d how to make string out of a ClipperPolygon object
|
||||
template<> inline double area(const PolygonImpl& sh, const PolygonTag&)
|
||||
{
|
||||
return _smartarea::area<OrientationType<PolygonImpl>::Value>(sh);
|
||||
@ -269,28 +270,6 @@ template<> inline std::string toString(const PolygonImpl& sh)
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template<> inline TVertexIterator<PolygonImpl> begin(PolygonImpl& sh)
|
||||
{
|
||||
return sh.Contour.begin();
|
||||
}
|
||||
|
||||
template<> inline TVertexIterator<PolygonImpl> end(PolygonImpl& sh)
|
||||
{
|
||||
return sh.Contour.end();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline TVertexConstIterator<PolygonImpl> cbegin(const PolygonImpl& sh)
|
||||
{
|
||||
return sh.Contour.cbegin();
|
||||
}
|
||||
|
||||
template<> inline TVertexConstIterator<PolygonImpl> cend(
|
||||
const PolygonImpl& sh)
|
||||
{
|
||||
return sh.Contour.cend();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline PolygonImpl create(const PathImpl& path, const HoleStore& holes)
|
||||
{
|
||||
@ -410,8 +389,8 @@ inline void rotate(PolygonImpl& sh, const Radians& rads)
|
||||
} // namespace shapelike
|
||||
|
||||
#define DISABLE_BOOST_NFP_MERGE
|
||||
inline nfp::Shapes<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
|
||||
nfp::Shapes<PolygonImpl> retv;
|
||||
inline std::vector<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
|
||||
shapelike::Shapes<PolygonImpl> retv;
|
||||
|
||||
ClipperLib::PolyTree result;
|
||||
clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNegative);
|
||||
@ -447,8 +426,8 @@ inline nfp::Shapes<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
|
||||
|
||||
namespace nfp {
|
||||
|
||||
template<> inline nfp::Shapes<PolygonImpl>
|
||||
merge(const nfp::Shapes<PolygonImpl>& shapes)
|
||||
template<> inline std::vector<PolygonImpl>
|
||||
merge(const std::vector<PolygonImpl>& shapes)
|
||||
{
|
||||
ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution);
|
||||
|
||||
|
@ -22,34 +22,12 @@ template<class GeomType>
|
||||
using TCoord = typename CoordType<remove_cvref_t<GeomType>>::Type;
|
||||
|
||||
/// Getting the type of point structure used by a shape.
|
||||
template<class Shape> struct PointType { /*using Type = void;*/ };
|
||||
template<class Sh> struct PointType { using Type = typename Sh::PointType; };
|
||||
|
||||
/// TPoint<ShapeClass> as shorthand for `typename PointType<ShapeClass>::Type`.
|
||||
template<class Shape>
|
||||
using TPoint = typename PointType<remove_cvref_t<Shape>>::Type;
|
||||
|
||||
/// Getting the VertexIterator type of a shape class.
|
||||
template<class Shape> struct VertexIteratorType { /*using Type = void;*/ };
|
||||
|
||||
/// Getting the const vertex iterator for a shape class.
|
||||
template<class Shape> struct VertexConstIteratorType {/* using Type = void;*/ };
|
||||
|
||||
/**
|
||||
* TVertexIterator<Shape> as shorthand for
|
||||
* `typename VertexIteratorType<Shape>::Type`
|
||||
*/
|
||||
template<class Shape>
|
||||
using TVertexIterator =
|
||||
typename VertexIteratorType<remove_cvref_t<Shape>>::Type;
|
||||
|
||||
/**
|
||||
* \brief TVertexConstIterator<Shape> as shorthand for
|
||||
* `typename VertexConstIteratorType<Shape>::Type`
|
||||
*/
|
||||
template<class ShapeClass>
|
||||
using TVertexConstIterator =
|
||||
typename VertexConstIteratorType<remove_cvref_t<ShapeClass>>::Type;
|
||||
|
||||
/**
|
||||
* \brief A point pair base class for other point pairs (segment, box, ...).
|
||||
* \tparam RawPoint The actual point type to use.
|
||||
@ -61,9 +39,16 @@ struct PointPair {
|
||||
};
|
||||
|
||||
struct PolygonTag {};
|
||||
struct MultiPolygonTag {};
|
||||
struct BoxTag {};
|
||||
struct CircleTag {};
|
||||
|
||||
template<class Shape> struct ShapeTag { using Type = typename Shape::Tag; };
|
||||
template<class S> using Tag = typename ShapeTag<S>::Type;
|
||||
|
||||
template<class S> struct MultiShape { using Type = std::vector<S>; };
|
||||
template<class S> using TMultiShape = typename MultiShape<S>::Type;
|
||||
|
||||
/**
|
||||
* \brief An abstraction of a box;
|
||||
*/
|
||||
@ -371,7 +356,7 @@ enum class Formats {
|
||||
namespace shapelike {
|
||||
|
||||
template<class RawShape>
|
||||
using Shapes = std::vector<RawShape>;
|
||||
using Shapes = TMultiShape<RawShape>;
|
||||
|
||||
template<class RawShape>
|
||||
inline RawShape create(const TContour<RawShape>& contour,
|
||||
@ -455,27 +440,28 @@ namespace shapelike {
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
inline TVertexIterator<RawShape> begin(RawShape& sh)
|
||||
inline typename TContour<RawShape>::iterator begin(RawShape& sh)
|
||||
{
|
||||
return sh.begin();
|
||||
return getContour(sh).begin();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
inline TVertexIterator<RawShape> end(RawShape& sh)
|
||||
inline typename TContour<RawShape>::iterator end(RawShape& sh)
|
||||
{
|
||||
return sh.end();
|
||||
return getContour(sh).end();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
inline TVertexConstIterator<RawShape> cbegin(const RawShape& sh)
|
||||
inline typename TContour<RawShape>::const_iterator
|
||||
cbegin(const RawShape& sh)
|
||||
{
|
||||
return sh.cbegin();
|
||||
return getContour(sh).cbegin();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
inline TVertexConstIterator<RawShape> cend(const RawShape& sh)
|
||||
inline typename TContour<RawShape>::const_iterator cend(const RawShape& sh)
|
||||
{
|
||||
return sh.cend();
|
||||
return getContour(sh).cend();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
@ -559,27 +545,29 @@ namespace shapelike {
|
||||
"ShapeLike::boundingBox(shape) unimplemented!");
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
inline _Box<TPoint<RawShape>> boundingBox(const Shapes<RawShape>& /*sh*/)
|
||||
template<class RawShapes>
|
||||
inline _Box<TPoint<typename RawShapes::value_type>>
|
||||
boundingBox(const RawShapes& /*sh*/, const MultiPolygonTag&)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
static_assert(always_false<RawShapes>::value,
|
||||
"ShapeLike::boundingBox(shapes) unimplemented!");
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
inline RawShape convexHull(const RawShape& /*sh*/)
|
||||
inline RawShape convexHull(const RawShape& /*sh*/, const PolygonTag&)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
"ShapeLike::convexHull(shape) unimplemented!");
|
||||
return RawShape();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
inline RawShape convexHull(const Shapes<RawShape>& /*sh*/)
|
||||
template<class RawShapes>
|
||||
inline typename RawShapes::value_type
|
||||
convexHull(const RawShapes& /*sh*/, const MultiPolygonTag&)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
static_assert(always_false<RawShapes>::value,
|
||||
"ShapeLike::convexHull(shapes) unimplemented!");
|
||||
return RawShape();
|
||||
return typename RawShapes::value_type();
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
@ -599,8 +587,7 @@ namespace shapelike {
|
||||
template<class RawShape>
|
||||
inline void offset(RawShape& /*sh*/, TCoord<TPoint<RawShape>> /*distance*/)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
"ShapeLike::offset() unimplemented!");
|
||||
dout() << "The current geometry backend does not support offsetting!\n";
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
@ -670,9 +657,9 @@ namespace shapelike {
|
||||
}
|
||||
|
||||
template<class S> // Dispatch function
|
||||
inline _Box<typename S::PointType> boundingBox(const S& sh)
|
||||
inline _Box<TPoint<S>> boundingBox(const S& sh)
|
||||
{
|
||||
return boundingBox(sh, typename S::Tag());
|
||||
return boundingBox(sh, Tag<S>() );
|
||||
}
|
||||
|
||||
template<class Box>
|
||||
@ -690,7 +677,7 @@ namespace shapelike {
|
||||
template<class RawShape> // Dispatching function
|
||||
inline double area(const RawShape& sh)
|
||||
{
|
||||
return area(sh, typename RawShape::Tag());
|
||||
return area(sh, Tag<RawShape>());
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
@ -702,6 +689,13 @@ namespace shapelike {
|
||||
});
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
inline auto convexHull(const RawShape& sh)
|
||||
-> decltype(convexHull(sh, Tag<RawShape>())) // TODO: C++14 could deduce
|
||||
{
|
||||
return convexHull(sh, Tag<RawShape>());
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
inline bool isInside(const TPoint<RawShape>& point,
|
||||
const _Circle<TPoint<RawShape>>& circ)
|
||||
@ -816,6 +810,16 @@ namespace shapelike {
|
||||
}
|
||||
}
|
||||
|
||||
#define DECLARE_MAIN_TYPES(T) \
|
||||
using Polygon = T; \
|
||||
using Point = TPoint<T>; \
|
||||
using Coord = TCoord<Point>; \
|
||||
using Contour = TContour<T>; \
|
||||
using Box = _Box<Point>; \
|
||||
using Circle = _Circle<Point>; \
|
||||
using Segment = _Segment<Point>; \
|
||||
using Polygons = TMultiShape<T>
|
||||
|
||||
}
|
||||
|
||||
#endif // GEOMETRY_TRAITS_HPP
|
||||
|
@ -27,8 +27,8 @@ inline bool _vsort(const TPoint<RawShape>& v1, const TPoint<RawShape>& v2)
|
||||
/// A collection of static methods for handling the no fit polygon creation.
|
||||
namespace nfp {
|
||||
|
||||
namespace sl = shapelike;
|
||||
namespace pl = pointlike;
|
||||
//namespace sl = shapelike;
|
||||
//namespace pl = pointlike;
|
||||
|
||||
/// The complexity level of a polygon that an NFP implementation can handle.
|
||||
enum class NfpLevel: unsigned {
|
||||
@ -49,7 +49,7 @@ template<class RawShape> struct MaxNfpLevel {
|
||||
|
||||
// Shorthand for a pile of polygons
|
||||
template<class RawShape>
|
||||
using Shapes = typename shapelike::Shapes<RawShape>;
|
||||
using Shapes = TMultiShape<RawShape>;
|
||||
|
||||
/**
|
||||
* Merge a bunch of polygons with the specified additional polygon.
|
||||
@ -62,10 +62,10 @@ using Shapes = typename shapelike::Shapes<RawShape>;
|
||||
* mostly it will be a set containing only one big polygon but if the input
|
||||
* polygons are disjuct than the resulting set will contain more polygons.
|
||||
*/
|
||||
template<class RawShape>
|
||||
inline Shapes<RawShape> merge(const Shapes<RawShape>& /*shc*/)
|
||||
template<class RawShapes>
|
||||
inline RawShapes merge(const RawShapes& /*shc*/)
|
||||
{
|
||||
static_assert(always_false<RawShape>::value,
|
||||
static_assert(always_false<RawShapes>::value,
|
||||
"Nfp::merge(shapes, shape) unimplemented!");
|
||||
}
|
||||
|
||||
@ -81,12 +81,12 @@ inline Shapes<RawShape> merge(const Shapes<RawShape>& /*shc*/)
|
||||
* polygons are disjuct than the resulting set will contain more polygons.
|
||||
*/
|
||||
template<class RawShape>
|
||||
inline Shapes<RawShape> merge(const Shapes<RawShape>& shc,
|
||||
const RawShape& sh)
|
||||
inline TMultiShape<RawShape> merge(const TMultiShape<RawShape>& shc,
|
||||
const RawShape& sh)
|
||||
{
|
||||
auto m = merge(shc);
|
||||
auto m = nfp::merge(shc);
|
||||
m.push_back(sh);
|
||||
return merge(m);
|
||||
return nfp::merge(m);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -31,6 +31,8 @@ class _Item {
|
||||
using Vertex = TPoint<RawShape>;
|
||||
using Box = _Box<Vertex>;
|
||||
|
||||
using VertexConstIterator = typename TContour<RawShape>::const_iterator;
|
||||
|
||||
// The original shape that gets encapsulated.
|
||||
RawShape sh_;
|
||||
|
||||
@ -39,7 +41,7 @@ class _Item {
|
||||
Radians rotation_;
|
||||
Coord offset_distance_;
|
||||
|
||||
// Info about whether the tranformations will have to take place
|
||||
// Info about whether the transformations will have to take place
|
||||
// This is needed because if floating point is used, it is hard to say
|
||||
// that a zero angle is not a rotation because of testing for equality.
|
||||
bool has_rotation_ = false, has_translation_ = false, has_offset_ = false;
|
||||
@ -59,8 +61,8 @@ class _Item {
|
||||
};
|
||||
|
||||
mutable Convexity convexity_ = Convexity::UNCHECKED;
|
||||
mutable TVertexConstIterator<RawShape> rmt_; // rightmost top vertex
|
||||
mutable TVertexConstIterator<RawShape> lmb_; // leftmost bottom vertex
|
||||
mutable VertexConstIterator rmt_; // rightmost top vertex
|
||||
mutable VertexConstIterator lmb_; // leftmost bottom vertex
|
||||
mutable bool rmt_valid_ = false, lmb_valid_ = false;
|
||||
mutable struct BBCache {
|
||||
Box bb; bool valid; Vertex tr;
|
||||
@ -81,7 +83,7 @@ public:
|
||||
* supports. Giving out a non const iterator would make it impossible to
|
||||
* perform correct cache invalidation.
|
||||
*/
|
||||
using Iterator = TVertexConstIterator<RawShape>;
|
||||
using Iterator = VertexConstIterator;
|
||||
|
||||
/**
|
||||
* @brief Get the orientation of the polygon.
|
||||
@ -110,7 +112,7 @@ public:
|
||||
explicit inline _Item(RawShape&& sh): sh_(std::move(sh)) {}
|
||||
|
||||
/**
|
||||
* @brief Create an item from an initilizer list.
|
||||
* @brief Create an item from an initializer list.
|
||||
* @param il The initializer list of vertices.
|
||||
*/
|
||||
inline _Item(const std::initializer_list< Vertex >& il):
|
||||
@ -160,7 +162,7 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a copy of an outer vertex whithin the carried shape.
|
||||
* @brief Get a copy of an outer vertex within the carried shape.
|
||||
*
|
||||
* Note that the vertex considered here is taken from the original shape
|
||||
* that this item is constructed from. This means that no transformation is
|
||||
@ -245,7 +247,7 @@ public:
|
||||
* @param p
|
||||
* @return
|
||||
*/
|
||||
inline bool isPointInside(const Vertex& p) const
|
||||
inline bool isInside(const Vertex& p) const
|
||||
{
|
||||
return sl::isInside(p, transformedShape());
|
||||
}
|
||||
@ -505,7 +507,7 @@ rem(typename Container::const_iterator it, const Container& cont) {
|
||||
/**
|
||||
* \brief A wrapper interface (trait) class for any placement strategy provider.
|
||||
*
|
||||
* If a client want's to use its own placement algorithm, all it has to do is to
|
||||
* If a client wants to use its own placement algorithm, all it has to do is to
|
||||
* specialize this class template and define all the ten methods it has. It can
|
||||
* use the strategies::PlacerBoilerplace class for creating a new placement
|
||||
* strategy where only the constructor and the trypack method has to be provided
|
||||
@ -558,7 +560,7 @@ public:
|
||||
* Note that it depends on the particular placer implementation how it
|
||||
* reacts to config changes in the middle of a calculation.
|
||||
*
|
||||
* @param config The configuration object defined by the placement startegy.
|
||||
* @param config The configuration object defined by the placement strategy.
|
||||
*/
|
||||
inline void configure(const Config& config) { impl_.configure(config); }
|
||||
|
||||
@ -568,7 +570,7 @@ public:
|
||||
*
|
||||
* \param item_store A container of items that are intended to be packed
|
||||
* later. Can be used by the placer to switch tactics. When it's knows that
|
||||
* many items will come a greedy startegy may not be the best.
|
||||
* many items will come a greedy strategy may not be the best.
|
||||
* \param from The iterator to the item from which the packing should start,
|
||||
* including the pointed item
|
||||
* \param count How many items should be packed. If the value is 1, than
|
||||
@ -596,7 +598,7 @@ public:
|
||||
* A default implementation would be to call
|
||||
* { auto&& r = trypack(...); accept(r); return r; } but we should let the
|
||||
* implementor of the placement strategy to harvest any optimizations from
|
||||
* the absence of an intermadiate step. The above version can still be used
|
||||
* the absence of an intermediate step. The above version can still be used
|
||||
* in the implementation.
|
||||
*
|
||||
* @param item The item to pack.
|
||||
@ -628,13 +630,6 @@ public:
|
||||
|
||||
inline double filledArea() const { return impl_.filledArea(); }
|
||||
|
||||
#ifndef NDEBUG
|
||||
inline auto getDebugItems() -> decltype(impl_.debug_items_)&
|
||||
{
|
||||
return impl_.debug_items_;
|
||||
}
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
// The progress function will be called with the number of placed items
|
||||
@ -659,15 +654,15 @@ public:
|
||||
* Note that it depends on the particular placer implementation how it
|
||||
* reacts to config changes in the middle of a calculation.
|
||||
*
|
||||
* @param config The configuration object defined by the selection startegy.
|
||||
* @param config The configuration object defined by the selection strategy.
|
||||
*/
|
||||
inline void configure(const Config& config) {
|
||||
impl_.configure(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A function callback which should be called whenewer an item or
|
||||
* a group of items where succesfully packed.
|
||||
* @brief A function callback which should be called whenever an item or
|
||||
* a group of items where successfully packed.
|
||||
* @param fn A function callback object taking one unsigned integer as the
|
||||
* number of the remaining items to pack.
|
||||
*/
|
||||
@ -680,7 +675,7 @@ public:
|
||||
* placer compatible with the PlacementStrategyLike interface.
|
||||
*
|
||||
* \param first, last The first and last iterator if the input sequence. It
|
||||
* can be only an iterator of a type converitible to Item.
|
||||
* can be only an iterator of a type convertible to Item.
|
||||
* \param bin. The shape of the bin. It has to be supported by the placement
|
||||
* strategy.
|
||||
* \param An optional config object for the placer.
|
||||
@ -712,7 +707,7 @@ public:
|
||||
/**
|
||||
* @brief Get the items for a particular bin.
|
||||
* @param binIndex The index of the requested bin.
|
||||
* @return Returns a list of allitems packed into the requested bin.
|
||||
* @return Returns a list of all items packed into the requested bin.
|
||||
*/
|
||||
inline ItemGroup itemsForBin(size_t binIndex) {
|
||||
return impl_.itemsForBin(binIndex);
|
||||
@ -754,7 +749,7 @@ using _IndexedPackGroup = std::vector<
|
||||
>;
|
||||
|
||||
/**
|
||||
* The Arranger is the frontend class for the binpack2d library. It takes the
|
||||
* The Arranger is the front-end class for the libnest2d library. It takes the
|
||||
* input items and outputs the items with the proper transformations to be
|
||||
* inside the provided bin.
|
||||
*/
|
||||
@ -857,7 +852,7 @@ public:
|
||||
return _execute(from, to);
|
||||
}
|
||||
|
||||
/// Set a progress indicatior function object for the selector.
|
||||
/// Set a progress indicator function object for the selector.
|
||||
inline Nester& progressIndicator(ProgressFunction func)
|
||||
{
|
||||
selector_.progressIndicator(func); return *this;
|
||||
@ -877,8 +872,8 @@ private:
|
||||
template<class TIterator,
|
||||
class IT = remove_cvref_t<typename TIterator::value_type>,
|
||||
|
||||
// This funtion will be used only if the iterators are pointing to
|
||||
// a type compatible with the binpack2d::_Item template.
|
||||
// This function will be used only if the iterators are pointing to
|
||||
// a type compatible with the libnets2d::_Item template.
|
||||
// This way we can use references to input elements as they will
|
||||
// have to exist for the lifetime of this call.
|
||||
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT>
|
||||
@ -904,8 +899,8 @@ private:
|
||||
template<class TIterator,
|
||||
class IT = remove_cvref_t<typename TIterator::value_type>,
|
||||
|
||||
// This funtion will be used only if the iterators are pointing to
|
||||
// a type compatible with the binpack2d::_Item template.
|
||||
// This function will be used only if the iterators are pointing to
|
||||
// a type compatible with the libnest2d::_Item template.
|
||||
// This way we can use references to input elements as they will
|
||||
// have to exist for the lifetime of this call.
|
||||
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT>
|
||||
|
@ -67,11 +67,11 @@ class metaloop {
|
||||
// need to wrap that in a type (see metaloop::Int).
|
||||
|
||||
/*
|
||||
* A helper alias to create integer values wrapped as a type. It is nessecary
|
||||
* A helper alias to create integer values wrapped as a type. It is necessary
|
||||
* because a non type template parameter (such as int) would be prohibited in
|
||||
* a partial specialization. Also for the same reason we have to use a class
|
||||
* _Metaloop instead of a simple function as a functor. A function cannot be
|
||||
* partially specialized in a way that is neccesary for this trick.
|
||||
* partially specialized in a way that is necessary for this trick.
|
||||
*/
|
||||
template<int N> using Int = std::integral_constant<int, N>;
|
||||
|
||||
@ -88,7 +88,7 @@ public:
|
||||
// It takes the real functor that can be specified in-place but only
|
||||
// with C++14 because the second parameter's type will depend on the
|
||||
// type of the parameter pack element that is processed. In C++14 we can
|
||||
// specify this second parameter type as auto in the lamda parameter list.
|
||||
// specify this second parameter type as auto in the lambda parameter list.
|
||||
inline MapFn(Fn&& fn): fn_(forward<Fn>(fn)) {}
|
||||
|
||||
template<class T> void operator ()(T&& pack_element) {
|
||||
@ -146,7 +146,7 @@ public:
|
||||
* version of run is called which does not call itself anymore.
|
||||
*
|
||||
* If you are utterly annoyed, at least you have learned a super crazy
|
||||
* functional metaprogramming pattern.
|
||||
* functional meta-programming pattern.
|
||||
*/
|
||||
template<class...Args>
|
||||
using MetaLoop = _MetaLoop<Int<sizeof...(Args)-1>, Args...>;
|
||||
|
@ -102,6 +102,9 @@ struct StopCriteria {
|
||||
/// If the relative value difference between two scores.
|
||||
double relative_score_difference = std::nan("");
|
||||
|
||||
/// Stop if this value or better is found.
|
||||
double stop_score = std::nan("");
|
||||
|
||||
unsigned max_iterations = 0;
|
||||
};
|
||||
|
||||
|
@ -142,10 +142,12 @@ protected:
|
||||
default: ;
|
||||
}
|
||||
|
||||
auto abs_diff = stopcr_.absolute_score_difference;
|
||||
auto rel_diff = stopcr_.relative_score_difference;
|
||||
double abs_diff = stopcr_.absolute_score_difference;
|
||||
double rel_diff = stopcr_.relative_score_difference;
|
||||
double stopval = stopcr_.stop_score;
|
||||
if(!std::isnan(abs_diff)) opt_.set_ftol_abs(abs_diff);
|
||||
if(!std::isnan(rel_diff)) opt_.set_ftol_rel(rel_diff);
|
||||
if(!std::isnan(stopval)) opt_.set_stopval(stopval);
|
||||
|
||||
if(this->stopcr_.max_iterations > 0)
|
||||
opt_.set_maxeval(this->stopcr_.max_iterations );
|
||||
|
@ -7,9 +7,24 @@
|
||||
|
||||
namespace libnest2d { namespace strategies {
|
||||
|
||||
template<class T, class = T> struct Epsilon {};
|
||||
|
||||
template<class T>
|
||||
struct Epsilon<T, enable_if_t<std::is_integral<T>::value, T> > {
|
||||
static const T Value = 1;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct Epsilon<T, enable_if_t<std::is_floating_point<T>::value, T> > {
|
||||
static const T Value = 1e-3;
|
||||
};
|
||||
|
||||
template<class RawShape>
|
||||
struct BLConfig {
|
||||
TCoord<TPoint<RawShape>> min_obj_distance = 0;
|
||||
DECLARE_MAIN_TYPES(RawShape);
|
||||
|
||||
Coord min_obj_distance = 0;
|
||||
Coord epsilon = Epsilon<Coord>::Value;
|
||||
bool allow_rotations = false;
|
||||
};
|
||||
|
||||
@ -69,20 +84,21 @@ protected:
|
||||
setInitialPosition(item);
|
||||
|
||||
Unit d = availableSpaceDown(item);
|
||||
bool can_move = d > 1 /*std::numeric_limits<Unit>::epsilon()*/;
|
||||
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+1});
|
||||
item.translate({0, -d+eps});
|
||||
d = availableSpaceLeft(item);
|
||||
can_move = d > 1/*std::numeric_limits<Unit>::epsilon()*/;
|
||||
can_move = d > eps;
|
||||
left = false;
|
||||
} else { // write previous left move and go down
|
||||
item.translate({-d+1, 0});
|
||||
item.translate({-d+eps, 0});
|
||||
d = availableSpaceDown(item);
|
||||
can_move = d > 1/*std::numeric_limits<Unit>::epsilon()*/;
|
||||
can_move = d > eps;
|
||||
left = true;
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,15 @@
|
||||
#define NOFITPOLY_HPP
|
||||
|
||||
#include <cassert>
|
||||
#include <random>
|
||||
|
||||
// For caching nfps
|
||||
#include <unordered_map>
|
||||
|
||||
// For parallel for
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <future>
|
||||
#include <atomic>
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <iostream>
|
||||
@ -13,8 +21,84 @@
|
||||
|
||||
#include "tools/svgtools.hpp"
|
||||
|
||||
namespace libnest2d {
|
||||
|
||||
namespace libnest2d { namespace strategies {
|
||||
namespace __parallel {
|
||||
|
||||
using std::function;
|
||||
using std::iterator_traits;
|
||||
template<class It>
|
||||
using TIteratorValue = typename iterator_traits<It>::value_type;
|
||||
|
||||
template<class Iterator>
|
||||
inline void enumerate(
|
||||
Iterator from, Iterator to,
|
||||
function<void(TIteratorValue<Iterator>, unsigned)> fn,
|
||||
std::launch policy = std::launch::deferred | std::launch::async)
|
||||
{
|
||||
auto N = to-from;
|
||||
std::vector<std::future<void>> rets(N);
|
||||
|
||||
auto it = from;
|
||||
for(unsigned b = 0; b < N; b++) {
|
||||
rets[b] = std::async(policy, fn, *it++, b);
|
||||
}
|
||||
|
||||
for(unsigned fi = 0; fi < rets.size(); ++fi) rets[fi].wait();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace __itemhash {
|
||||
|
||||
using Key = size_t;
|
||||
|
||||
template<class S>
|
||||
Key hash(const _Item<S>& item) {
|
||||
using Point = TPoint<S>;
|
||||
using Segment = _Segment<Point>;
|
||||
|
||||
static const int N = 26;
|
||||
static const int M = N*N - 1;
|
||||
|
||||
std::string ret;
|
||||
auto& rhs = item.rawShape();
|
||||
auto& ctr = sl::getContour(rhs);
|
||||
auto it = ctr.begin();
|
||||
auto nx = std::next(it);
|
||||
|
||||
double circ = 0;
|
||||
while(nx != ctr.end()) {
|
||||
Segment seg(*it++, *nx++);
|
||||
Radians a = seg.angleToXaxis();
|
||||
double deg = Degrees(a);
|
||||
int ms = 'A', ls = 'A';
|
||||
while(deg > N) { ms++; deg -= N; }
|
||||
ls += int(deg);
|
||||
ret.push_back(char(ms)); ret.push_back(char(ls));
|
||||
circ += seg.length();
|
||||
}
|
||||
|
||||
it = ctr.begin(); nx = std::next(it);
|
||||
|
||||
while(nx != ctr.end()) {
|
||||
Segment seg(*it++, *nx++);
|
||||
auto l = int(M * seg.length() / circ);
|
||||
int ms = 'A', ls = 'A';
|
||||
while(l > N) { ms++; l -= N; }
|
||||
ls += l;
|
||||
ret.push_back(char(ms)); ret.push_back(char(ls));
|
||||
}
|
||||
|
||||
return std::hash<std::string>()(ret);
|
||||
}
|
||||
|
||||
template<class S>
|
||||
using Hash = std::unordered_map<Key, nfp::NfpResult<S>>;
|
||||
|
||||
}
|
||||
|
||||
namespace strategies {
|
||||
|
||||
template<class RawShape>
|
||||
struct NfpPConfig {
|
||||
@ -71,7 +155,7 @@ struct NfpPConfig {
|
||||
* decisions (for you or a more intelligent AI).
|
||||
*
|
||||
*/
|
||||
std::function<double(nfp::Shapes<RawShape>&, const _Item<RawShape>&,
|
||||
std::function<double(const nfp::Shapes<RawShape>&, const _Item<RawShape>&,
|
||||
const ItemGroup&)>
|
||||
object_function;
|
||||
|
||||
@ -80,7 +164,7 @@ struct NfpPConfig {
|
||||
* This is a compromise slider between quality and speed. Zero is the
|
||||
* fast and poor solution while 1.0 is the slowest but most accurate.
|
||||
*/
|
||||
float accuracy = 1.0;
|
||||
float accuracy = 0.65f;
|
||||
|
||||
/**
|
||||
* @brief If you want to see items inside other item's holes, you have to
|
||||
@ -91,6 +175,11 @@ struct NfpPConfig {
|
||||
*/
|
||||
bool explore_holes = false;
|
||||
|
||||
/**
|
||||
* @brief If true, use all CPUs available. Run on a single core otherwise.
|
||||
*/
|
||||
bool parallel = true;
|
||||
|
||||
NfpPConfig(): rotations({0.0, Pi/2.0, Pi, 3*Pi/2}),
|
||||
alignment(Alignment::CENTER), starting_point(Alignment::CENTER) {}
|
||||
};
|
||||
@ -325,121 +414,8 @@ inline void correctNfpPosition(nfp::NfpResult<RawShape>& nfp,
|
||||
shapelike::translate(nfp.first, dnfp);
|
||||
}
|
||||
|
||||
template<class RawShape, class Container>
|
||||
nfp::Shapes<RawShape> calcnfp( const Container& polygons,
|
||||
const _Item<RawShape>& trsh,
|
||||
Lvl<nfp::NfpLevel::CONVEX_ONLY>)
|
||||
{
|
||||
using Item = _Item<RawShape>;
|
||||
using namespace nfp;
|
||||
|
||||
nfp::Shapes<RawShape> nfps;
|
||||
|
||||
// int pi = 0;
|
||||
for(Item& sh : polygons) {
|
||||
auto subnfp_r = noFitPolygon<NfpLevel::CONVEX_ONLY>(
|
||||
sh.transformedShape(), trsh.transformedShape());
|
||||
#ifndef NDEBUG
|
||||
auto vv = sl::isValid(sh.transformedShape());
|
||||
assert(vv.first);
|
||||
|
||||
auto vnfp = sl::isValid(subnfp_r.first);
|
||||
assert(vnfp.first);
|
||||
#endif
|
||||
|
||||
correctNfpPosition(subnfp_r, sh, trsh);
|
||||
|
||||
nfps = nfp::merge(nfps, subnfp_r.first);
|
||||
|
||||
// double SCALE = 1000000;
|
||||
// using SVGWriter = svg::SVGWriter<RawShape>;
|
||||
// SVGWriter::Config conf;
|
||||
// conf.mm_in_coord_units = SCALE;
|
||||
// SVGWriter svgw(conf);
|
||||
// Box bin(250*SCALE, 210*SCALE);
|
||||
// svgw.setSize(bin);
|
||||
// for(int i = 0; i <= pi; i++) svgw.writeItem(polygons[i]);
|
||||
// svgw.writeItem(trsh);
|
||||
//// svgw.writeItem(Item(subnfp_r.first));
|
||||
// for(auto& n : nfps) svgw.writeItem(Item(n));
|
||||
// svgw.save("nfpout");
|
||||
// pi++;
|
||||
}
|
||||
|
||||
return nfps;
|
||||
}
|
||||
|
||||
template<class RawShape, class Container, class Level>
|
||||
nfp::Shapes<RawShape> calcnfp( const Container& polygons,
|
||||
const _Item<RawShape>& trsh,
|
||||
Level)
|
||||
{
|
||||
using namespace nfp;
|
||||
using Item = _Item<RawShape>;
|
||||
|
||||
Shapes<RawShape> nfps;
|
||||
|
||||
auto& orb = trsh.transformedShape();
|
||||
bool orbconvex = trsh.isContourConvex();
|
||||
|
||||
for(Item& sh : polygons) {
|
||||
nfp::NfpResult<RawShape> subnfp;
|
||||
auto& stat = sh.transformedShape();
|
||||
|
||||
if(sh.isContourConvex() && orbconvex)
|
||||
subnfp = nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(stat, orb);
|
||||
else if(orbconvex)
|
||||
subnfp = nfp::noFitPolygon<NfpLevel::ONE_CONVEX>(stat, orb);
|
||||
else
|
||||
subnfp = nfp::noFitPolygon<Level::value>(stat, orb);
|
||||
|
||||
correctNfpPosition(subnfp, sh, trsh);
|
||||
|
||||
nfps = nfp::merge(nfps, subnfp.first);
|
||||
}
|
||||
|
||||
return nfps;
|
||||
|
||||
|
||||
// using Item = _Item<RawShape>;
|
||||
// using sl = ShapeLike;
|
||||
|
||||
// Nfp::Shapes<RawShape> nfps, stationary;
|
||||
|
||||
// for(Item& sh : polygons) {
|
||||
// stationary = Nfp::merge(stationary, sh.transformedShape());
|
||||
// }
|
||||
|
||||
// for(RawShape& sh : stationary) {
|
||||
|
||||
//// auto vv = sl::isValid(sh);
|
||||
//// std::cout << vv.second << std::endl;
|
||||
|
||||
|
||||
// Nfp::NfpResult<RawShape> subnfp;
|
||||
// bool shconvex = sl::isConvex<RawShape>(sl::getContour(sh));
|
||||
// if(shconvex && trsh.isContourConvex()) {
|
||||
// subnfp = Nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(
|
||||
// sh, trsh.transformedShape());
|
||||
// } else if(trsh.isContourConvex()) {
|
||||
// subnfp = Nfp::noFitPolygon<NfpLevel::ONE_CONVEX>(
|
||||
// sh, trsh.transformedShape());
|
||||
// }
|
||||
// else {
|
||||
// subnfp = Nfp::noFitPolygon<Level::value>( sh,
|
||||
// trsh.transformedShape());
|
||||
// }
|
||||
|
||||
// correctNfpPosition(subnfp, sh, trsh);
|
||||
|
||||
// nfps = Nfp::merge(nfps, subnfp.first);
|
||||
// }
|
||||
|
||||
// return nfps;
|
||||
}
|
||||
|
||||
template<class RawShape>
|
||||
_Circle<TPoint<RawShape>> minimizeCircle(const RawShape& sh) {
|
||||
template<class RawShape, class Circle = _Circle<TPoint<RawShape>> >
|
||||
Circle minimizeCircle(const RawShape& sh) {
|
||||
using Point = TPoint<RawShape>;
|
||||
using Coord = TCoord<Point>;
|
||||
|
||||
@ -507,9 +483,19 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin
|
||||
|
||||
using Box = _Box<TPoint<RawShape>>;
|
||||
|
||||
using MaxNfpLevel = nfp::MaxNfpLevel<RawShape>;
|
||||
|
||||
using ItemKeys = std::vector<__itemhash::Key>;
|
||||
|
||||
// Norming factor for the optimization function
|
||||
const double norm_;
|
||||
|
||||
using MaxNfpLevel = nfp::MaxNfpLevel<RawShape>;
|
||||
// Caching calculated nfps
|
||||
__itemhash::Hash<RawShape> nfpcache_;
|
||||
|
||||
// Storing item hash keys
|
||||
ItemKeys item_keys_;
|
||||
|
||||
public:
|
||||
|
||||
using Pile = nfp::Shapes<RawShape>;
|
||||
@ -526,60 +512,290 @@ public:
|
||||
_NofitPolyPlacer& operator=(_NofitPolyPlacer&&) BP2D_NOEXCEPT = default;
|
||||
#endif
|
||||
|
||||
bool static inline wouldFit(const Box& bb, const RawShape& bin) {
|
||||
auto bbin = sl::boundingBox<RawShape>(bin);
|
||||
static inline double overfit(const Box& bb, const RawShape& bin) {
|
||||
auto bbin = sl::boundingBox(bin);
|
||||
auto d = bbin.center() - bb.center();
|
||||
_Rectangle<RawShape> rect(bb.width(), bb.height());
|
||||
rect.translate(bb.minCorner() + d);
|
||||
return sl::isInside<RawShape>(rect.transformedShape(), bin);
|
||||
return sl::isInside(rect.transformedShape(), bin) ? -1.0 : 1;
|
||||
}
|
||||
|
||||
bool static inline wouldFit(const RawShape& chull, const RawShape& bin) {
|
||||
auto bbch = sl::boundingBox<RawShape>(chull);
|
||||
auto bbin = sl::boundingBox<RawShape>(bin);
|
||||
static inline double overfit(const RawShape& chull, const RawShape& bin) {
|
||||
auto bbch = sl::boundingBox(chull);
|
||||
auto bbin = sl::boundingBox(bin);
|
||||
auto d = bbch.center() - bbin.center();
|
||||
auto chullcpy = chull;
|
||||
sl::translate(chullcpy, d);
|
||||
return sl::isInside<RawShape>(chullcpy, bin);
|
||||
return sl::isInside(chullcpy, bin) ? -1.0 : 1.0;
|
||||
}
|
||||
|
||||
bool static inline wouldFit(const RawShape& chull, const Box& bin)
|
||||
static inline double overfit(const RawShape& chull, const Box& bin)
|
||||
{
|
||||
auto bbch = sl::boundingBox<RawShape>(chull);
|
||||
return wouldFit(bbch, bin);
|
||||
auto bbch = sl::boundingBox(chull);
|
||||
return overfit(bbch, bin);
|
||||
}
|
||||
|
||||
bool static inline wouldFit(const Box& bb, const Box& bin)
|
||||
static inline double overfit(const Box& bb, const Box& bin)
|
||||
{
|
||||
return bb.width() <= bin.width() && bb.height() <= bin.height();
|
||||
auto wdiff = double(bb.width() - bin.width());
|
||||
auto hdiff = double(bb.height() - bin.height());
|
||||
double diff = 0;
|
||||
if(wdiff > 0) diff += wdiff;
|
||||
if(hdiff > 0) diff += hdiff;
|
||||
return diff;
|
||||
}
|
||||
|
||||
bool static inline wouldFit(const Box& bb, const _Circle<Vertex>& bin)
|
||||
static inline double overfit(const Box& bb, const _Circle<Vertex>& bin)
|
||||
{
|
||||
|
||||
return sl::isInside<RawShape>(bb, bin);
|
||||
double boxr = 0.5*pl::distance(bb.minCorner(), bb.maxCorner());
|
||||
double diff = boxr - bin.radius();
|
||||
return diff;
|
||||
}
|
||||
|
||||
bool static inline wouldFit(const RawShape& chull,
|
||||
static inline double overfit(const RawShape& chull,
|
||||
const _Circle<Vertex>& bin)
|
||||
{
|
||||
return boundingCircle(chull).radius() < bin.radius();
|
||||
double r = boundingCircle(chull).radius();
|
||||
double diff = r - bin.radius();
|
||||
return diff;
|
||||
}
|
||||
|
||||
template<class Range = ConstItemRange<typename Base::DefaultIter>>
|
||||
PackResult trypack(
|
||||
PackResult trypack(Item& item,
|
||||
const Range& remaining = Range()) {
|
||||
auto result = _trypack(item, remaining);
|
||||
|
||||
// Experimental
|
||||
// if(!result) repack(item, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
~_NofitPolyPlacer() {
|
||||
clearItems();
|
||||
}
|
||||
|
||||
inline void clearItems() {
|
||||
finalAlign(bin_);
|
||||
Base::clearItems();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
using Shapes = TMultiShape<RawShape>;
|
||||
using ItemRef = std::reference_wrapper<Item>;
|
||||
using ItemWithHash = const std::pair<ItemRef, __itemhash::Key>;
|
||||
|
||||
Shapes calcnfp(const ItemWithHash itsh, Lvl<nfp::NfpLevel::CONVEX_ONLY>)
|
||||
{
|
||||
using namespace nfp;
|
||||
|
||||
Shapes nfps;
|
||||
const Item& trsh = itsh.first;
|
||||
// nfps.reserve(polygons.size());
|
||||
|
||||
// unsigned idx = 0;
|
||||
for(Item& sh : items_) {
|
||||
|
||||
// auto ik = item_keys_[idx++] + itsh.second;
|
||||
// auto fnd = nfpcache_.find(ik);
|
||||
|
||||
// nfp::NfpResult<RawShape> subnfp_r;
|
||||
// if(fnd == nfpcache_.end()) {
|
||||
|
||||
auto subnfp_r = noFitPolygon<NfpLevel::CONVEX_ONLY>(
|
||||
sh.transformedShape(), trsh.transformedShape());
|
||||
// nfpcache_[ik] = subnfp_r;
|
||||
// } else {
|
||||
// subnfp_r = fnd->second;
|
||||
// }
|
||||
|
||||
correctNfpPosition(subnfp_r, sh, trsh);
|
||||
|
||||
// nfps.emplace_back(subnfp_r.first);
|
||||
nfps = nfp::merge(nfps, subnfp_r.first);
|
||||
}
|
||||
|
||||
// nfps = nfp::merge(nfps);
|
||||
|
||||
return nfps;
|
||||
}
|
||||
|
||||
template<class Level>
|
||||
Shapes calcnfp( const ItemWithHash itsh, Level)
|
||||
{ // Function for arbitrary level of nfp implementation
|
||||
using namespace nfp;
|
||||
|
||||
Shapes nfps;
|
||||
const Item& trsh = itsh.first;
|
||||
|
||||
auto& orb = trsh.transformedShape();
|
||||
bool orbconvex = trsh.isContourConvex();
|
||||
|
||||
for(Item& sh : items_) {
|
||||
nfp::NfpResult<RawShape> subnfp;
|
||||
auto& stat = sh.transformedShape();
|
||||
|
||||
if(sh.isContourConvex() && orbconvex)
|
||||
subnfp = nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(stat, orb);
|
||||
else if(orbconvex)
|
||||
subnfp = nfp::noFitPolygon<NfpLevel::ONE_CONVEX>(stat, orb);
|
||||
else
|
||||
subnfp = nfp::noFitPolygon<Level::value>(stat, orb);
|
||||
|
||||
correctNfpPosition(subnfp, sh, trsh);
|
||||
|
||||
nfps = nfp::merge(nfps, subnfp.first);
|
||||
}
|
||||
|
||||
return nfps;
|
||||
}
|
||||
|
||||
// Very much experimental
|
||||
void repack(Item& item, PackResult& result) {
|
||||
|
||||
if((sl::area(bin_) - this->filledArea()) >= item.area()) {
|
||||
auto prev_func = config_.object_function;
|
||||
|
||||
unsigned iter = 0;
|
||||
ItemGroup backup_rf = items_;
|
||||
std::vector<Item> backup_cpy;
|
||||
for(Item& itm : items_) backup_cpy.emplace_back(itm);
|
||||
|
||||
auto ofn = [this, &item, &result, &iter, &backup_cpy, &backup_rf]
|
||||
(double ratio)
|
||||
{
|
||||
auto& bin = bin_;
|
||||
iter++;
|
||||
config_.object_function = [bin, ratio](
|
||||
nfp::Shapes<RawShape>& pile,
|
||||
const Item& item,
|
||||
const ItemGroup& /*remaining*/)
|
||||
{
|
||||
pile.emplace_back(item.transformedShape());
|
||||
auto ch = sl::convexHull(pile);
|
||||
auto pbb = sl::boundingBox(pile);
|
||||
pile.pop_back();
|
||||
|
||||
double parea = 0.5*(sl::area(ch) + sl::area(pbb));
|
||||
|
||||
double pile_area = std::accumulate(
|
||||
pile.begin(), pile.end(), item.area(),
|
||||
[](double sum, const RawShape& sh){
|
||||
return sum + sl::area(sh);
|
||||
});
|
||||
|
||||
// The pack ratio -- how much is the convex hull occupied
|
||||
double pack_rate = (pile_area)/parea;
|
||||
|
||||
// ratio of waste
|
||||
double waste = 1.0 - pack_rate;
|
||||
|
||||
// Score is the square root of waste. This will extend the
|
||||
// range of good (lower) values and shrink the range of bad
|
||||
// (larger) values.
|
||||
auto wscore = std::sqrt(waste);
|
||||
|
||||
|
||||
auto ibb = item.boundingBox();
|
||||
auto bbb = sl::boundingBox(bin);
|
||||
auto c = ibb.center();
|
||||
double norm = 0.5*pl::distance(bbb.minCorner(),
|
||||
bbb.maxCorner());
|
||||
|
||||
double dscore = pl::distance(c, pbb.center()) / norm;
|
||||
|
||||
return ratio*wscore + (1.0 - ratio) * dscore;
|
||||
};
|
||||
|
||||
auto bb = sl::boundingBox(bin);
|
||||
double norm = bb.width() + bb.height();
|
||||
|
||||
auto items = items_;
|
||||
clearItems();
|
||||
auto it = items.begin();
|
||||
while(auto pr = _trypack(*it++)) {
|
||||
this->accept(pr); if(it == items.end()) break;
|
||||
}
|
||||
|
||||
auto count_diff = items.size() - items_.size();
|
||||
double score = count_diff;
|
||||
|
||||
if(count_diff == 0) {
|
||||
result = _trypack(item);
|
||||
|
||||
if(result) {
|
||||
std::cout << "Success" << std::endl;
|
||||
score = 0.0;
|
||||
} else {
|
||||
score += result.overfit() / norm;
|
||||
}
|
||||
} else {
|
||||
result = PackResult();
|
||||
items_ = backup_rf;
|
||||
for(unsigned i = 0; i < items_.size(); i++) {
|
||||
items_[i].get() = backup_cpy[i];
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << iter << " repack result: " << score << " "
|
||||
<< ratio << " " << count_diff << std::endl;
|
||||
|
||||
return score;
|
||||
};
|
||||
|
||||
opt::StopCriteria stopcr;
|
||||
stopcr.max_iterations = 30;
|
||||
stopcr.stop_score = 1e-20;
|
||||
opt::TOptimizer<opt::Method::L_SUBPLEX> solver(stopcr);
|
||||
solver.optimize_min(ofn, opt::initvals(0.5),
|
||||
opt::bound(0.0, 1.0));
|
||||
|
||||
// optimize
|
||||
config_.object_function = prev_func;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct Optimum {
|
||||
double relpos;
|
||||
unsigned nfpidx;
|
||||
int hidx;
|
||||
Optimum(double pos, unsigned nidx):
|
||||
relpos(pos), nfpidx(nidx), hidx(-1) {}
|
||||
Optimum(double pos, unsigned nidx, int holeidx):
|
||||
relpos(pos), nfpidx(nidx), hidx(holeidx) {}
|
||||
};
|
||||
|
||||
class Optimizer: public opt::TOptimizer<opt::Method::L_SUBPLEX> {
|
||||
public:
|
||||
Optimizer() {
|
||||
opt::StopCriteria stopcr;
|
||||
stopcr.max_iterations = 200;
|
||||
stopcr.relative_score_difference = 1e-20;
|
||||
this->stopcr_ = stopcr;
|
||||
}
|
||||
};
|
||||
|
||||
using Edges = EdgeCache<RawShape>;
|
||||
|
||||
template<class Range = ConstItemRange<typename Base::DefaultIter>>
|
||||
PackResult _trypack(
|
||||
Item& item,
|
||||
const Range& remaining = Range()) {
|
||||
|
||||
PackResult ret;
|
||||
|
||||
bool can_pack = false;
|
||||
double best_overfit = std::numeric_limits<double>::max();
|
||||
|
||||
auto remlist = ItemGroup(remaining.from, remaining.to);
|
||||
size_t itemhash = __itemhash::hash(item);
|
||||
|
||||
if(items_.empty()) {
|
||||
setInitialPosition(item);
|
||||
can_pack = item.isInside(bin_);
|
||||
best_overfit = overfit(item.transformedShape(), bin_);
|
||||
can_pack = best_overfit <= 0;
|
||||
} else {
|
||||
|
||||
double global_score = std::numeric_limits<double>::max();
|
||||
@ -588,7 +804,7 @@ public:
|
||||
auto initial_rot = item.rotation();
|
||||
Vertex final_tr = {0, 0};
|
||||
Radians final_rot = initial_rot;
|
||||
nfp::Shapes<RawShape> nfps;
|
||||
Shapes nfps;
|
||||
|
||||
for(auto rot : config_.rotations) {
|
||||
|
||||
@ -596,17 +812,16 @@ public:
|
||||
item.rotation(initial_rot + rot);
|
||||
|
||||
// place the new item outside of the print bed to make sure
|
||||
// it is disjuct from the current merged pile
|
||||
// it is disjunct from the current merged pile
|
||||
placeOutsideOfBin(item);
|
||||
|
||||
auto trsh = item.transformedShape();
|
||||
nfps = calcnfp({item, itemhash}, Lvl<MaxNfpLevel::value>());
|
||||
|
||||
nfps = calcnfp(items_, item, Lvl<MaxNfpLevel::value>());
|
||||
auto iv = nfp::referenceVertex(trsh);
|
||||
auto iv = item.referenceVertex();
|
||||
|
||||
auto startpos = item.translation();
|
||||
|
||||
std::vector<EdgeCache<RawShape>> ecache;
|
||||
std::vector<Edges> ecache;
|
||||
ecache.reserve(nfps.size());
|
||||
|
||||
for(auto& nfp : nfps ) {
|
||||
@ -614,14 +829,54 @@ public:
|
||||
ecache.back().accuracy(config_.accuracy);
|
||||
}
|
||||
|
||||
struct Optimum {
|
||||
double relpos;
|
||||
unsigned nfpidx;
|
||||
int hidx;
|
||||
Optimum(double pos, unsigned nidx):
|
||||
relpos(pos), nfpidx(nidx), hidx(-1) {}
|
||||
Optimum(double pos, unsigned nidx, int holeidx):
|
||||
relpos(pos), nfpidx(nidx), hidx(holeidx) {}
|
||||
Shapes pile;
|
||||
pile.reserve(items_.size()+1);
|
||||
// double pile_area = 0;
|
||||
for(Item& mitem : items_) {
|
||||
pile.emplace_back(mitem.transformedShape());
|
||||
// pile_area += mitem.area();
|
||||
}
|
||||
|
||||
auto merged_pile = nfp::merge(pile);
|
||||
auto& bin = bin_;
|
||||
double norm = norm_;
|
||||
|
||||
// This is the kernel part of the object function that is
|
||||
// customizable by the library client
|
||||
auto _objfunc = config_.object_function?
|
||||
config_.object_function :
|
||||
[norm, /*pile_area,*/ bin, merged_pile](
|
||||
const Pile& /*pile*/,
|
||||
const Item& item,
|
||||
const ItemGroup& /*remaining*/)
|
||||
{
|
||||
auto ibb = item.boundingBox();
|
||||
auto binbb = sl::boundingBox(bin);
|
||||
auto mp = merged_pile;
|
||||
mp.emplace_back(item.transformedShape());
|
||||
auto fullbb = sl::boundingBox(mp);
|
||||
|
||||
double score = pl::distance(ibb.center(), binbb.center());
|
||||
score /= norm;
|
||||
|
||||
double miss = overfit(fullbb, bin);
|
||||
miss = miss > 0? miss : 0;
|
||||
score += std::pow(miss, 2);
|
||||
|
||||
return score;
|
||||
};
|
||||
|
||||
// Our object function for placement
|
||||
auto rawobjfunc =
|
||||
[item, _objfunc, iv,
|
||||
startpos, remlist, pile] (Vertex v)
|
||||
{
|
||||
auto d = v - iv;
|
||||
d += startpos;
|
||||
Item itm = item;
|
||||
itm.translation(d);
|
||||
|
||||
return _objfunc(pile, itm, remlist);
|
||||
};
|
||||
|
||||
auto getNfpPoint = [&ecache](const Optimum& opt)
|
||||
@ -630,58 +885,10 @@ public:
|
||||
ecache[opt.nfpidx].coords(opt.hidx, opt.relpos);
|
||||
};
|
||||
|
||||
nfp::Shapes<RawShape> pile;
|
||||
pile.reserve(items_.size()+1);
|
||||
double pile_area = 0;
|
||||
for(Item& mitem : items_) {
|
||||
pile.emplace_back(mitem.transformedShape());
|
||||
pile_area += mitem.area();
|
||||
}
|
||||
|
||||
auto merged_pile = nfp::merge(pile);
|
||||
|
||||
// This is the kernel part of the object function that is
|
||||
// customizable by the library client
|
||||
auto _objfunc = config_.object_function?
|
||||
config_.object_function :
|
||||
[this, &merged_pile, &pile_area](
|
||||
nfp::Shapes<RawShape>& /*pile*/,
|
||||
const Item& item,
|
||||
const ItemGroup& /*remaining*/)
|
||||
auto boundaryCheck =
|
||||
[&merged_pile, &getNfpPoint, &item, &bin, &iv, &startpos]
|
||||
(const Optimum& o)
|
||||
{
|
||||
merged_pile.emplace_back(item.transformedShape());
|
||||
auto ch = sl::convexHull(merged_pile);
|
||||
merged_pile.pop_back();
|
||||
|
||||
// The pack ratio -- how much is the convex hull occupied
|
||||
double pack_rate = (pile_area + item.area())/sl::area(ch);
|
||||
|
||||
// ratio of waste
|
||||
double waste = 1.0 - pack_rate;
|
||||
|
||||
// Score is the square root of waste. This will extend the
|
||||
// range of good (lower) values and shring the range of bad
|
||||
// (larger) values.
|
||||
auto score = std::sqrt(waste);
|
||||
|
||||
if(!wouldFit(ch, bin_)) score += norm_;
|
||||
|
||||
return score;
|
||||
};
|
||||
|
||||
// Our object function for placement
|
||||
auto rawobjfunc = [&] (Vertex v)
|
||||
{
|
||||
auto d = v - iv;
|
||||
d += startpos;
|
||||
item.translation(d);
|
||||
|
||||
double score = _objfunc(pile, item, remlist);
|
||||
|
||||
return score;
|
||||
};
|
||||
|
||||
auto boundaryCheck = [&](const Optimum& o) {
|
||||
auto v = getNfpPoint(o);
|
||||
auto d = v - iv;
|
||||
d += startpos;
|
||||
@ -691,84 +898,111 @@ public:
|
||||
auto chull = sl::convexHull(merged_pile);
|
||||
merged_pile.pop_back();
|
||||
|
||||
return wouldFit(chull, bin_);
|
||||
return overfit(chull, bin);
|
||||
};
|
||||
|
||||
opt::StopCriteria stopcr;
|
||||
stopcr.max_iterations = 200;
|
||||
stopcr.relative_score_difference = 1e-20;
|
||||
opt::TOptimizer<opt::Method::L_SUBPLEX> solver(stopcr);
|
||||
|
||||
Optimum optimum(0, 0);
|
||||
double best_score = std::numeric_limits<double>::max();
|
||||
std::launch policy = std::launch::deferred;
|
||||
if(config_.parallel) policy |= std::launch::async;
|
||||
|
||||
using OptResult = opt::Result<double>;
|
||||
using OptResults = std::vector<OptResult>;
|
||||
|
||||
// Local optimization with the four polygon corners as
|
||||
// starting points
|
||||
for(unsigned ch = 0; ch < ecache.size(); ch++) {
|
||||
auto& cache = ecache[ch];
|
||||
|
||||
auto contour_ofn = [&rawobjfunc, &getNfpPoint, ch]
|
||||
auto contour_ofn = [rawobjfunc, getNfpPoint, ch]
|
||||
(double relpos)
|
||||
{
|
||||
return rawobjfunc(getNfpPoint(Optimum(relpos, ch)));
|
||||
};
|
||||
|
||||
std::for_each(cache.corners().begin(),
|
||||
cache.corners().end(),
|
||||
[ch, &contour_ofn, &solver, &best_score,
|
||||
&optimum, &boundaryCheck] (double pos)
|
||||
OptResults results(cache.corners().size());
|
||||
|
||||
__parallel::enumerate(
|
||||
cache.corners().begin(),
|
||||
cache.corners().end(),
|
||||
[&contour_ofn, &results]
|
||||
(double pos, unsigned n)
|
||||
{
|
||||
Optimizer solver;
|
||||
try {
|
||||
auto result = solver.optimize_min(contour_ofn,
|
||||
results[n] = solver.optimize_min(contour_ofn,
|
||||
opt::initvals<double>(pos),
|
||||
opt::bound<double>(0, 1.0)
|
||||
);
|
||||
|
||||
if(result.score < best_score) {
|
||||
Optimum o(std::get<0>(result.optimum), ch, -1);
|
||||
if(boundaryCheck(o)) {
|
||||
best_score = result.score;
|
||||
optimum = o;
|
||||
}
|
||||
}
|
||||
} catch(std::exception& e) {
|
||||
derr() << "ERROR: " << e.what() << "\n";
|
||||
}
|
||||
});
|
||||
}, policy);
|
||||
|
||||
auto resultcomp =
|
||||
[]( const OptResult& r1, const OptResult& r2 ) {
|
||||
return r1.score < r2.score;
|
||||
};
|
||||
|
||||
auto mr = *std::min_element(results.begin(), results.end(),
|
||||
resultcomp);
|
||||
|
||||
if(mr.score < best_score) {
|
||||
Optimum o(std::get<0>(mr.optimum), ch, -1);
|
||||
double miss = boundaryCheck(o);
|
||||
if(miss <= 0) {
|
||||
best_score = mr.score;
|
||||
optimum = o;
|
||||
} else {
|
||||
best_overfit = std::min(miss, best_overfit);
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned hidx = 0; hidx < cache.holeCount(); ++hidx) {
|
||||
auto hole_ofn =
|
||||
[&rawobjfunc, &getNfpPoint, ch, hidx]
|
||||
[rawobjfunc, getNfpPoint, ch, hidx]
|
||||
(double pos)
|
||||
{
|
||||
Optimum opt(pos, ch, hidx);
|
||||
return rawobjfunc(getNfpPoint(opt));
|
||||
};
|
||||
|
||||
std::for_each(cache.corners(hidx).begin(),
|
||||
results.clear();
|
||||
results.resize(cache.corners(hidx).size());
|
||||
|
||||
// TODO : use parallel for
|
||||
__parallel::enumerate(cache.corners(hidx).begin(),
|
||||
cache.corners(hidx).end(),
|
||||
[&hole_ofn, &solver, &best_score,
|
||||
&optimum, ch, hidx, &boundaryCheck]
|
||||
(double pos)
|
||||
[&hole_ofn, &results]
|
||||
(double pos, unsigned n)
|
||||
{
|
||||
Optimizer solver;
|
||||
try {
|
||||
auto result = solver.optimize_min(hole_ofn,
|
||||
results[n] = solver.optimize_min(hole_ofn,
|
||||
opt::initvals<double>(pos),
|
||||
opt::bound<double>(0, 1.0)
|
||||
);
|
||||
|
||||
if(result.score < best_score) {
|
||||
Optimum o(std::get<0>(result.optimum),
|
||||
ch, hidx);
|
||||
if(boundaryCheck(o)) {
|
||||
best_score = result.score;
|
||||
optimum = o;
|
||||
}
|
||||
}
|
||||
} catch(std::exception& e) {
|
||||
derr() << "ERROR: " << e.what() << "\n";
|
||||
}
|
||||
});
|
||||
}, policy);
|
||||
|
||||
auto hmr = *std::min_element(results.begin(),
|
||||
results.end(),
|
||||
resultcomp);
|
||||
|
||||
if(hmr.score < best_score) {
|
||||
Optimum o(std::get<0>(hmr.optimum),
|
||||
ch, hidx);
|
||||
double miss = boundaryCheck(o);
|
||||
if(miss <= 0.0) {
|
||||
best_score = hmr.score;
|
||||
optimum = o;
|
||||
} else {
|
||||
best_overfit = std::min(miss, best_overfit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -788,22 +1022,14 @@ public:
|
||||
|
||||
if(can_pack) {
|
||||
ret = PackResult(item);
|
||||
item_keys_.emplace_back(itemhash);
|
||||
} else {
|
||||
ret = PackResult(best_overfit);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
~_NofitPolyPlacer() {
|
||||
clearItems();
|
||||
}
|
||||
|
||||
inline void clearItems() {
|
||||
finalAlign(bin_);
|
||||
Base::clearItems();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
inline void finalAlign(const RawShape& pbin) {
|
||||
auto bbin = sl::boundingBox(pbin);
|
||||
finalAlign(bbin);
|
||||
@ -826,7 +1052,7 @@ private:
|
||||
nfp::Shapes<RawShape> m;
|
||||
m.reserve(items_.size());
|
||||
for(Item& item : items_) m.emplace_back(item.transformedShape());
|
||||
auto&& bb = sl::boundingBox<RawShape>(m);
|
||||
auto&& bb = sl::boundingBox(m);
|
||||
|
||||
Vertex ci, cb;
|
||||
|
||||
|
@ -26,15 +26,21 @@ 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()) {}
|
||||
PackResult(): item_ptr_(nullptr) {}
|
||||
|
||||
PackResult(double overfit = 1.0):
|
||||
item_ptr_(nullptr), overfit_(overfit) {}
|
||||
|
||||
public:
|
||||
operator bool() { return item_ptr_ != nullptr; }
|
||||
double overfit() const { return overfit_; }
|
||||
};
|
||||
|
||||
inline PlacerBoilerplate(const BinType& bin, unsigned cap = 50): bin_(bin)
|
||||
@ -82,9 +88,6 @@ public:
|
||||
inline void clearItems() {
|
||||
items_.clear();
|
||||
farea_valid_ = false;
|
||||
#ifndef NDEBUG
|
||||
debug_items_.clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
inline double filledArea() const {
|
||||
@ -101,10 +104,6 @@ public:
|
||||
return farea_;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
std::vector<Item> debug_items_;
|
||||
#endif
|
||||
|
||||
protected:
|
||||
|
||||
BinType bin_;
|
||||
|
@ -493,8 +493,7 @@ public:
|
||||
std::array<typename ItemList::iterator, 3>
|
||||
candidates = {it, it2, it3};
|
||||
|
||||
auto tryPack = [&placer, &candidates, ¬_packed,
|
||||
&pack](
|
||||
auto tryPack = [&placer, &candidates, &pack](
|
||||
const decltype(indices)& idx)
|
||||
{
|
||||
std::array<bool, 3> packed = {false};
|
||||
@ -569,11 +568,7 @@ public:
|
||||
{
|
||||
|
||||
packed_bins_[idx] = placer.getItems();
|
||||
#ifndef NDEBUG
|
||||
packed_bins_[idx].insert(packed_bins_[idx].end(),
|
||||
placer.getDebugItems().begin(),
|
||||
placer.getDebugItems().end());
|
||||
#endif
|
||||
|
||||
// TODO here should be a spinlock
|
||||
slock.lock();
|
||||
acounter -= packednum;
|
||||
|
@ -68,13 +68,13 @@ public:
|
||||
}
|
||||
|
||||
auto it = store_.begin();
|
||||
|
||||
while(it != store_.end()) {
|
||||
bool was_packed = false;
|
||||
size_t j = 0;
|
||||
while(!was_packed) {
|
||||
|
||||
for(size_t j = 0; j < placers.size() && !was_packed; j++) {
|
||||
if((was_packed =
|
||||
placers[j].pack(*it, rem(it, store_) )))
|
||||
for(; j < placers.size() && !was_packed; j++) {
|
||||
if((was_packed = placers[j].pack(*it, rem(it, store_) )))
|
||||
makeProgress(placers[j], j);
|
||||
}
|
||||
|
||||
@ -82,6 +82,7 @@ public:
|
||||
placers.emplace_back(bin);
|
||||
placers.back().configure(pconfig);
|
||||
packed_bins_.emplace_back();
|
||||
j = placers.size() - 1;
|
||||
}
|
||||
}
|
||||
++it;
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "printer_parts.h"
|
||||
#include <libnest2d/geometry_traits_nfp.hpp>
|
||||
//#include "../tools/libnfpglue.hpp"
|
||||
//#include "../tools/nfp_svgnest_glue.hpp"
|
||||
|
||||
std::vector<libnest2d::Item>& prusaParts() {
|
||||
static std::vector<libnest2d::Item> ret;
|
||||
@ -219,21 +220,21 @@ TEST(GeometryAlgorithms, IsPointInsidePolygon) {
|
||||
|
||||
Point p = {1, 1};
|
||||
|
||||
ASSERT_TRUE(rect.isPointInside(p));
|
||||
ASSERT_TRUE(rect.isInside(p));
|
||||
|
||||
p = {11, 11};
|
||||
|
||||
ASSERT_FALSE(rect.isPointInside(p));
|
||||
ASSERT_FALSE(rect.isInside(p));
|
||||
|
||||
|
||||
p = {11, 12};
|
||||
|
||||
ASSERT_FALSE(rect.isPointInside(p));
|
||||
ASSERT_FALSE(rect.isInside(p));
|
||||
|
||||
|
||||
p = {3, 3};
|
||||
|
||||
ASSERT_TRUE(rect.isPointInside(p));
|
||||
ASSERT_TRUE(rect.isInside(p));
|
||||
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ libnfporb::point_t scale(const libnfporb::point_t& p, long double factor) {
|
||||
|
||||
NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
|
||||
{
|
||||
using Vertex = PointImpl;
|
||||
namespace sl = shapelike;
|
||||
|
||||
NfpR ret;
|
||||
|
||||
@ -85,7 +85,7 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
|
||||
// this can throw
|
||||
auto nfp = libnfporb::generateNFP(pstat, porb, true);
|
||||
|
||||
auto &ct = ShapeLike::getContour(ret.first);
|
||||
auto &ct = sl::getContour(ret.first);
|
||||
ct.reserve(nfp.front().size()+1);
|
||||
for(auto v : nfp.front()) {
|
||||
v = scale(v, refactor);
|
||||
@ -94,7 +94,7 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
|
||||
ct.push_back(ct.front());
|
||||
std::reverse(ct.begin(), ct.end());
|
||||
|
||||
auto &rholes = ShapeLike::holes(ret.first);
|
||||
auto &rholes = sl::holes(ret.first);
|
||||
for(size_t hidx = 1; hidx < nfp.size(); ++hidx) {
|
||||
if(nfp[hidx].size() >= 3) {
|
||||
rholes.emplace_back();
|
||||
@ -110,31 +110,31 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
|
||||
}
|
||||
}
|
||||
|
||||
ret.second = Nfp::referenceVertex(ret.first);
|
||||
ret.second = nfp::referenceVertex(ret.first);
|
||||
|
||||
} catch(std::exception& e) {
|
||||
std::cout << "Error: " << e.what() << "\nTrying with convex hull..." << std::endl;
|
||||
// auto ch_stat = ShapeLike::convexHull(sh);
|
||||
// auto ch_orb = ShapeLike::convexHull(cother);
|
||||
ret = Nfp::nfpConvexOnly(sh, cother);
|
||||
ret = nfp::nfpConvexOnly(sh, cother);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY>::operator()(
|
||||
NfpR nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::CONVEX_ONLY>::operator()(
|
||||
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
{
|
||||
return _nfp(sh, cother);//nfpConvexOnly(sh, cother);
|
||||
}
|
||||
|
||||
NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX>::operator()(
|
||||
NfpR nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::ONE_CONVEX>::operator()(
|
||||
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
{
|
||||
return _nfp(sh, cother);
|
||||
}
|
||||
|
||||
NfpR Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE>::operator()(
|
||||
NfpR nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::BOTH_CONCAVE>::operator()(
|
||||
const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
|
||||
{
|
||||
return _nfp(sh, cother);
|
||||
|
@ -5,22 +5,22 @@
|
||||
|
||||
namespace libnest2d {
|
||||
|
||||
using NfpR = Nfp::NfpResult<PolygonImpl>;
|
||||
using NfpR = nfp::NfpResult<PolygonImpl>;
|
||||
|
||||
NfpR _nfp(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
|
||||
template<>
|
||||
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY> {
|
||||
struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::CONVEX_ONLY> {
|
||||
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX> {
|
||||
struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::ONE_CONVEX> {
|
||||
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE> {
|
||||
struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::BOTH_CONCAVE> {
|
||||
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
};
|
||||
|
||||
@ -34,7 +34,7 @@ struct Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE> {
|
||||
// NfpResult operator()(const PolygonImpl& sh, const PolygonImpl& cother);
|
||||
//};
|
||||
|
||||
template<> struct Nfp::MaxNfpLevel<PolygonImpl> {
|
||||
template<> struct nfp::MaxNfpLevel<PolygonImpl> {
|
||||
static const BP2D_CONSTEXPR NfpLevel value =
|
||||
// NfpLevel::CONVEX_ONLY;
|
||||
NfpLevel::BOTH_CONCAVE;
|
||||
|
1004
xs/src/libnest2d/tools/nfp_svgnest.hpp
Normal file
1004
xs/src/libnest2d/tools/nfp_svgnest.hpp
Normal file
File diff suppressed because it is too large
Load Diff
77
xs/src/libnest2d/tools/nfp_svgnest_glue.hpp
Normal file
77
xs/src/libnest2d/tools/nfp_svgnest_glue.hpp
Normal file
@ -0,0 +1,77 @@
|
||||
#ifndef NFP_SVGNEST_GLUE_HPP
|
||||
#define NFP_SVGNEST_GLUE_HPP
|
||||
|
||||
#include "nfp_svgnest.hpp"
|
||||
|
||||
#include <libnest2d/clipper_backend/clipper_backend.hpp>
|
||||
|
||||
namespace libnest2d {
|
||||
|
||||
namespace __svgnest {
|
||||
|
||||
//template<> struct _Tol<double> {
|
||||
// static const BP2D_CONSTEXPR TCoord<PointImpl> Value = 1000000;
|
||||
//};
|
||||
|
||||
}
|
||||
|
||||
namespace nfp {
|
||||
|
||||
using NfpR = NfpResult<PolygonImpl>;
|
||||
|
||||
template<> struct NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY> {
|
||||
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) {
|
||||
// return nfpConvexOnly(sh, cother);
|
||||
namespace sl = shapelike;
|
||||
using alg = __svgnest::_alg<PolygonImpl>;
|
||||
|
||||
std::cout << "Itt vagyok" << std::endl;
|
||||
auto nfp_p = alg::noFitPolygon(sl::getContour(sh),
|
||||
sl::getContour(cother), false, false);
|
||||
|
||||
PolygonImpl nfp_cntr;
|
||||
nfp_cntr.Contour = nfp_p.front();
|
||||
std::cout << "Contour size: " << nfp_cntr.Contour.size() << std::endl;
|
||||
return {nfp_cntr, referenceVertex(nfp_cntr)};
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX> {
|
||||
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) {
|
||||
// return nfpConvexOnly(sh, cother);
|
||||
namespace sl = shapelike;
|
||||
using alg = __svgnest::_alg<PolygonImpl>;
|
||||
|
||||
std::cout << "Itt vagyok" << std::endl;
|
||||
auto nfp_p = alg::noFitPolygon(sl::getContour(sh),
|
||||
sl::getContour(cother), false, false);
|
||||
|
||||
PolygonImpl nfp_cntr;
|
||||
nfp_cntr.Contour = nfp_p.front();
|
||||
return {nfp_cntr, referenceVertex(nfp_cntr)};
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE> {
|
||||
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother) {
|
||||
namespace sl = shapelike;
|
||||
using alg = __svgnest::_alg<PolygonImpl>;
|
||||
|
||||
auto nfp_p = alg::noFitPolygon(sl::getContour(sh),
|
||||
sl::getContour(cother), true, false);
|
||||
|
||||
PolygonImpl nfp_cntr;
|
||||
nfp_cntr.Contour = nfp_p.front();
|
||||
return {nfp_cntr, referenceVertex(nfp_cntr)};
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct MaxNfpLevel<PolygonImpl> {
|
||||
// static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::BOTH_CONCAVE;
|
||||
static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif // NFP_SVGNEST_GLUE_HPP
|
@ -270,6 +270,8 @@ void fillConfig(PConf& pcfg) {
|
||||
// The accuracy of optimization.
|
||||
// Goes from 0.0 to 1.0 and scales performance as well
|
||||
pcfg.accuracy = 0.65f;
|
||||
|
||||
pcfg.parallel = false;
|
||||
}
|
||||
|
||||
template<class TBin>
|
||||
@ -291,6 +293,7 @@ protected:
|
||||
std::vector<double> areacache_;
|
||||
SpatIndex rtree_;
|
||||
double norm_;
|
||||
Pile pile_cache_;
|
||||
public:
|
||||
|
||||
_ArrBase(const TBin& bin, Distance dist,
|
||||
@ -317,23 +320,26 @@ public:
|
||||
std::function<void(unsigned)> progressind):
|
||||
_ArrBase<Box>(bin, dist, progressind)
|
||||
{
|
||||
pconf_.object_function = [this, bin] (
|
||||
Pile& pile,
|
||||
const Item &item,
|
||||
const ItemGroup& rem) {
|
||||
// pconf_.object_function = [this, bin] (
|
||||
// const Pile& pile_c,
|
||||
// const Item &item,
|
||||
// const ItemGroup& rem) {
|
||||
|
||||
auto result = objfunc(bin.center(), bin_area_, pile,
|
||||
item, norm_, areacache_, rtree_, rem);
|
||||
double score = std::get<0>(result);
|
||||
auto& fullbb = std::get<1>(result);
|
||||
// auto& pile = pile_cache_;
|
||||
// if(pile.size() != pile_c.size()) pile = pile_c;
|
||||
|
||||
auto wdiff = fullbb.width() - bin.width();
|
||||
auto hdiff = fullbb.height() - bin.height();
|
||||
if(wdiff > 0) score += std::pow(wdiff, 2) / norm_;
|
||||
if(hdiff > 0) score += std::pow(hdiff, 2) / norm_;
|
||||
// auto result = objfunc(bin.center(), bin_area_, pile,
|
||||
// item, norm_, areacache_, rtree_, rem);
|
||||
// double score = std::get<0>(result);
|
||||
// auto& fullbb = std::get<1>(result);
|
||||
|
||||
return score;
|
||||
};
|
||||
// auto wdiff = fullbb.width() - bin.width();
|
||||
// auto hdiff = fullbb.height() - bin.height();
|
||||
// if(wdiff > 0) score += std::pow(wdiff, 2) / norm_;
|
||||
// if(hdiff > 0) score += std::pow(hdiff, 2) / norm_;
|
||||
|
||||
// return score;
|
||||
// };
|
||||
|
||||
pck_.configure(pconf_);
|
||||
}
|
||||
@ -350,10 +356,13 @@ public:
|
||||
_ArrBase<lnCircle>(bin, dist, progressind) {
|
||||
|
||||
pconf_.object_function = [this, &bin] (
|
||||
Pile& pile,
|
||||
const Pile& pile_c,
|
||||
const Item &item,
|
||||
const ItemGroup& rem) {
|
||||
|
||||
auto& pile = pile_cache_;
|
||||
if(pile.size() != pile_c.size()) pile = pile_c;
|
||||
|
||||
auto result = objfunc(bin.center(), bin_area_, pile, item, norm_,
|
||||
areacache_, rtree_, rem);
|
||||
double score = std::get<0>(result);
|
||||
@ -393,10 +402,13 @@ public:
|
||||
_ArrBase<PolygonImpl>(bin, dist, progressind)
|
||||
{
|
||||
pconf_.object_function = [this, &bin] (
|
||||
Pile& pile,
|
||||
const Pile& pile_c,
|
||||
const Item &item,
|
||||
const ItemGroup& rem) {
|
||||
|
||||
auto& pile = pile_cache_;
|
||||
if(pile.size() != pile_c.size()) pile = pile_c;
|
||||
|
||||
auto binbb = sl::boundingBox(bin);
|
||||
auto result = objfunc(binbb.center(), bin_area_, pile, item, norm_,
|
||||
areacache_, rtree_, rem);
|
||||
@ -417,10 +429,13 @@ public:
|
||||
_ArrBase<Box>(Box(0, 0), dist, progressind)
|
||||
{
|
||||
this->pconf_.object_function = [this] (
|
||||
Pile& pile,
|
||||
const Pile& pile_c,
|
||||
const Item &item,
|
||||
const ItemGroup& rem) {
|
||||
|
||||
auto& pile = pile_cache_;
|
||||
if(pile.size() != pile_c.size()) pile = pile_c;
|
||||
|
||||
auto result = objfunc({0, 0}, 0, pile, item, norm_,
|
||||
areacache_, rtree_, rem);
|
||||
return std::get<0>(result);
|
||||
|
Loading…
Reference in New Issue
Block a user