mirror of
				https://github.com/SoftFever/OrcaSlicer.git
				synced 2025-10-30 20:21:12 -06:00 
			
		
		
		
	parallel nesting can be enabled but fails with the current objectfunction.
This commit is contained in:
		
							parent
							
								
									e678368b23
								
							
						
					
					
						commit
						8617b0a409
					
				
					 21 changed files with 1833 additions and 490 deletions
				
			
		|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 tamasmeszaros
						tamasmeszaros