parallel nesting can be enabled but fails with the current objectfunction.

This commit is contained in:
tamasmeszaros 2018-08-20 16:34:35 +02:00
parent e678368b23
commit 8617b0a409
21 changed files with 1833 additions and 490 deletions

View file

@ -93,6 +93,8 @@ if(LIBNEST2D_BUILD_EXAMPLES)
add_executable(example examples/main.cpp add_executable(example examples/main.cpp
# tools/libnfpglue.hpp # tools/libnfpglue.hpp
# tools/libnfpglue.cpp # tools/libnfpglue.cpp
tools/nfp_svgnest.hpp
tools/nfp_svgnest_glue.hpp
tools/svgtools.hpp tools/svgtools.hpp
tests/printer_parts.cpp tests/printer_parts.cpp
tests/printer_parts.h tests/printer_parts.h

View file

@ -1,7 +1,6 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <fstream> #include <fstream>
//#define DEBUG_EXPORT_NFP //#define DEBUG_EXPORT_NFP
#include <libnest2d.h> #include <libnest2d.h>
@ -12,6 +11,8 @@
#include "libnest2d/rotfinder.hpp" #include "libnest2d/rotfinder.hpp"
//#include "tools/libnfpglue.hpp" //#include "tools/libnfpglue.hpp"
//#include "tools/nfp_svgnest_glue.hpp"
using namespace libnest2d; using namespace libnest2d;
using ItemGroup = std::vector<std::reference_wrapper<Item>>; using ItemGroup = std::vector<std::reference_wrapper<Item>>;
@ -53,10 +54,50 @@ void arrangeRectangles() {
const int SCALE = 1000000; const int SCALE = 1000000;
std::vector<Rectangle> rects = { std::vector<Item> rects(100, {
{60*SCALE, 200*SCALE}, {-9945219, -3065619},
{60*SCALE, 200*SCALE} {-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; std::vector<Item> input;
input.insert(input.end(), prusaParts().begin(), prusaParts().end()); input.insert(input.end(), prusaParts().begin(), prusaParts().end());
@ -84,7 +125,7 @@ void arrangeRectangles() {
// _Circle<PointImpl> bin({0, 0}, 125*SCALE); // _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 Placer = strategies::_NofitPolyPlacer<PolygonImpl, decltype(bin)>;
using Packer = Nester<Placer, FirstFitSelection>; using Packer = Nester<Placer, FirstFitSelection>;
@ -95,14 +136,15 @@ void arrangeRectangles() {
pconf.alignment = Placer::Config::Alignment::CENTER; pconf.alignment = Placer::Config::Alignment::CENTER;
pconf.starting_point = Placer::Config::Alignment::CENTER; pconf.starting_point = Placer::Config::Alignment::CENTER;
pconf.rotations = {0.0/*, Pi/2.0, Pi, 3*Pi/2*/}; 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; Packer::SelectionConfig sconf;
// sconf.allow_parallel = false; // sconf.allow_parallel = false;
// sconf.force_parallel = false; // sconf.force_parallel = false;
// sconf.try_triplets = true; // sconf.try_triplets = true;
// sconf.try_reverse_order = true; // sconf.try_reverse_order = true;
// sconf.waste_increment = 0.005; // sconf.waste_increment = 0.01;
arrange.configure(pconf, sconf); arrange.configure(pconf, sconf);
@ -175,7 +217,6 @@ void arrangeRectangles() {
SVGWriter svgw(conf); SVGWriter svgw(conf);
svgw.setSize(Box(250*SCALE, 210*SCALE)); svgw.setSize(Box(250*SCALE, 210*SCALE));
svgw.writePackGroup(result); svgw.writePackGroup(result);
// std::for_each(input.begin(), input.end(), [&svgw](Item& item){ svgw.writeItem(item);});
svgw.save("out"); svgw.save("out");
} }

View file

@ -356,8 +356,7 @@ inline double area(const PolygonImpl& shape, const PolygonTag&)
#endif #endif
template<> template<>
inline bool isInside<PolygonImpl>(const PointImpl& point, inline bool isInside(const PointImpl& point, const PolygonImpl& shape)
const PolygonImpl& shape)
{ {
return boost::geometry::within(point, shape); return boost::geometry::within(point, shape);
} }
@ -390,7 +389,8 @@ inline bp2d::Box boundingBox(const PolygonImpl& sh, const PolygonTag&)
} }
template<> 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; bp2d::Box b;
boost::geometry::envelope(shapes, b); boost::geometry::envelope(shapes, b);
@ -400,7 +400,7 @@ inline bp2d::Box boundingBox<PolygonImpl>(const bp2d::Shapes& shapes)
#ifndef DISABLE_BOOST_CONVEX_HULL #ifndef DISABLE_BOOST_CONVEX_HULL
template<> template<>
inline PolygonImpl convexHull(const PolygonImpl& sh) inline PolygonImpl convexHull(const PolygonImpl& sh, const PolygonTag&)
{ {
PolygonImpl ret; PolygonImpl ret;
boost::geometry::convex_hull(sh, ret); boost::geometry::convex_hull(sh, ret);
@ -408,7 +408,8 @@ inline PolygonImpl convexHull(const PolygonImpl& sh)
} }
template<> template<>
inline PolygonImpl convexHull(const bp2d::Shapes& shapes) inline PolygonImpl convexHull(const TMultiShape<PolygonImpl>& shapes,
const MultiPolygonTag&)
{ {
PolygonImpl ret; PolygonImpl ret;
boost::geometry::convex_hull(shapes, ret); boost::geometry::convex_hull(shapes, ret);
@ -416,34 +417,6 @@ inline PolygonImpl convexHull(const bp2d::Shapes& shapes)
} }
#endif #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 #ifndef DISABLE_BOOST_OFFSET
template<> template<>
inline void offset(PolygonImpl& sh, bp2d::Coord distance) 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 { namespace nfp {
#ifndef DISABLE_BOOST_NFP_MERGE #ifndef DISABLE_BOOST_NFP_MERGE
// Warning: I could not get boost union_ to work. Geometries will overlap.
template<> template<>
inline bp2d::Shapes Nfp::merge(const bp2d::Shapes& shapes, inline bp2d::Shapes nfp::merge(const bp2d::Shapes& shapes,
const PolygonImpl& sh) const PolygonImpl& sh)
{ {
bp2d::Shapes retv; bp2d::Shapes retv;
boost::geometry::union_(shapes, sh, retv); boost::geometry::union_(shapes, sh, retv);
return 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 #endif
} }

View file

@ -21,9 +21,6 @@ struct PolygonImpl {
PathImpl Contour; PathImpl Contour;
HoleStore Holes; HoleStore Holes;
using Tag = libnest2d::PolygonTag;
using PointType = PointImpl;
inline PolygonImpl() = default; inline PolygonImpl() = default;
inline explicit PolygonImpl(const PathImpl& cont): Contour(cont) {} inline explicit PolygonImpl(const PathImpl& cont): Contour(cont) {}
@ -102,41 +99,49 @@ template<> struct PointType<PolygonImpl> {
using Type = PointImpl; using Type = PointImpl;
}; };
// Type of vertex iterator used by Clipper template<> struct PointType<PointImpl> {
template<> struct VertexIteratorType<PolygonImpl> { using Type = PointImpl;
using Type = ClipperLib::Path::iterator;
};
// Type of vertex iterator used by Clipper
template<> struct VertexConstIteratorType<PolygonImpl> {
using Type = ClipperLib::Path::const_iterator;
}; };
template<> struct CountourType<PolygonImpl> { template<> struct CountourType<PolygonImpl> {
using Type = PathImpl; 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 { 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) template<> inline TCoord<PointImpl> x(const PointImpl& p)
{ {
return p.X; 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) template<> inline TCoord<PointImpl> y(const PointImpl& p)
{ {
return p.Y; 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) template<> inline TCoord<PointImpl>& x(PointImpl& p)
{ {
return p.X; 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) template<> inline TCoord<PointImpl>& y(PointImpl& p)
{ {
return p.Y; 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 { namespace shapelike {
template<> inline void reserve(PolygonImpl& sh, size_t vertex_capacity) 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); 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&) template<> inline double area(const PolygonImpl& sh, const PolygonTag&)
{ {
return _smartarea::area<OrientationType<PolygonImpl>::Value>(sh); return _smartarea::area<OrientationType<PolygonImpl>::Value>(sh);
@ -269,28 +270,6 @@ template<> inline std::string toString(const PolygonImpl& sh)
return ss.str(); 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<> template<>
inline PolygonImpl create(const PathImpl& path, const HoleStore& holes) inline PolygonImpl create(const PathImpl& path, const HoleStore& holes)
{ {
@ -410,8 +389,8 @@ inline void rotate(PolygonImpl& sh, const Radians& rads)
} // namespace shapelike } // namespace shapelike
#define DISABLE_BOOST_NFP_MERGE #define DISABLE_BOOST_NFP_MERGE
inline nfp::Shapes<PolygonImpl> _merge(ClipperLib::Clipper& clipper) { inline std::vector<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
nfp::Shapes<PolygonImpl> retv; shapelike::Shapes<PolygonImpl> retv;
ClipperLib::PolyTree result; ClipperLib::PolyTree result;
clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNegative); clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNegative);
@ -447,8 +426,8 @@ inline nfp::Shapes<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
namespace nfp { namespace nfp {
template<> inline nfp::Shapes<PolygonImpl> template<> inline std::vector<PolygonImpl>
merge(const nfp::Shapes<PolygonImpl>& shapes) merge(const std::vector<PolygonImpl>& shapes)
{ {
ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution); ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution);

View file

@ -22,34 +22,12 @@ template<class GeomType>
using TCoord = typename CoordType<remove_cvref_t<GeomType>>::Type; using TCoord = typename CoordType<remove_cvref_t<GeomType>>::Type;
/// Getting the type of point structure used by a shape. /// 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`. /// TPoint<ShapeClass> as shorthand for `typename PointType<ShapeClass>::Type`.
template<class Shape> template<class Shape>
using TPoint = typename PointType<remove_cvref_t<Shape>>::Type; 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, ...). * \brief A point pair base class for other point pairs (segment, box, ...).
* \tparam RawPoint The actual point type to use. * \tparam RawPoint The actual point type to use.
@ -61,9 +39,16 @@ struct PointPair {
}; };
struct PolygonTag {}; struct PolygonTag {};
struct MultiPolygonTag {};
struct BoxTag {}; struct BoxTag {};
struct CircleTag {}; 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; * \brief An abstraction of a box;
*/ */
@ -371,7 +356,7 @@ enum class Formats {
namespace shapelike { namespace shapelike {
template<class RawShape> template<class RawShape>
using Shapes = std::vector<RawShape>; using Shapes = TMultiShape<RawShape>;
template<class RawShape> template<class RawShape>
inline RawShape create(const TContour<RawShape>& contour, inline RawShape create(const TContour<RawShape>& contour,
@ -455,27 +440,28 @@ namespace shapelike {
} }
template<class RawShape> 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> 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> 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> 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> template<class RawShape>
@ -559,27 +545,29 @@ namespace shapelike {
"ShapeLike::boundingBox(shape) unimplemented!"); "ShapeLike::boundingBox(shape) unimplemented!");
} }
template<class RawShape> template<class RawShapes>
inline _Box<TPoint<RawShape>> boundingBox(const Shapes<RawShape>& /*sh*/) 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!"); "ShapeLike::boundingBox(shapes) unimplemented!");
} }
template<class RawShape> template<class RawShape>
inline RawShape convexHull(const RawShape& /*sh*/) inline RawShape convexHull(const RawShape& /*sh*/, const PolygonTag&)
{ {
static_assert(always_false<RawShape>::value, static_assert(always_false<RawShape>::value,
"ShapeLike::convexHull(shape) unimplemented!"); "ShapeLike::convexHull(shape) unimplemented!");
return RawShape(); return RawShape();
} }
template<class RawShape> template<class RawShapes>
inline RawShape convexHull(const Shapes<RawShape>& /*sh*/) 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!"); "ShapeLike::convexHull(shapes) unimplemented!");
return RawShape(); return typename RawShapes::value_type();
} }
template<class RawShape> template<class RawShape>
@ -599,8 +587,7 @@ namespace shapelike {
template<class RawShape> template<class RawShape>
inline void offset(RawShape& /*sh*/, TCoord<TPoint<RawShape>> /*distance*/) inline void offset(RawShape& /*sh*/, TCoord<TPoint<RawShape>> /*distance*/)
{ {
static_assert(always_false<RawShape>::value, dout() << "The current geometry backend does not support offsetting!\n";
"ShapeLike::offset() unimplemented!");
} }
template<class RawShape> template<class RawShape>
@ -670,9 +657,9 @@ namespace shapelike {
} }
template<class S> // Dispatch function 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> template<class Box>
@ -690,7 +677,7 @@ namespace shapelike {
template<class RawShape> // Dispatching function template<class RawShape> // Dispatching function
inline double area(const RawShape& sh) inline double area(const RawShape& sh)
{ {
return area(sh, typename RawShape::Tag()); return area(sh, Tag<RawShape>());
} }
template<class 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> template<class RawShape>
inline bool isInside(const TPoint<RawShape>& point, inline bool isInside(const TPoint<RawShape>& point,
const _Circle<TPoint<RawShape>>& circ) 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 #endif // GEOMETRY_TRAITS_HPP

View file

@ -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. /// A collection of static methods for handling the no fit polygon creation.
namespace nfp { namespace nfp {
namespace sl = shapelike; //namespace sl = shapelike;
namespace pl = pointlike; //namespace pl = pointlike;
/// The complexity level of a polygon that an NFP implementation can handle. /// The complexity level of a polygon that an NFP implementation can handle.
enum class NfpLevel: unsigned { enum class NfpLevel: unsigned {
@ -49,7 +49,7 @@ template<class RawShape> struct MaxNfpLevel {
// Shorthand for a pile of polygons // Shorthand for a pile of polygons
template<class RawShape> template<class RawShape>
using Shapes = typename shapelike::Shapes<RawShape>; using Shapes = TMultiShape<RawShape>;
/** /**
* Merge a bunch of polygons with the specified additional polygon. * 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 * 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. * polygons are disjuct than the resulting set will contain more polygons.
*/ */
template<class RawShape> template<class RawShapes>
inline Shapes<RawShape> merge(const Shapes<RawShape>& /*shc*/) inline RawShapes merge(const RawShapes& /*shc*/)
{ {
static_assert(always_false<RawShape>::value, static_assert(always_false<RawShapes>::value,
"Nfp::merge(shapes, shape) unimplemented!"); "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. * polygons are disjuct than the resulting set will contain more polygons.
*/ */
template<class RawShape> template<class RawShape>
inline Shapes<RawShape> merge(const Shapes<RawShape>& shc, inline TMultiShape<RawShape> merge(const TMultiShape<RawShape>& shc,
const RawShape& sh) const RawShape& sh)
{ {
auto m = merge(shc); auto m = nfp::merge(shc);
m.push_back(sh); m.push_back(sh);
return merge(m); return nfp::merge(m);
} }
/** /**

View file

@ -31,6 +31,8 @@ class _Item {
using Vertex = TPoint<RawShape>; using Vertex = TPoint<RawShape>;
using Box = _Box<Vertex>; using Box = _Box<Vertex>;
using VertexConstIterator = typename TContour<RawShape>::const_iterator;
// The original shape that gets encapsulated. // The original shape that gets encapsulated.
RawShape sh_; RawShape sh_;
@ -39,7 +41,7 @@ class _Item {
Radians rotation_; Radians rotation_;
Coord offset_distance_; 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 // 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. // that a zero angle is not a rotation because of testing for equality.
bool has_rotation_ = false, has_translation_ = false, has_offset_ = false; bool has_rotation_ = false, has_translation_ = false, has_offset_ = false;
@ -59,8 +61,8 @@ class _Item {
}; };
mutable Convexity convexity_ = Convexity::UNCHECKED; mutable Convexity convexity_ = Convexity::UNCHECKED;
mutable TVertexConstIterator<RawShape> rmt_; // rightmost top vertex mutable VertexConstIterator rmt_; // rightmost top vertex
mutable TVertexConstIterator<RawShape> lmb_; // leftmost bottom vertex mutable VertexConstIterator lmb_; // leftmost bottom vertex
mutable bool rmt_valid_ = false, lmb_valid_ = false; mutable bool rmt_valid_ = false, lmb_valid_ = false;
mutable struct BBCache { mutable struct BBCache {
Box bb; bool valid; Vertex tr; Box bb; bool valid; Vertex tr;
@ -81,7 +83,7 @@ public:
* supports. Giving out a non const iterator would make it impossible to * supports. Giving out a non const iterator would make it impossible to
* perform correct cache invalidation. * perform correct cache invalidation.
*/ */
using Iterator = TVertexConstIterator<RawShape>; using Iterator = VertexConstIterator;
/** /**
* @brief Get the orientation of the polygon. * @brief Get the orientation of the polygon.
@ -110,7 +112,7 @@ public:
explicit inline _Item(RawShape&& sh): sh_(std::move(sh)) {} 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. * @param il The initializer list of vertices.
*/ */
inline _Item(const std::initializer_list< Vertex >& il): 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 * Note that the vertex considered here is taken from the original shape
* that this item is constructed from. This means that no transformation is * that this item is constructed from. This means that no transformation is
@ -245,7 +247,7 @@ public:
* @param p * @param p
* @return * @return
*/ */
inline bool isPointInside(const Vertex& p) const inline bool isInside(const Vertex& p) const
{ {
return sl::isInside(p, transformedShape()); 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. * \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 * specialize this class template and define all the ten methods it has. It can
* use the strategies::PlacerBoilerplace class for creating a new placement * use the strategies::PlacerBoilerplace class for creating a new placement
* strategy where only the constructor and the trypack method has to be provided * 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 * Note that it depends on the particular placer implementation how it
* reacts to config changes in the middle of a calculation. * 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); } 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 * \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 * 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, * \param from The iterator to the item from which the packing should start,
* including the pointed item * including the pointed item
* \param count How many items should be packed. If the value is 1, than * \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 * A default implementation would be to call
* { auto&& r = trypack(...); accept(r); return r; } but we should let the * { auto&& r = trypack(...); accept(r); return r; } but we should let the
* implementor of the placement strategy to harvest any optimizations from * 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. * in the implementation.
* *
* @param item The item to pack. * @param item The item to pack.
@ -628,13 +630,6 @@ public:
inline double filledArea() const { return impl_.filledArea(); } 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 // 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 * Note that it depends on the particular placer implementation how it
* reacts to config changes in the middle of a calculation. * 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) { inline void configure(const Config& config) {
impl_.configure(config); impl_.configure(config);
} }
/** /**
* @brief A function callback which should be called whenewer an item or * @brief A function callback which should be called whenever an item or
* a group of items where succesfully packed. * a group of items where successfully packed.
* @param fn A function callback object taking one unsigned integer as the * @param fn A function callback object taking one unsigned integer as the
* number of the remaining items to pack. * number of the remaining items to pack.
*/ */
@ -680,7 +675,7 @@ public:
* placer compatible with the PlacementStrategyLike interface. * placer compatible with the PlacementStrategyLike interface.
* *
* \param first, last The first and last iterator if the input sequence. It * \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 * \param bin. The shape of the bin. It has to be supported by the placement
* strategy. * strategy.
* \param An optional config object for the placer. * \param An optional config object for the placer.
@ -712,7 +707,7 @@ public:
/** /**
* @brief Get the items for a particular bin. * @brief Get the items for a particular bin.
* @param binIndex The index of the requested 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) { inline ItemGroup itemsForBin(size_t binIndex) {
return impl_.itemsForBin(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 * input items and outputs the items with the proper transformations to be
* inside the provided bin. * inside the provided bin.
*/ */
@ -857,7 +852,7 @@ public:
return _execute(from, to); 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) inline Nester& progressIndicator(ProgressFunction func)
{ {
selector_.progressIndicator(func); return *this; selector_.progressIndicator(func); return *this;
@ -877,8 +872,8 @@ private:
template<class TIterator, template<class TIterator,
class IT = remove_cvref_t<typename TIterator::value_type>, class IT = remove_cvref_t<typename TIterator::value_type>,
// This funtion will be used only if the iterators are pointing to // This function will be used only if the iterators are pointing to
// a type compatible with the binpack2d::_Item template. // a type compatible with the libnets2d::_Item template.
// This way we can use references to input elements as they will // This way we can use references to input elements as they will
// have to exist for the lifetime of this call. // have to exist for the lifetime of this call.
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT> class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT>
@ -904,8 +899,8 @@ private:
template<class TIterator, template<class TIterator,
class IT = remove_cvref_t<typename TIterator::value_type>, class IT = remove_cvref_t<typename TIterator::value_type>,
// This funtion will be used only if the iterators are pointing to // This function will be used only if the iterators are pointing to
// a type compatible with the binpack2d::_Item template. // a type compatible with the libnest2d::_Item template.
// This way we can use references to input elements as they will // This way we can use references to input elements as they will
// have to exist for the lifetime of this call. // have to exist for the lifetime of this call.
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT> class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT>

View file

@ -67,11 +67,11 @@ class metaloop {
// need to wrap that in a type (see metaloop::Int). // 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 * 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 * 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 * _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>; 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 // 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 // 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 // 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)) {} inline MapFn(Fn&& fn): fn_(forward<Fn>(fn)) {}
template<class T> void operator ()(T&& pack_element) { template<class T> void operator ()(T&& pack_element) {
@ -146,7 +146,7 @@ public:
* version of run is called which does not call itself anymore. * version of run is called which does not call itself anymore.
* *
* If you are utterly annoyed, at least you have learned a super crazy * If you are utterly annoyed, at least you have learned a super crazy
* functional metaprogramming pattern. * functional meta-programming pattern.
*/ */
template<class...Args> template<class...Args>
using MetaLoop = _MetaLoop<Int<sizeof...(Args)-1>, Args...>; using MetaLoop = _MetaLoop<Int<sizeof...(Args)-1>, Args...>;

View file

@ -102,6 +102,9 @@ struct StopCriteria {
/// If the relative value difference between two scores. /// If the relative value difference between two scores.
double relative_score_difference = std::nan(""); double relative_score_difference = std::nan("");
/// Stop if this value or better is found.
double stop_score = std::nan("");
unsigned max_iterations = 0; unsigned max_iterations = 0;
}; };

View file

@ -142,10 +142,12 @@ protected:
default: ; default: ;
} }
auto abs_diff = stopcr_.absolute_score_difference; double abs_diff = stopcr_.absolute_score_difference;
auto rel_diff = stopcr_.relative_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(abs_diff)) opt_.set_ftol_abs(abs_diff);
if(!std::isnan(rel_diff)) opt_.set_ftol_rel(rel_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) if(this->stopcr_.max_iterations > 0)
opt_.set_maxeval(this->stopcr_.max_iterations ); opt_.set_maxeval(this->stopcr_.max_iterations );

View file

@ -7,9 +7,24 @@
namespace libnest2d { namespace strategies { 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> template<class RawShape>
struct BLConfig { 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; bool allow_rotations = false;
}; };
@ -69,20 +84,21 @@ protected:
setInitialPosition(item); setInitialPosition(item);
Unit d = availableSpaceDown(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 can_be_packed = can_move;
bool left = true; bool left = true;
while(can_move) { while(can_move) {
if(left) { // write previous down move and go down if(left) { // write previous down move and go down
item.translate({0, -d+1}); item.translate({0, -d+eps});
d = availableSpaceLeft(item); d = availableSpaceLeft(item);
can_move = d > 1/*std::numeric_limits<Unit>::epsilon()*/; can_move = d > eps;
left = false; left = false;
} else { // write previous left move and go down } else { // write previous left move and go down
item.translate({-d+1, 0}); item.translate({-d+eps, 0});
d = availableSpaceDown(item); d = availableSpaceDown(item);
can_move = d > 1/*std::numeric_limits<Unit>::epsilon()*/; can_move = d > eps;
left = true; left = true;
} }
} }

View file

@ -2,7 +2,15 @@
#define NOFITPOLY_HPP #define NOFITPOLY_HPP
#include <cassert> #include <cassert>
#include <random>
// For caching nfps
#include <unordered_map>
// For parallel for
#include <functional>
#include <iterator>
#include <future>
#include <atomic>
#ifndef NDEBUG #ifndef NDEBUG
#include <iostream> #include <iostream>
@ -13,8 +21,84 @@
#include "tools/svgtools.hpp" #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> template<class RawShape>
struct NfpPConfig { struct NfpPConfig {
@ -71,7 +155,7 @@ struct NfpPConfig {
* decisions (for you or a more intelligent AI). * 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&)> const ItemGroup&)>
object_function; object_function;
@ -80,7 +164,7 @@ struct NfpPConfig {
* This is a compromise slider between quality and speed. Zero is the * 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. * 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 * @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; 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}), NfpPConfig(): rotations({0.0, Pi/2.0, Pi, 3*Pi/2}),
alignment(Alignment::CENTER), starting_point(Alignment::CENTER) {} alignment(Alignment::CENTER), starting_point(Alignment::CENTER) {}
}; };
@ -325,121 +414,8 @@ inline void correctNfpPosition(nfp::NfpResult<RawShape>& nfp,
shapelike::translate(nfp.first, dnfp); shapelike::translate(nfp.first, dnfp);
} }
template<class RawShape, class Container> template<class RawShape, class Circle = _Circle<TPoint<RawShape>> >
nfp::Shapes<RawShape> calcnfp( const Container& polygons, Circle minimizeCircle(const RawShape& sh) {
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) {
using Point = TPoint<RawShape>; using Point = TPoint<RawShape>;
using Coord = TCoord<Point>; using Coord = TCoord<Point>;
@ -507,9 +483,19 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin
using Box = _Box<TPoint<RawShape>>; 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_; const double norm_;
using MaxNfpLevel = nfp::MaxNfpLevel<RawShape>; // Caching calculated nfps
__itemhash::Hash<RawShape> nfpcache_;
// Storing item hash keys
ItemKeys item_keys_;
public: public:
using Pile = nfp::Shapes<RawShape>; using Pile = nfp::Shapes<RawShape>;
@ -526,60 +512,290 @@ public:
_NofitPolyPlacer& operator=(_NofitPolyPlacer&&) BP2D_NOEXCEPT = default; _NofitPolyPlacer& operator=(_NofitPolyPlacer&&) BP2D_NOEXCEPT = default;
#endif #endif
bool static inline wouldFit(const Box& bb, const RawShape& bin) { static inline double overfit(const Box& bb, const RawShape& bin) {
auto bbin = sl::boundingBox<RawShape>(bin); auto bbin = sl::boundingBox(bin);
auto d = bbin.center() - bb.center(); auto d = bbin.center() - bb.center();
_Rectangle<RawShape> rect(bb.width(), bb.height()); _Rectangle<RawShape> rect(bb.width(), bb.height());
rect.translate(bb.minCorner() + d); 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) { static inline double overfit(const RawShape& chull, const RawShape& bin) {
auto bbch = sl::boundingBox<RawShape>(chull); auto bbch = sl::boundingBox(chull);
auto bbin = sl::boundingBox<RawShape>(bin); auto bbin = sl::boundingBox(bin);
auto d = bbch.center() - bbin.center(); auto d = bbch.center() - bbin.center();
auto chullcpy = chull; auto chullcpy = chull;
sl::translate(chullcpy, d); 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); auto bbch = sl::boundingBox(chull);
return wouldFit(bbch, bin); 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)
{ {
double boxr = 0.5*pl::distance(bb.minCorner(), bb.maxCorner());
return sl::isInside<RawShape>(bb, bin); 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) 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>> 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, Item& item,
const Range& remaining = Range()) { const Range& remaining = Range()) {
PackResult ret; PackResult ret;
bool can_pack = false; bool can_pack = false;
double best_overfit = std::numeric_limits<double>::max();
auto remlist = ItemGroup(remaining.from, remaining.to); auto remlist = ItemGroup(remaining.from, remaining.to);
size_t itemhash = __itemhash::hash(item);
if(items_.empty()) { if(items_.empty()) {
setInitialPosition(item); setInitialPosition(item);
can_pack = item.isInside(bin_); best_overfit = overfit(item.transformedShape(), bin_);
can_pack = best_overfit <= 0;
} else { } else {
double global_score = std::numeric_limits<double>::max(); double global_score = std::numeric_limits<double>::max();
@ -588,7 +804,7 @@ public:
auto initial_rot = item.rotation(); auto initial_rot = item.rotation();
Vertex final_tr = {0, 0}; Vertex final_tr = {0, 0};
Radians final_rot = initial_rot; Radians final_rot = initial_rot;
nfp::Shapes<RawShape> nfps; Shapes nfps;
for(auto rot : config_.rotations) { for(auto rot : config_.rotations) {
@ -596,17 +812,16 @@ public:
item.rotation(initial_rot + rot); item.rotation(initial_rot + rot);
// place the new item outside of the print bed to make sure // 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); placeOutsideOfBin(item);
auto trsh = item.transformedShape(); nfps = calcnfp({item, itemhash}, Lvl<MaxNfpLevel::value>());
nfps = calcnfp(items_, item, Lvl<MaxNfpLevel::value>()); auto iv = item.referenceVertex();
auto iv = nfp::referenceVertex(trsh);
auto startpos = item.translation(); auto startpos = item.translation();
std::vector<EdgeCache<RawShape>> ecache; std::vector<Edges> ecache;
ecache.reserve(nfps.size()); ecache.reserve(nfps.size());
for(auto& nfp : nfps ) { for(auto& nfp : nfps ) {
@ -614,14 +829,54 @@ public:
ecache.back().accuracy(config_.accuracy); ecache.back().accuracy(config_.accuracy);
} }
struct Optimum { Shapes pile;
double relpos; pile.reserve(items_.size()+1);
unsigned nfpidx; // double pile_area = 0;
int hidx; for(Item& mitem : items_) {
Optimum(double pos, unsigned nidx): pile.emplace_back(mitem.transformedShape());
relpos(pos), nfpidx(nidx), hidx(-1) {} // pile_area += mitem.area();
Optimum(double pos, unsigned nidx, int holeidx): }
relpos(pos), nfpidx(nidx), hidx(holeidx) {}
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) auto getNfpPoint = [&ecache](const Optimum& opt)
@ -630,58 +885,10 @@ public:
ecache[opt.nfpidx].coords(opt.hidx, opt.relpos); ecache[opt.nfpidx].coords(opt.hidx, opt.relpos);
}; };
nfp::Shapes<RawShape> pile; auto boundaryCheck =
pile.reserve(items_.size()+1); [&merged_pile, &getNfpPoint, &item, &bin, &iv, &startpos]
double pile_area = 0; (const Optimum& o)
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*/)
{ {
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 v = getNfpPoint(o);
auto d = v - iv; auto d = v - iv;
d += startpos; d += startpos;
@ -691,84 +898,111 @@ public:
auto chull = sl::convexHull(merged_pile); auto chull = sl::convexHull(merged_pile);
merged_pile.pop_back(); 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); Optimum optimum(0, 0);
double best_score = std::numeric_limits<double>::max(); 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 // Local optimization with the four polygon corners as
// starting points // starting points
for(unsigned ch = 0; ch < ecache.size(); ch++) { for(unsigned ch = 0; ch < ecache.size(); ch++) {
auto& cache = ecache[ch]; auto& cache = ecache[ch];
auto contour_ofn = [&rawobjfunc, &getNfpPoint, ch] auto contour_ofn = [rawobjfunc, getNfpPoint, ch]
(double relpos) (double relpos)
{ {
return rawobjfunc(getNfpPoint(Optimum(relpos, ch))); return rawobjfunc(getNfpPoint(Optimum(relpos, ch)));
}; };
std::for_each(cache.corners().begin(), OptResults results(cache.corners().size());
cache.corners().end(),
[ch, &contour_ofn, &solver, &best_score, __parallel::enumerate(
&optimum, &boundaryCheck] (double pos) cache.corners().begin(),
cache.corners().end(),
[&contour_ofn, &results]
(double pos, unsigned n)
{ {
Optimizer solver;
try { try {
auto result = solver.optimize_min(contour_ofn, results[n] = solver.optimize_min(contour_ofn,
opt::initvals<double>(pos), opt::initvals<double>(pos),
opt::bound<double>(0, 1.0) 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) { } catch(std::exception& e) {
derr() << "ERROR: " << e.what() << "\n"; 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) { for(unsigned hidx = 0; hidx < cache.holeCount(); ++hidx) {
auto hole_ofn = auto hole_ofn =
[&rawobjfunc, &getNfpPoint, ch, hidx] [rawobjfunc, getNfpPoint, ch, hidx]
(double pos) (double pos)
{ {
Optimum opt(pos, ch, hidx); Optimum opt(pos, ch, hidx);
return rawobjfunc(getNfpPoint(opt)); 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(), cache.corners(hidx).end(),
[&hole_ofn, &solver, &best_score, [&hole_ofn, &results]
&optimum, ch, hidx, &boundaryCheck] (double pos, unsigned n)
(double pos)
{ {
Optimizer solver;
try { try {
auto result = solver.optimize_min(hole_ofn, results[n] = solver.optimize_min(hole_ofn,
opt::initvals<double>(pos), opt::initvals<double>(pos),
opt::bound<double>(0, 1.0) 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) { } catch(std::exception& e) {
derr() << "ERROR: " << e.what() << "\n"; 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) { if(can_pack) {
ret = PackResult(item); ret = PackResult(item);
item_keys_.emplace_back(itemhash);
} else {
ret = PackResult(best_overfit);
} }
return ret; return ret;
} }
~_NofitPolyPlacer() {
clearItems();
}
inline void clearItems() {
finalAlign(bin_);
Base::clearItems();
}
private:
inline void finalAlign(const RawShape& pbin) { inline void finalAlign(const RawShape& pbin) {
auto bbin = sl::boundingBox(pbin); auto bbin = sl::boundingBox(pbin);
finalAlign(bbin); finalAlign(bbin);
@ -826,7 +1052,7 @@ private:
nfp::Shapes<RawShape> m; nfp::Shapes<RawShape> m;
m.reserve(items_.size()); m.reserve(items_.size());
for(Item& item : items_) m.emplace_back(item.transformedShape()); for(Item& item : items_) m.emplace_back(item.transformedShape());
auto&& bb = sl::boundingBox<RawShape>(m); auto&& bb = sl::boundingBox(m);
Vertex ci, cb; Vertex ci, cb;

View file

@ -26,15 +26,21 @@ public:
Item *item_ptr_; Item *item_ptr_;
Vertex move_; Vertex move_;
Radians rot_; Radians rot_;
double overfit_;
friend class PlacerBoilerplate; friend class PlacerBoilerplate;
friend Subclass; friend Subclass;
PackResult(Item& item): PackResult(Item& item):
item_ptr_(&item), item_ptr_(&item),
move_(item.translation()), move_(item.translation()),
rot_(item.rotation()) {} rot_(item.rotation()) {}
PackResult(): item_ptr_(nullptr) {}
PackResult(double overfit = 1.0):
item_ptr_(nullptr), overfit_(overfit) {}
public: public:
operator bool() { return item_ptr_ != nullptr; } operator bool() { return item_ptr_ != nullptr; }
double overfit() const { return overfit_; }
}; };
inline PlacerBoilerplate(const BinType& bin, unsigned cap = 50): bin_(bin) inline PlacerBoilerplate(const BinType& bin, unsigned cap = 50): bin_(bin)
@ -82,9 +88,6 @@ public:
inline void clearItems() { inline void clearItems() {
items_.clear(); items_.clear();
farea_valid_ = false; farea_valid_ = false;
#ifndef NDEBUG
debug_items_.clear();
#endif
} }
inline double filledArea() const { inline double filledArea() const {
@ -101,10 +104,6 @@ public:
return farea_; return farea_;
} }
#ifndef NDEBUG
std::vector<Item> debug_items_;
#endif
protected: protected:
BinType bin_; BinType bin_;

View file

@ -493,8 +493,7 @@ public:
std::array<typename ItemList::iterator, 3> std::array<typename ItemList::iterator, 3>
candidates = {it, it2, it3}; candidates = {it, it2, it3};
auto tryPack = [&placer, &candidates, &not_packed, auto tryPack = [&placer, &candidates, &pack](
&pack](
const decltype(indices)& idx) const decltype(indices)& idx)
{ {
std::array<bool, 3> packed = {false}; std::array<bool, 3> packed = {false};
@ -569,11 +568,7 @@ public:
{ {
packed_bins_[idx] = placer.getItems(); 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 // TODO here should be a spinlock
slock.lock(); slock.lock();
acounter -= packednum; acounter -= packednum;

View file

@ -68,13 +68,13 @@ public:
} }
auto it = store_.begin(); auto it = store_.begin();
while(it != store_.end()) { while(it != store_.end()) {
bool was_packed = false; bool was_packed = false;
size_t j = 0;
while(!was_packed) { while(!was_packed) {
for(; j < placers.size() && !was_packed; j++) {
for(size_t j = 0; j < placers.size() && !was_packed; j++) { if((was_packed = placers[j].pack(*it, rem(it, store_) )))
if((was_packed =
placers[j].pack(*it, rem(it, store_) )))
makeProgress(placers[j], j); makeProgress(placers[j], j);
} }
@ -82,6 +82,7 @@ public:
placers.emplace_back(bin); placers.emplace_back(bin);
placers.back().configure(pconfig); placers.back().configure(pconfig);
packed_bins_.emplace_back(); packed_bins_.emplace_back();
j = placers.size() - 1;
} }
} }
++it; ++it;

View file

@ -5,6 +5,7 @@
#include "printer_parts.h" #include "printer_parts.h"
#include <libnest2d/geometry_traits_nfp.hpp> #include <libnest2d/geometry_traits_nfp.hpp>
//#include "../tools/libnfpglue.hpp" //#include "../tools/libnfpglue.hpp"
//#include "../tools/nfp_svgnest_glue.hpp"
std::vector<libnest2d::Item>& prusaParts() { std::vector<libnest2d::Item>& prusaParts() {
static std::vector<libnest2d::Item> ret; static std::vector<libnest2d::Item> ret;
@ -219,21 +220,21 @@ TEST(GeometryAlgorithms, IsPointInsidePolygon) {
Point p = {1, 1}; Point p = {1, 1};
ASSERT_TRUE(rect.isPointInside(p)); ASSERT_TRUE(rect.isInside(p));
p = {11, 11}; p = {11, 11};
ASSERT_FALSE(rect.isPointInside(p)); ASSERT_FALSE(rect.isInside(p));
p = {11, 12}; p = {11, 12};
ASSERT_FALSE(rect.isPointInside(p)); ASSERT_FALSE(rect.isInside(p));
p = {3, 3}; p = {3, 3};
ASSERT_TRUE(rect.isPointInside(p)); ASSERT_TRUE(rect.isInside(p));
} }

View file

@ -56,7 +56,7 @@ libnfporb::point_t scale(const libnfporb::point_t& p, long double factor) {
NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother) NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
{ {
using Vertex = PointImpl; namespace sl = shapelike;
NfpR ret; NfpR ret;
@ -85,7 +85,7 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
// this can throw // this can throw
auto nfp = libnfporb::generateNFP(pstat, porb, true); 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); ct.reserve(nfp.front().size()+1);
for(auto v : nfp.front()) { for(auto v : nfp.front()) {
v = scale(v, refactor); v = scale(v, refactor);
@ -94,7 +94,7 @@ NfpR _nfp(const PolygonImpl &sh, const PolygonImpl &cother)
ct.push_back(ct.front()); ct.push_back(ct.front());
std::reverse(ct.begin(), ct.end()); 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) { for(size_t hidx = 1; hidx < nfp.size(); ++hidx) {
if(nfp[hidx].size() >= 3) { if(nfp[hidx].size() >= 3) {
rholes.emplace_back(); 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) { } catch(std::exception& e) {
std::cout << "Error: " << e.what() << "\nTrying with convex hull..." << std::endl; std::cout << "Error: " << e.what() << "\nTrying with convex hull..." << std::endl;
// auto ch_stat = ShapeLike::convexHull(sh); // auto ch_stat = ShapeLike::convexHull(sh);
// auto ch_orb = ShapeLike::convexHull(cother); // auto ch_orb = ShapeLike::convexHull(cother);
ret = Nfp::nfpConvexOnly(sh, cother); ret = nfp::nfpConvexOnly(sh, cother);
} }
return ret; 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) const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
{ {
return _nfp(sh, cother);//nfpConvexOnly(sh, 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) const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
{ {
return _nfp(sh, 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) const PolygonImpl &sh, const ClipperLib::PolygonImpl &cother)
{ {
return _nfp(sh, cother); return _nfp(sh, cother);

View file

@ -5,22 +5,22 @@
namespace libnest2d { namespace libnest2d {
using NfpR = Nfp::NfpResult<PolygonImpl>; using NfpR = nfp::NfpResult<PolygonImpl>;
NfpR _nfp(const PolygonImpl& sh, const PolygonImpl& cother); NfpR _nfp(const PolygonImpl& sh, const PolygonImpl& cother);
template<> template<>
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::CONVEX_ONLY> { struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::CONVEX_ONLY> {
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother); NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
}; };
template<> template<>
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::ONE_CONVEX> { struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::ONE_CONVEX> {
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother); NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother);
}; };
template<> template<>
struct Nfp::NfpImpl<PolygonImpl, NfpLevel::BOTH_CONCAVE> { struct nfp::NfpImpl<PolygonImpl, nfp::NfpLevel::BOTH_CONCAVE> {
NfpR operator()(const PolygonImpl& sh, const PolygonImpl& cother); 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); // NfpResult operator()(const PolygonImpl& sh, const PolygonImpl& cother);
//}; //};
template<> struct Nfp::MaxNfpLevel<PolygonImpl> { template<> struct nfp::MaxNfpLevel<PolygonImpl> {
static const BP2D_CONSTEXPR NfpLevel value = static const BP2D_CONSTEXPR NfpLevel value =
// NfpLevel::CONVEX_ONLY; // NfpLevel::CONVEX_ONLY;
NfpLevel::BOTH_CONCAVE; NfpLevel::BOTH_CONCAVE;

File diff suppressed because it is too large Load diff

View 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

View file

@ -270,6 +270,8 @@ void fillConfig(PConf& pcfg) {
// The accuracy of optimization. // The accuracy of optimization.
// Goes from 0.0 to 1.0 and scales performance as well // Goes from 0.0 to 1.0 and scales performance as well
pcfg.accuracy = 0.65f; pcfg.accuracy = 0.65f;
pcfg.parallel = false;
} }
template<class TBin> template<class TBin>
@ -291,6 +293,7 @@ protected:
std::vector<double> areacache_; std::vector<double> areacache_;
SpatIndex rtree_; SpatIndex rtree_;
double norm_; double norm_;
Pile pile_cache_;
public: public:
_ArrBase(const TBin& bin, Distance dist, _ArrBase(const TBin& bin, Distance dist,
@ -317,23 +320,26 @@ public:
std::function<void(unsigned)> progressind): std::function<void(unsigned)> progressind):
_ArrBase<Box>(bin, dist, progressind) _ArrBase<Box>(bin, dist, progressind)
{ {
pconf_.object_function = [this, bin] ( // pconf_.object_function = [this, bin] (
Pile& pile, // const Pile& pile_c,
const Item &item, // const Item &item,
const ItemGroup& rem) { // const ItemGroup& rem) {
auto result = objfunc(bin.center(), bin_area_, pile, // auto& pile = pile_cache_;
item, norm_, areacache_, rtree_, rem); // if(pile.size() != pile_c.size()) pile = pile_c;
double score = std::get<0>(result);
auto& fullbb = std::get<1>(result);
auto wdiff = fullbb.width() - bin.width(); // auto result = objfunc(bin.center(), bin_area_, pile,
auto hdiff = fullbb.height() - bin.height(); // item, norm_, areacache_, rtree_, rem);
if(wdiff > 0) score += std::pow(wdiff, 2) / norm_; // double score = std::get<0>(result);
if(hdiff > 0) score += std::pow(hdiff, 2) / norm_; // 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_); pck_.configure(pconf_);
} }
@ -350,10 +356,13 @@ public:
_ArrBase<lnCircle>(bin, dist, progressind) { _ArrBase<lnCircle>(bin, dist, progressind) {
pconf_.object_function = [this, &bin] ( pconf_.object_function = [this, &bin] (
Pile& pile, const Pile& pile_c,
const Item &item, const Item &item,
const ItemGroup& rem) { 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_, auto result = objfunc(bin.center(), bin_area_, pile, item, norm_,
areacache_, rtree_, rem); areacache_, rtree_, rem);
double score = std::get<0>(result); double score = std::get<0>(result);
@ -393,10 +402,13 @@ public:
_ArrBase<PolygonImpl>(bin, dist, progressind) _ArrBase<PolygonImpl>(bin, dist, progressind)
{ {
pconf_.object_function = [this, &bin] ( pconf_.object_function = [this, &bin] (
Pile& pile, const Pile& pile_c,
const Item &item, const Item &item,
const ItemGroup& rem) { const ItemGroup& rem) {
auto& pile = pile_cache_;
if(pile.size() != pile_c.size()) pile = pile_c;
auto binbb = sl::boundingBox(bin); auto binbb = sl::boundingBox(bin);
auto result = objfunc(binbb.center(), bin_area_, pile, item, norm_, auto result = objfunc(binbb.center(), bin_area_, pile, item, norm_,
areacache_, rtree_, rem); areacache_, rtree_, rem);
@ -417,10 +429,13 @@ public:
_ArrBase<Box>(Box(0, 0), dist, progressind) _ArrBase<Box>(Box(0, 0), dist, progressind)
{ {
this->pconf_.object_function = [this] ( this->pconf_.object_function = [this] (
Pile& pile, const Pile& pile_c,
const Item &item, const Item &item,
const ItemGroup& rem) { 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_, auto result = objfunc({0, 0}, 0, pile, item, norm_,
areacache_, rtree_, rem); areacache_, rtree_, rem);
return std::get<0>(result); return std::get<0>(result);