fix compilation on linux and mac
This commit is contained in:
parent
1764f3b57b
commit
e678368b23
18 changed files with 590 additions and 543 deletions
|
@ -31,6 +31,7 @@ set(LIBNEST2D_SRCFILES
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/common.hpp
|
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/common.hpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizer.hpp
|
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/optimizer.hpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/metaloop.hpp
|
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/metaloop.hpp
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/rotfinder.hpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/placer_boilerplate.hpp
|
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/placer_boilerplate.hpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/bottomleftplacer.hpp
|
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/bottomleftplacer.hpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/nfpplacer.hpp
|
${CMAKE_CURRENT_SOURCE_DIR}/libnest2d/placers/nfpplacer.hpp
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#include "tests/printer_parts.h"
|
#include "tests/printer_parts.h"
|
||||||
#include "tools/benchmark.h"
|
#include "tools/benchmark.h"
|
||||||
#include "tools/svgtools.hpp"
|
#include "tools/svgtools.hpp"
|
||||||
|
#include "libnest2d/rotfinder.hpp"
|
||||||
|
|
||||||
//#include "tools/libnfpglue.hpp"
|
//#include "tools/libnfpglue.hpp"
|
||||||
|
|
||||||
using namespace libnest2d;
|
using namespace libnest2d;
|
||||||
|
@ -64,7 +66,7 @@ void arrangeRectangles() {
|
||||||
// input.insert(input.end(), proba.begin(), proba.end());
|
// input.insert(input.end(), proba.begin(), proba.end());
|
||||||
// input.insert(input.end(), crasher.begin(), crasher.end());
|
// input.insert(input.end(), crasher.begin(), crasher.end());
|
||||||
|
|
||||||
// Box bin(250*SCALE, 210*SCALE);
|
Box bin(250*SCALE, 210*SCALE);
|
||||||
// PolygonImpl bin = {
|
// PolygonImpl bin = {
|
||||||
// {
|
// {
|
||||||
// {25*SCALE, 0},
|
// {25*SCALE, 0},
|
||||||
|
@ -80,12 +82,12 @@ void arrangeRectangles() {
|
||||||
// {}
|
// {}
|
||||||
// };
|
// };
|
||||||
|
|
||||||
_Circle<PointImpl> bin({0, 0}, 125*SCALE);
|
// _Circle<PointImpl> bin({0, 0}, 125*SCALE);
|
||||||
|
|
||||||
auto min_obj_distance = static_cast<Coord>(0*SCALE);
|
auto min_obj_distance = static_cast<Coord>(0*SCALE);
|
||||||
|
|
||||||
using Placer = strategies::_NofitPolyPlacer<PolygonImpl, decltype(bin)>;
|
using Placer = strategies::_NofitPolyPlacer<PolygonImpl, decltype(bin)>;
|
||||||
using Packer = Arranger<Placer, FirstFitSelection>;
|
using Packer = Nester<Placer, FirstFitSelection>;
|
||||||
|
|
||||||
Packer arrange(bin, min_obj_distance);
|
Packer arrange(bin, min_obj_distance);
|
||||||
|
|
||||||
|
@ -112,7 +114,9 @@ void arrangeRectangles() {
|
||||||
// svgw.writePackGroup(arrange.lastResult());
|
// svgw.writePackGroup(arrange.lastResult());
|
||||||
// svgw.save("debout");
|
// svgw.save("debout");
|
||||||
std::cout << "Remaining items: " << r << std::endl;
|
std::cout << "Remaining items: " << r << std::endl;
|
||||||
})/*.useMinimumBoundigBoxRotation()*/;
|
});
|
||||||
|
|
||||||
|
// findMinimumBoundingBoxRotations(input.begin(), input.end());
|
||||||
|
|
||||||
Benchmark bench;
|
Benchmark bench;
|
||||||
|
|
||||||
|
@ -120,7 +124,7 @@ void arrangeRectangles() {
|
||||||
Packer::ResultType result;
|
Packer::ResultType result;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
result = arrange.arrange(input.begin(), input.end());
|
result = arrange.execute(input.begin(), input.end());
|
||||||
} catch(GeometryException& ge) {
|
} catch(GeometryException& ge) {
|
||||||
std::cerr << "Geometry error: " << ge.what() << std::endl;
|
std::cerr << "Geometry error: " << ge.what() << std::endl;
|
||||||
return ;
|
return ;
|
||||||
|
@ -134,7 +138,7 @@ void arrangeRectangles() {
|
||||||
std::vector<double> eff;
|
std::vector<double> eff;
|
||||||
eff.reserve(result.size());
|
eff.reserve(result.size());
|
||||||
|
|
||||||
auto bin_area = ShapeLike::area<PolygonImpl>(bin);
|
auto bin_area = sl::area(bin);
|
||||||
for(auto& r : result) {
|
for(auto& r : result) {
|
||||||
double a = 0;
|
double a = 0;
|
||||||
std::for_each(r.begin(), r.end(), [&a] (Item& e ){ a += e.area(); });
|
std::for_each(r.begin(), r.end(), [&a] (Item& e ){ a += e.area(); });
|
||||||
|
@ -156,7 +160,7 @@ void arrangeRectangles() {
|
||||||
std::cout << ") Total: " << total << std::endl;
|
std::cout << ") Total: " << total << std::endl;
|
||||||
|
|
||||||
for(auto& it : input) {
|
for(auto& it : input) {
|
||||||
auto ret = ShapeLike::isValid(it.transformedShape());
|
auto ret = sl::isValid(it.transformedShape());
|
||||||
std::cout << ret.second << std::endl;
|
std::cout << ret.second << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +181,7 @@ void arrangeRectangles() {
|
||||||
|
|
||||||
int main(void /*int argc, char **argv*/) {
|
int main(void /*int argc, char **argv*/) {
|
||||||
arrangeRectangles();
|
arrangeRectangles();
|
||||||
// findDegenerateCase();
|
//// findDegenerateCase();
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ using Point = PointImpl;
|
||||||
using Coord = TCoord<PointImpl>;
|
using Coord = TCoord<PointImpl>;
|
||||||
using Box = _Box<PointImpl>;
|
using Box = _Box<PointImpl>;
|
||||||
using Segment = _Segment<PointImpl>;
|
using Segment = _Segment<PointImpl>;
|
||||||
|
using Circle = _Circle<PointImpl>;
|
||||||
|
|
||||||
using Item = _Item<PolygonImpl>;
|
using Item = _Item<PolygonImpl>;
|
||||||
using Rectangle = _Rectangle<PolygonImpl>;
|
using Rectangle = _Rectangle<PolygonImpl>;
|
||||||
|
@ -36,9 +37,6 @@ using DJDHeuristic = strategies::_DJDHeuristic<PolygonImpl>;
|
||||||
using NfpPlacer = strategies::_NofitPolyPlacer<PolygonImpl>;
|
using NfpPlacer = strategies::_NofitPolyPlacer<PolygonImpl>;
|
||||||
using BottomLeftPlacer = strategies::_BottomLeftPlacer<PolygonImpl>;
|
using BottomLeftPlacer = strategies::_BottomLeftPlacer<PolygonImpl>;
|
||||||
|
|
||||||
//template<NfpLevel lvl = NfpLevel::BOTH_CONCAVE_WITH_HOLES>
|
|
||||||
//using NofitPolyPlacer = strategies::_NofitPolyPlacer<PolygonImpl, lvl>;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // LIBNEST2D_H
|
#endif // LIBNEST2D_H
|
||||||
|
|
|
@ -36,7 +36,7 @@ using libnest2d::setX;
|
||||||
using libnest2d::setY;
|
using libnest2d::setY;
|
||||||
using Box = libnest2d::_Box<PointImpl>;
|
using Box = libnest2d::_Box<PointImpl>;
|
||||||
using Segment = libnest2d::_Segment<PointImpl>;
|
using Segment = libnest2d::_Segment<PointImpl>;
|
||||||
using Shapes = libnest2d::Nfp::Shapes<PolygonImpl>;
|
using Shapes = libnest2d::nfp::Shapes<PolygonImpl>;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -241,11 +241,11 @@ template<> struct tag<bp2d::PolygonImpl> {
|
||||||
|
|
||||||
template<> struct exterior_ring<bp2d::PolygonImpl> {
|
template<> struct exterior_ring<bp2d::PolygonImpl> {
|
||||||
static inline bp2d::PathImpl& get(bp2d::PolygonImpl& p) {
|
static inline bp2d::PathImpl& get(bp2d::PolygonImpl& p) {
|
||||||
return libnest2d::ShapeLike::getContour(p);
|
return libnest2d::shapelike::getContour(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bp2d::PathImpl const& get(bp2d::PolygonImpl const& p) {
|
static inline bp2d::PathImpl const& get(bp2d::PolygonImpl const& p) {
|
||||||
return libnest2d::ShapeLike::getContour(p);
|
return libnest2d::shapelike::getContour(p);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -271,13 +271,13 @@ struct interior_rings<bp2d::PolygonImpl> {
|
||||||
static inline libnest2d::THolesContainer<bp2d::PolygonImpl>& get(
|
static inline libnest2d::THolesContainer<bp2d::PolygonImpl>& get(
|
||||||
bp2d::PolygonImpl& p)
|
bp2d::PolygonImpl& p)
|
||||||
{
|
{
|
||||||
return libnest2d::ShapeLike::holes(p);
|
return libnest2d::shapelike::holes(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline const libnest2d::THolesContainer<bp2d::PolygonImpl>& get(
|
static inline const libnest2d::THolesContainer<bp2d::PolygonImpl>& get(
|
||||||
bp2d::PolygonImpl const& p)
|
bp2d::PolygonImpl const& p)
|
||||||
{
|
{
|
||||||
return libnest2d::ShapeLike::holes(p);
|
return libnest2d::shapelike::holes(p);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -311,83 +311,78 @@ struct range_value<bp2d::Shapes> {
|
||||||
|
|
||||||
namespace libnest2d { // Now the algorithms that boost can provide...
|
namespace libnest2d { // Now the algorithms that boost can provide...
|
||||||
|
|
||||||
|
namespace pointlike {
|
||||||
template<>
|
template<>
|
||||||
inline double PointLike::distance(const PointImpl& p1,
|
inline double distance(const PointImpl& p1, const PointImpl& p2 )
|
||||||
const PointImpl& p2 )
|
|
||||||
{
|
{
|
||||||
return boost::geometry::distance(p1, p2);
|
return boost::geometry::distance(p1, p2);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline double PointLike::distance(const PointImpl& p,
|
inline double distance(const PointImpl& p, const bp2d::Segment& seg )
|
||||||
const bp2d::Segment& seg )
|
|
||||||
{
|
{
|
||||||
return boost::geometry::distance(p, seg);
|
return boost::geometry::distance(p, seg);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace shapelike {
|
||||||
// Tell libnest2d how to make string out of a ClipperPolygon object
|
// Tell libnest2d how to make string out of a ClipperPolygon object
|
||||||
template<>
|
template<>
|
||||||
inline bool ShapeLike::intersects(const PathImpl& sh1,
|
inline bool intersects(const PathImpl& sh1, const PathImpl& sh2)
|
||||||
const PathImpl& sh2)
|
|
||||||
{
|
{
|
||||||
return boost::geometry::intersects(sh1, sh2);
|
return boost::geometry::intersects(sh1, sh2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell libnest2d how to make string out of a ClipperPolygon object
|
// Tell libnest2d how to make string out of a ClipperPolygon object
|
||||||
template<>
|
template<>
|
||||||
inline bool ShapeLike::intersects(const PolygonImpl& sh1,
|
inline bool intersects(const PolygonImpl& sh1, const PolygonImpl& sh2)
|
||||||
const PolygonImpl& sh2)
|
|
||||||
{
|
{
|
||||||
return boost::geometry::intersects(sh1, sh2);
|
return boost::geometry::intersects(sh1, sh2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell libnest2d how to make string out of a ClipperPolygon object
|
// Tell libnest2d how to make string out of a ClipperPolygon object
|
||||||
template<>
|
template<>
|
||||||
inline bool ShapeLike::intersects(const bp2d::Segment& s1,
|
inline bool intersects(const bp2d::Segment& s1, const bp2d::Segment& s2)
|
||||||
const bp2d::Segment& s2)
|
|
||||||
{
|
{
|
||||||
return boost::geometry::intersects(s1, s2);
|
return boost::geometry::intersects(s1, s2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef DISABLE_BOOST_AREA
|
#ifndef DISABLE_BOOST_AREA
|
||||||
template<>
|
template<>
|
||||||
inline double ShapeLike::area(const PolygonImpl& shape)
|
inline double area(const PolygonImpl& shape, const PolygonTag&)
|
||||||
{
|
{
|
||||||
return boost::geometry::area(shape);
|
return boost::geometry::area(shape);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline bool ShapeLike::isInside<PolygonImpl>(const PointImpl& point,
|
inline bool isInside<PolygonImpl>(const PointImpl& point,
|
||||||
const PolygonImpl& shape)
|
const PolygonImpl& shape)
|
||||||
{
|
{
|
||||||
return boost::geometry::within(point, shape);
|
return boost::geometry::within(point, shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline bool ShapeLike::isInside(const PolygonImpl& sh1,
|
inline bool isInside(const PolygonImpl& sh1, const PolygonImpl& sh2)
|
||||||
const PolygonImpl& sh2)
|
|
||||||
{
|
{
|
||||||
return boost::geometry::within(sh1, sh2);
|
return boost::geometry::within(sh1, sh2);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline bool ShapeLike::touches( const PolygonImpl& sh1,
|
inline bool touches(const PolygonImpl& sh1, const PolygonImpl& sh2)
|
||||||
const PolygonImpl& sh2)
|
|
||||||
{
|
{
|
||||||
return boost::geometry::touches(sh1, sh2);
|
return boost::geometry::touches(sh1, sh2);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline bool ShapeLike::touches( const PointImpl& point,
|
inline bool touches( const PointImpl& point, const PolygonImpl& shape)
|
||||||
const PolygonImpl& shape)
|
|
||||||
{
|
{
|
||||||
return boost::geometry::touches(point, shape);
|
return boost::geometry::touches(point, shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef DISABLE_BOOST_BOUNDING_BOX
|
#ifndef DISABLE_BOOST_BOUNDING_BOX
|
||||||
template<>
|
template<>
|
||||||
inline bp2d::Box ShapeLike::boundingBox(const PolygonImpl& sh)
|
inline bp2d::Box boundingBox(const PolygonImpl& sh, const PolygonTag&)
|
||||||
{
|
{
|
||||||
bp2d::Box b;
|
bp2d::Box b;
|
||||||
boost::geometry::envelope(sh, b);
|
boost::geometry::envelope(sh, b);
|
||||||
|
@ -395,7 +390,7 @@ inline bp2d::Box ShapeLike::boundingBox(const PolygonImpl& sh)
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline bp2d::Box ShapeLike::boundingBox<PolygonImpl>(const bp2d::Shapes& shapes)
|
inline bp2d::Box boundingBox<PolygonImpl>(const bp2d::Shapes& shapes)
|
||||||
{
|
{
|
||||||
bp2d::Box b;
|
bp2d::Box b;
|
||||||
boost::geometry::envelope(shapes, b);
|
boost::geometry::envelope(shapes, b);
|
||||||
|
@ -405,7 +400,7 @@ inline bp2d::Box ShapeLike::boundingBox<PolygonImpl>(const bp2d::Shapes& shapes)
|
||||||
|
|
||||||
#ifndef DISABLE_BOOST_CONVEX_HULL
|
#ifndef DISABLE_BOOST_CONVEX_HULL
|
||||||
template<>
|
template<>
|
||||||
inline PolygonImpl ShapeLike::convexHull(const PolygonImpl& sh)
|
inline PolygonImpl convexHull(const PolygonImpl& sh)
|
||||||
{
|
{
|
||||||
PolygonImpl ret;
|
PolygonImpl ret;
|
||||||
boost::geometry::convex_hull(sh, ret);
|
boost::geometry::convex_hull(sh, ret);
|
||||||
|
@ -413,7 +408,7 @@ inline PolygonImpl ShapeLike::convexHull(const PolygonImpl& sh)
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline PolygonImpl ShapeLike::convexHull(const bp2d::Shapes& shapes)
|
inline PolygonImpl convexHull(const bp2d::Shapes& shapes)
|
||||||
{
|
{
|
||||||
PolygonImpl ret;
|
PolygonImpl ret;
|
||||||
boost::geometry::convex_hull(shapes, ret);
|
boost::geometry::convex_hull(shapes, ret);
|
||||||
|
@ -423,7 +418,7 @@ inline PolygonImpl ShapeLike::convexHull(const bp2d::Shapes& shapes)
|
||||||
|
|
||||||
#ifndef DISABLE_BOOST_ROTATE
|
#ifndef DISABLE_BOOST_ROTATE
|
||||||
template<>
|
template<>
|
||||||
inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads)
|
inline void rotate(PolygonImpl& sh, const Radians& rads)
|
||||||
{
|
{
|
||||||
namespace trans = boost::geometry::strategy::transform;
|
namespace trans = boost::geometry::strategy::transform;
|
||||||
|
|
||||||
|
@ -437,7 +432,7 @@ inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads)
|
||||||
|
|
||||||
#ifndef DISABLE_BOOST_TRANSLATE
|
#ifndef DISABLE_BOOST_TRANSLATE
|
||||||
template<>
|
template<>
|
||||||
inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs)
|
inline void translate(PolygonImpl& sh, const PointImpl& offs)
|
||||||
{
|
{
|
||||||
namespace trans = boost::geometry::strategy::transform;
|
namespace trans = boost::geometry::strategy::transform;
|
||||||
|
|
||||||
|
@ -451,26 +446,15 @@ inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs)
|
||||||
|
|
||||||
#ifndef DISABLE_BOOST_OFFSET
|
#ifndef DISABLE_BOOST_OFFSET
|
||||||
template<>
|
template<>
|
||||||
inline void ShapeLike::offset(PolygonImpl& sh, bp2d::Coord distance)
|
inline void offset(PolygonImpl& sh, bp2d::Coord distance)
|
||||||
{
|
{
|
||||||
PolygonImpl cpy = sh;
|
PolygonImpl cpy = sh;
|
||||||
boost::geometry::buffer(cpy, sh, distance);
|
boost::geometry::buffer(cpy, sh, distance);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DISABLE_BOOST_NFP_MERGE
|
|
||||||
template<>
|
|
||||||
inline bp2d::Shapes Nfp::merge(const bp2d::Shapes& shapes,
|
|
||||||
const PolygonImpl& sh)
|
|
||||||
{
|
|
||||||
bp2d::Shapes retv;
|
|
||||||
boost::geometry::union_(shapes, sh, retv);
|
|
||||||
return retv;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef DISABLE_BOOST_SERIALIZE
|
#ifndef DISABLE_BOOST_SERIALIZE
|
||||||
template<> inline std::string ShapeLike::serialize<libnest2d::Formats::SVG>(
|
template<> inline std::string serialize<libnest2d::Formats::SVG>(
|
||||||
const PolygonImpl& sh, double scale)
|
const PolygonImpl& sh, double scale)
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
@ -482,14 +466,14 @@ template<> inline std::string ShapeLike::serialize<libnest2d::Formats::SVG>(
|
||||||
|
|
||||||
Polygonf::ring_type ring;
|
Polygonf::ring_type ring;
|
||||||
Polygonf::inner_container_type holes;
|
Polygonf::inner_container_type holes;
|
||||||
ring.reserve(ShapeLike::contourVertexCount(sh));
|
ring.reserve(shapelike::contourVertexCount(sh));
|
||||||
|
|
||||||
for(auto it = ShapeLike::cbegin(sh); it != ShapeLike::cend(sh); it++) {
|
for(auto it = shapelike::cbegin(sh); it != shapelike::cend(sh); it++) {
|
||||||
auto& v = *it;
|
auto& v = *it;
|
||||||
ring.emplace_back(getX(v)*scale, getY(v)*scale);
|
ring.emplace_back(getX(v)*scale, getY(v)*scale);
|
||||||
};
|
};
|
||||||
|
|
||||||
auto H = ShapeLike::holes(sh);
|
auto H = shapelike::holes(sh);
|
||||||
for(PathImpl& h : H ) {
|
for(PathImpl& h : H ) {
|
||||||
Polygonf::ring_type hf;
|
Polygonf::ring_type hf;
|
||||||
for(auto it = h.begin(); it != h.end(); it++) {
|
for(auto it = h.begin(); it != h.end(); it++) {
|
||||||
|
@ -512,21 +496,37 @@ template<> inline std::string ShapeLike::serialize<libnest2d::Formats::SVG>(
|
||||||
|
|
||||||
#ifndef DISABLE_BOOST_UNSERIALIZE
|
#ifndef DISABLE_BOOST_UNSERIALIZE
|
||||||
template<>
|
template<>
|
||||||
inline void ShapeLike::unserialize<libnest2d::Formats::SVG>(
|
inline void unserialize<libnest2d::Formats::SVG>(
|
||||||
PolygonImpl& sh,
|
PolygonImpl& sh,
|
||||||
const std::string& str)
|
const std::string& str)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template<> inline std::pair<bool, std::string>
|
template<> inline std::pair<bool, std::string> isValid(const PolygonImpl& sh)
|
||||||
ShapeLike::isValid(const PolygonImpl& sh)
|
|
||||||
{
|
{
|
||||||
std::string message;
|
std::string message;
|
||||||
bool ret = boost::geometry::is_valid(sh, message);
|
bool ret = boost::geometry::is_valid(sh, message);
|
||||||
|
|
||||||
return {ret, message};
|
return {ret, message};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace nfp {
|
||||||
|
|
||||||
|
#ifndef DISABLE_BOOST_NFP_MERGE
|
||||||
|
template<>
|
||||||
|
inline bp2d::Shapes Nfp::merge(const bp2d::Shapes& shapes,
|
||||||
|
const PolygonImpl& sh)
|
||||||
|
{
|
||||||
|
bp2d::Shapes retv;
|
||||||
|
boost::geometry::union_(shapes, sh, retv);
|
||||||
|
return retv;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,9 @@ struct PolygonImpl {
|
||||||
PathImpl Contour;
|
PathImpl Contour;
|
||||||
HoleStore Holes;
|
HoleStore Holes;
|
||||||
|
|
||||||
|
using Tag = libnest2d::PolygonTag;
|
||||||
|
using PointType = PointImpl;
|
||||||
|
|
||||||
inline PolygonImpl() = default;
|
inline PolygonImpl() = default;
|
||||||
|
|
||||||
inline explicit PolygonImpl(const PathImpl& cont): Contour(cont) {}
|
inline explicit PolygonImpl(const PathImpl& cont): Contour(cont) {}
|
||||||
|
@ -113,35 +116,32 @@ template<> struct CountourType<PolygonImpl> {
|
||||||
using Type = PathImpl;
|
using Type = PathImpl;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace pointlike {
|
||||||
|
|
||||||
// Tell binpack2d how to extract the X coord from a ClipperPoint object
|
// Tell binpack2d how to extract the X coord from a ClipperPoint object
|
||||||
template<> inline TCoord<PointImpl> PointLike::x(const PointImpl& p)
|
template<> inline TCoord<PointImpl> x(const PointImpl& p)
|
||||||
{
|
{
|
||||||
return p.X;
|
return p.X;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell binpack2d how to extract the Y coord from a ClipperPoint object
|
// Tell binpack2d how to extract the Y coord from a ClipperPoint object
|
||||||
template<> inline TCoord<PointImpl> PointLike::y(const PointImpl& p)
|
template<> inline TCoord<PointImpl> y(const PointImpl& p)
|
||||||
{
|
{
|
||||||
return p.Y;
|
return p.Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell binpack2d how to extract the X coord from a ClipperPoint object
|
// Tell binpack2d how to extract the X coord from a ClipperPoint object
|
||||||
template<> inline TCoord<PointImpl>& PointLike::x(PointImpl& p)
|
template<> inline TCoord<PointImpl>& x(PointImpl& p)
|
||||||
{
|
{
|
||||||
return p.X;
|
return p.X;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell binpack2d how to extract the Y coord from a ClipperPoint object
|
// Tell binpack2d how to extract the Y coord from a ClipperPoint object
|
||||||
template<>
|
template<> inline TCoord<PointImpl>& y(PointImpl& p)
|
||||||
inline TCoord<PointImpl>& PointLike::y(PointImpl& p)
|
|
||||||
{
|
{
|
||||||
return p.Y;
|
return p.Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
|
||||||
inline void ShapeLike::reserve(PolygonImpl& sh, size_t vertex_capacity)
|
|
||||||
{
|
|
||||||
return sh.Contour.reserve(vertex_capacity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DISABLE_BOOST_AREA
|
#define DISABLE_BOOST_AREA
|
||||||
|
@ -175,16 +175,28 @@ inline double area<Orientation::COUNTER_CLOCKWISE>(const PolygonImpl& sh) {
|
||||||
|
|
||||||
return ClipperLib::Area(sh.Contour) + a;
|
return ClipperLib::Area(sh.Contour) + a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<> struct HolesContainer<PolygonImpl> {
|
||||||
|
using Type = ClipperLib::Paths;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace shapelike {
|
||||||
|
|
||||||
|
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 binpack2d how to make string out of a ClipperPolygon object
|
||||||
template<>
|
template<> inline double area(const PolygonImpl& sh, const PolygonTag&)
|
||||||
inline double ShapeLike::area(const PolygonImpl& sh) {
|
{
|
||||||
return _smartarea::area<OrientationType<PolygonImpl>::Value>(sh);
|
return _smartarea::area<OrientationType<PolygonImpl>::Value>(sh);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<> inline void offset(PolygonImpl& sh, TCoord<PointImpl> distance)
|
||||||
inline void ShapeLike::offset(PolygonImpl& sh, TCoord<PointImpl> distance) {
|
{
|
||||||
#define DISABLE_BOOST_OFFSET
|
#define DISABLE_BOOST_OFFSET
|
||||||
|
|
||||||
using ClipperLib::ClipperOffset;
|
using ClipperLib::ClipperOffset;
|
||||||
|
@ -234,7 +246,8 @@ inline void ShapeLike::offset(PolygonImpl& sh, TCoord<PointImpl> distance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell libnest2d how to make string out of a ClipperPolygon object
|
// Tell libnest2d how to make string out of a ClipperPolygon object
|
||||||
template<> inline std::string ShapeLike::toString(const PolygonImpl& sh) {
|
template<> inline std::string toString(const PolygonImpl& sh)
|
||||||
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
||||||
ss << "Contour {\n";
|
ss << "Contour {\n";
|
||||||
|
@ -256,38 +269,31 @@ template<> inline std::string ShapeLike::toString(const PolygonImpl& sh) {
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<> inline TVertexIterator<PolygonImpl> begin(PolygonImpl& sh)
|
||||||
inline TVertexIterator<PolygonImpl> ShapeLike::begin(PolygonImpl& sh)
|
|
||||||
{
|
{
|
||||||
return sh.Contour.begin();
|
return sh.Contour.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<> inline TVertexIterator<PolygonImpl> end(PolygonImpl& sh)
|
||||||
inline TVertexIterator<PolygonImpl> ShapeLike::end(PolygonImpl& sh)
|
|
||||||
{
|
{
|
||||||
return sh.Contour.end();
|
return sh.Contour.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline TVertexConstIterator<PolygonImpl> ShapeLike::cbegin(
|
inline TVertexConstIterator<PolygonImpl> cbegin(const PolygonImpl& sh)
|
||||||
const PolygonImpl& sh)
|
|
||||||
{
|
{
|
||||||
return sh.Contour.cbegin();
|
return sh.Contour.cbegin();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<> inline TVertexConstIterator<PolygonImpl> cend(
|
||||||
inline TVertexConstIterator<PolygonImpl> ShapeLike::cend(
|
|
||||||
const PolygonImpl& sh)
|
const PolygonImpl& sh)
|
||||||
{
|
{
|
||||||
return sh.Contour.cend();
|
return sh.Contour.cend();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> struct HolesContainer<PolygonImpl> {
|
template<>
|
||||||
using Type = ClipperLib::Paths;
|
inline PolygonImpl create(const PathImpl& path, const HoleStore& holes)
|
||||||
};
|
{
|
||||||
|
|
||||||
template<> inline PolygonImpl ShapeLike::create(const PathImpl& path,
|
|
||||||
const HoleStore& holes) {
|
|
||||||
PolygonImpl p;
|
PolygonImpl p;
|
||||||
p.Contour = path;
|
p.Contour = path;
|
||||||
|
|
||||||
|
@ -308,8 +314,7 @@ template<> inline PolygonImpl ShapeLike::create(const PathImpl& path,
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> inline PolygonImpl ShapeLike::create( PathImpl&& path,
|
template<> inline PolygonImpl create( PathImpl&& path, HoleStore&& holes) {
|
||||||
HoleStore&& holes) {
|
|
||||||
PolygonImpl p;
|
PolygonImpl p;
|
||||||
p.Contour.swap(path);
|
p.Contour.swap(path);
|
||||||
|
|
||||||
|
@ -331,49 +336,49 @@ template<> inline PolygonImpl ShapeLike::create( PathImpl&& path,
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> inline const THolesContainer<PolygonImpl>&
|
template<>
|
||||||
ShapeLike::holes(const PolygonImpl& sh)
|
inline const THolesContainer<PolygonImpl>& holes(const PolygonImpl& sh)
|
||||||
{
|
{
|
||||||
return sh.Holes;
|
return sh.Holes;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> inline THolesContainer<PolygonImpl>&
|
template<> inline THolesContainer<PolygonImpl>& holes(PolygonImpl& sh)
|
||||||
ShapeLike::holes(PolygonImpl& sh)
|
|
||||||
{
|
{
|
||||||
return sh.Holes;
|
return sh.Holes;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> inline TContour<PolygonImpl>&
|
template<>
|
||||||
ShapeLike::getHole(PolygonImpl& sh, unsigned long idx)
|
inline TContour<PolygonImpl>& getHole(PolygonImpl& sh, unsigned long idx)
|
||||||
{
|
{
|
||||||
return sh.Holes[idx];
|
return sh.Holes[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> inline const TContour<PolygonImpl>&
|
template<>
|
||||||
ShapeLike::getHole(const PolygonImpl& sh, unsigned long idx)
|
inline const TContour<PolygonImpl>& getHole(const PolygonImpl& sh,
|
||||||
|
unsigned long idx)
|
||||||
{
|
{
|
||||||
return sh.Holes[idx];
|
return sh.Holes[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> inline size_t ShapeLike::holeCount(const PolygonImpl& sh)
|
template<> inline size_t holeCount(const PolygonImpl& sh)
|
||||||
{
|
{
|
||||||
return sh.Holes.size();
|
return sh.Holes.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> inline PathImpl& ShapeLike::getContour(PolygonImpl& sh)
|
template<> inline PathImpl& getContour(PolygonImpl& sh)
|
||||||
{
|
{
|
||||||
return sh.Contour;
|
return sh.Contour;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
inline const PathImpl& ShapeLike::getContour(const PolygonImpl& sh)
|
inline const PathImpl& getContour(const PolygonImpl& sh)
|
||||||
{
|
{
|
||||||
return sh.Contour;
|
return sh.Contour;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define DISABLE_BOOST_TRANSLATE
|
#define DISABLE_BOOST_TRANSLATE
|
||||||
template<>
|
template<>
|
||||||
inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs)
|
inline void translate(PolygonImpl& sh, const PointImpl& offs)
|
||||||
{
|
{
|
||||||
for(auto& p : sh.Contour) { p += offs; }
|
for(auto& p : sh.Contour) { p += offs; }
|
||||||
for(auto& hole : sh.Holes) for(auto& p : hole) { p += offs; }
|
for(auto& hole : sh.Holes) for(auto& p : hole) { p += offs; }
|
||||||
|
@ -381,7 +386,7 @@ inline void ShapeLike::translate(PolygonImpl& sh, const PointImpl& offs)
|
||||||
|
|
||||||
#define DISABLE_BOOST_ROTATE
|
#define DISABLE_BOOST_ROTATE
|
||||||
template<>
|
template<>
|
||||||
inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads)
|
inline void rotate(PolygonImpl& sh, const Radians& rads)
|
||||||
{
|
{
|
||||||
using Coord = TCoord<PointImpl>;
|
using Coord = TCoord<PointImpl>;
|
||||||
|
|
||||||
|
@ -402,9 +407,11 @@ inline void ShapeLike::rotate(PolygonImpl& sh, const Radians& rads)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace shapelike
|
||||||
|
|
||||||
#define DISABLE_BOOST_NFP_MERGE
|
#define DISABLE_BOOST_NFP_MERGE
|
||||||
inline Nfp::Shapes<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
|
inline nfp::Shapes<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
|
||||||
Nfp::Shapes<PolygonImpl> retv;
|
nfp::Shapes<PolygonImpl> retv;
|
||||||
|
|
||||||
ClipperLib::PolyTree result;
|
ClipperLib::PolyTree result;
|
||||||
clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNegative);
|
clipper.Execute(ClipperLib::ctUnion, result, ClipperLib::pftNegative);
|
||||||
|
@ -438,8 +445,10 @@ inline Nfp::Shapes<PolygonImpl> _merge(ClipperLib::Clipper& clipper) {
|
||||||
return retv;
|
return retv;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<> inline Nfp::Shapes<PolygonImpl>
|
namespace nfp {
|
||||||
Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes)
|
|
||||||
|
template<> inline nfp::Shapes<PolygonImpl>
|
||||||
|
merge(const nfp::Shapes<PolygonImpl>& shapes)
|
||||||
{
|
{
|
||||||
ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution);
|
ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution);
|
||||||
|
|
||||||
|
@ -461,6 +470,8 @@ Nfp::merge(const Nfp::Shapes<PolygonImpl>& shapes)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//#define DISABLE_BOOST_SERIALIZE
|
//#define DISABLE_BOOST_SERIALIZE
|
||||||
//#define DISABLE_BOOST_UNSERIALIZE
|
//#define DISABLE_BOOST_UNSERIALIZE
|
||||||
|
|
||||||
|
|
|
@ -60,6 +60,10 @@ struct PointPair {
|
||||||
RawPoint p2;
|
RawPoint p2;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct PolygonTag {};
|
||||||
|
struct BoxTag {};
|
||||||
|
struct CircleTag {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief An abstraction of a box;
|
* \brief An abstraction of a box;
|
||||||
*/
|
*/
|
||||||
|
@ -69,6 +73,9 @@ class _Box: PointPair<RawPoint> {
|
||||||
using PointPair<RawPoint>::p2;
|
using PointPair<RawPoint>::p2;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
using Tag = BoxTag;
|
||||||
|
using PointType = RawPoint;
|
||||||
|
|
||||||
inline _Box() = default;
|
inline _Box() = default;
|
||||||
inline _Box(const RawPoint& p, const RawPoint& pp):
|
inline _Box(const RawPoint& p, const RawPoint& pp):
|
||||||
PointPair<RawPoint>({p, pp}) {}
|
PointPair<RawPoint>({p, pp}) {}
|
||||||
|
@ -98,6 +105,9 @@ class _Circle {
|
||||||
double radius_ = 0;
|
double radius_ = 0;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
using Tag = CircleTag;
|
||||||
|
using PointType = RawPoint;
|
||||||
|
|
||||||
_Circle() = default;
|
_Circle() = default;
|
||||||
|
|
||||||
_Circle(const RawPoint& center, double r): center_(center), radius_(r) {}
|
_Circle(const RawPoint& center, double r): center_(center), radius_(r) {}
|
||||||
|
@ -123,6 +133,8 @@ class _Segment: PointPair<RawPoint> {
|
||||||
mutable Radians angletox_ = std::nan("");
|
mutable Radians angletox_ = std::nan("");
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
using PointType = RawPoint;
|
||||||
|
|
||||||
inline _Segment() = default;
|
inline _Segment() = default;
|
||||||
|
|
||||||
inline _Segment(const RawPoint& p, const RawPoint& pp):
|
inline _Segment(const RawPoint& p, const RawPoint& pp):
|
||||||
|
@ -156,36 +168,36 @@ public:
|
||||||
inline double length();
|
inline double length();
|
||||||
};
|
};
|
||||||
|
|
||||||
// This struct serves as a namespace. The only difference is that is can be
|
// This struct serves almost as a namespace. The only difference is that is can
|
||||||
// used in friend declarations.
|
// used in friend declarations.
|
||||||
struct PointLike {
|
namespace pointlike {
|
||||||
|
|
||||||
template<class RawPoint>
|
template<class RawPoint>
|
||||||
static TCoord<RawPoint> x(const RawPoint& p)
|
inline TCoord<RawPoint> x(const RawPoint& p)
|
||||||
{
|
{
|
||||||
return p.x();
|
return p.x();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawPoint>
|
template<class RawPoint>
|
||||||
static TCoord<RawPoint> y(const RawPoint& p)
|
inline TCoord<RawPoint> y(const RawPoint& p)
|
||||||
{
|
{
|
||||||
return p.y();
|
return p.y();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawPoint>
|
template<class RawPoint>
|
||||||
static TCoord<RawPoint>& x(RawPoint& p)
|
inline TCoord<RawPoint>& x(RawPoint& p)
|
||||||
{
|
{
|
||||||
return p.x();
|
return p.x();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawPoint>
|
template<class RawPoint>
|
||||||
static TCoord<RawPoint>& y(RawPoint& p)
|
inline TCoord<RawPoint>& y(RawPoint& p)
|
||||||
{
|
{
|
||||||
return p.y();
|
return p.y();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawPoint>
|
template<class RawPoint>
|
||||||
static double distance(const RawPoint& /*p1*/, const RawPoint& /*p2*/)
|
inline double distance(const RawPoint& /*p1*/, const RawPoint& /*p2*/)
|
||||||
{
|
{
|
||||||
static_assert(always_false<RawPoint>::value,
|
static_assert(always_false<RawPoint>::value,
|
||||||
"PointLike::distance(point, point) unimplemented!");
|
"PointLike::distance(point, point) unimplemented!");
|
||||||
|
@ -193,7 +205,7 @@ struct PointLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawPoint>
|
template<class RawPoint>
|
||||||
static double distance(const RawPoint& /*p1*/,
|
inline double distance(const RawPoint& /*p1*/,
|
||||||
const _Segment<RawPoint>& /*s*/)
|
const _Segment<RawPoint>& /*s*/)
|
||||||
{
|
{
|
||||||
static_assert(always_false<RawPoint>::value,
|
static_assert(always_false<RawPoint>::value,
|
||||||
|
@ -202,13 +214,13 @@ struct PointLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawPoint>
|
template<class RawPoint>
|
||||||
static std::pair<TCoord<RawPoint>, bool> horizontalDistance(
|
inline std::pair<TCoord<RawPoint>, bool> horizontalDistance(
|
||||||
const RawPoint& p, const _Segment<RawPoint>& s)
|
const RawPoint& p, const _Segment<RawPoint>& s)
|
||||||
{
|
{
|
||||||
using Unit = TCoord<RawPoint>;
|
using Unit = TCoord<RawPoint>;
|
||||||
auto x = PointLike::x(p), y = PointLike::y(p);
|
auto x = pointlike::x(p), y = pointlike::y(p);
|
||||||
auto x1 = PointLike::x(s.first()), y1 = PointLike::y(s.first());
|
auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first());
|
||||||
auto x2 = PointLike::x(s.second()), y2 = PointLike::y(s.second());
|
auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second());
|
||||||
|
|
||||||
TCoord<RawPoint> ret;
|
TCoord<RawPoint> ret;
|
||||||
|
|
||||||
|
@ -228,13 +240,13 @@ struct PointLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawPoint>
|
template<class RawPoint>
|
||||||
static std::pair<TCoord<RawPoint>, bool> verticalDistance(
|
inline std::pair<TCoord<RawPoint>, bool> verticalDistance(
|
||||||
const RawPoint& p, const _Segment<RawPoint>& s)
|
const RawPoint& p, const _Segment<RawPoint>& s)
|
||||||
{
|
{
|
||||||
using Unit = TCoord<RawPoint>;
|
using Unit = TCoord<RawPoint>;
|
||||||
auto x = PointLike::x(p), y = PointLike::y(p);
|
auto x = pointlike::x(p), y = pointlike::y(p);
|
||||||
auto x1 = PointLike::x(s.first()), y1 = PointLike::y(s.first());
|
auto x1 = pointlike::x(s.first()), y1 = pointlike::y(s.first());
|
||||||
auto x2 = PointLike::x(s.second()), y2 = PointLike::y(s.second());
|
auto x2 = pointlike::x(s.second()), y2 = pointlike::y(s.second());
|
||||||
|
|
||||||
TCoord<RawPoint> ret;
|
TCoord<RawPoint> ret;
|
||||||
|
|
||||||
|
@ -252,36 +264,36 @@ struct PointLike {
|
||||||
|
|
||||||
return {ret, true};
|
return {ret, true};
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
template<class RawPoint>
|
template<class RawPoint>
|
||||||
TCoord<RawPoint> _Box<RawPoint>::width() const BP2D_NOEXCEPT
|
TCoord<RawPoint> _Box<RawPoint>::width() const BP2D_NOEXCEPT
|
||||||
{
|
{
|
||||||
return PointLike::x(maxCorner()) - PointLike::x(minCorner());
|
return pointlike::x(maxCorner()) - pointlike::x(minCorner());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawPoint>
|
template<class RawPoint>
|
||||||
TCoord<RawPoint> _Box<RawPoint>::height() const BP2D_NOEXCEPT
|
TCoord<RawPoint> _Box<RawPoint>::height() const BP2D_NOEXCEPT
|
||||||
{
|
{
|
||||||
return PointLike::y(maxCorner()) - PointLike::y(minCorner());
|
return pointlike::y(maxCorner()) - pointlike::y(minCorner());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawPoint>
|
template<class RawPoint>
|
||||||
TCoord<RawPoint> getX(const RawPoint& p) { return PointLike::x<RawPoint>(p); }
|
TCoord<RawPoint> getX(const RawPoint& p) { return pointlike::x<RawPoint>(p); }
|
||||||
|
|
||||||
template<class RawPoint>
|
template<class RawPoint>
|
||||||
TCoord<RawPoint> getY(const RawPoint& p) { return PointLike::y<RawPoint>(p); }
|
TCoord<RawPoint> getY(const RawPoint& p) { return pointlike::y<RawPoint>(p); }
|
||||||
|
|
||||||
template<class RawPoint>
|
template<class RawPoint>
|
||||||
void setX(RawPoint& p, const TCoord<RawPoint>& val)
|
void setX(RawPoint& p, const TCoord<RawPoint>& val)
|
||||||
{
|
{
|
||||||
PointLike::x<RawPoint>(p) = val;
|
pointlike::x<RawPoint>(p) = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawPoint>
|
template<class RawPoint>
|
||||||
void setY(RawPoint& p, const TCoord<RawPoint>& val)
|
void setY(RawPoint& p, const TCoord<RawPoint>& val)
|
||||||
{
|
{
|
||||||
PointLike::y<RawPoint>(p) = val;
|
pointlike::y<RawPoint>(p) = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawPoint>
|
template<class RawPoint>
|
||||||
|
@ -303,7 +315,7 @@ inline Radians _Segment<RawPoint>::angleToXaxis() const
|
||||||
template<class RawPoint>
|
template<class RawPoint>
|
||||||
inline double _Segment<RawPoint>::length()
|
inline double _Segment<RawPoint>::length()
|
||||||
{
|
{
|
||||||
return PointLike::distance(first(), second());
|
return pointlike::distance(first(), second());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawPoint>
|
template<class RawPoint>
|
||||||
|
@ -356,124 +368,124 @@ enum class Formats {
|
||||||
|
|
||||||
// This struct serves as a namespace. The only difference is that it can be
|
// This struct serves as a namespace. The only difference is that it can be
|
||||||
// used in friend declarations and can be aliased at class scope.
|
// used in friend declarations and can be aliased at class scope.
|
||||||
struct ShapeLike {
|
namespace shapelike {
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
using Shapes = std::vector<RawShape>;
|
using Shapes = std::vector<RawShape>;
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static RawShape create(const TContour<RawShape>& contour,
|
inline RawShape create(const TContour<RawShape>& contour,
|
||||||
const THolesContainer<RawShape>& holes)
|
const THolesContainer<RawShape>& holes)
|
||||||
{
|
{
|
||||||
return RawShape(contour, holes);
|
return RawShape(contour, holes);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static RawShape create(TContour<RawShape>&& contour,
|
inline RawShape create(TContour<RawShape>&& contour,
|
||||||
THolesContainer<RawShape>&& holes)
|
THolesContainer<RawShape>&& holes)
|
||||||
{
|
{
|
||||||
return RawShape(contour, holes);
|
return RawShape(contour, holes);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static RawShape create(const TContour<RawShape>& contour)
|
inline RawShape create(const TContour<RawShape>& contour)
|
||||||
{
|
{
|
||||||
return create<RawShape>(contour, {});
|
return create<RawShape>(contour, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static RawShape create(TContour<RawShape>&& contour)
|
inline RawShape create(TContour<RawShape>&& contour)
|
||||||
{
|
{
|
||||||
return create<RawShape>(contour, {});
|
return create<RawShape>(contour, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static THolesContainer<RawShape>& holes(RawShape& /*sh*/)
|
inline THolesContainer<RawShape>& holes(RawShape& /*sh*/)
|
||||||
{
|
{
|
||||||
static THolesContainer<RawShape> empty;
|
static THolesContainer<RawShape> empty;
|
||||||
return empty;
|
return empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static const THolesContainer<RawShape>& holes(const RawShape& /*sh*/)
|
inline const THolesContainer<RawShape>& holes(const RawShape& /*sh*/)
|
||||||
{
|
{
|
||||||
static THolesContainer<RawShape> empty;
|
static THolesContainer<RawShape> empty;
|
||||||
return empty;
|
return empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static TContour<RawShape>& getHole(RawShape& sh, unsigned long idx)
|
inline TContour<RawShape>& getHole(RawShape& sh, unsigned long idx)
|
||||||
{
|
{
|
||||||
return holes(sh)[idx];
|
return holes(sh)[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static const TContour<RawShape>& getHole(const RawShape& sh,
|
inline const TContour<RawShape>& getHole(const RawShape& sh,
|
||||||
unsigned long idx)
|
unsigned long idx)
|
||||||
{
|
{
|
||||||
return holes(sh)[idx];
|
return holes(sh)[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static size_t holeCount(const RawShape& sh)
|
inline size_t holeCount(const RawShape& sh)
|
||||||
{
|
{
|
||||||
return holes(sh).size();
|
return holes(sh).size();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static TContour<RawShape>& getContour(RawShape& sh)
|
inline TContour<RawShape>& getContour(RawShape& sh)
|
||||||
{
|
{
|
||||||
return sh;
|
return sh;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static const TContour<RawShape>& getContour(const RawShape& sh)
|
inline const TContour<RawShape>& getContour(const RawShape& sh)
|
||||||
{
|
{
|
||||||
return sh;
|
return sh;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Optional, does nothing by default
|
// Optional, does nothing by default
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static void reserve(RawShape& /*sh*/, size_t /*vertex_capacity*/) {}
|
inline void reserve(RawShape& /*sh*/, size_t /*vertex_capacity*/) {}
|
||||||
|
|
||||||
template<class RawShape, class...Args>
|
template<class RawShape, class...Args>
|
||||||
static void addVertex(RawShape& sh, Args...args)
|
inline void addVertex(RawShape& sh, Args...args)
|
||||||
{
|
{
|
||||||
return getContour(sh).emplace_back(std::forward<Args>(args)...);
|
return getContour(sh).emplace_back(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static TVertexIterator<RawShape> begin(RawShape& sh)
|
inline TVertexIterator<RawShape> begin(RawShape& sh)
|
||||||
{
|
{
|
||||||
return sh.begin();
|
return sh.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static TVertexIterator<RawShape> end(RawShape& sh)
|
inline TVertexIterator<RawShape> end(RawShape& sh)
|
||||||
{
|
{
|
||||||
return sh.end();
|
return sh.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static TVertexConstIterator<RawShape> cbegin(const RawShape& sh)
|
inline TVertexConstIterator<RawShape> cbegin(const RawShape& sh)
|
||||||
{
|
{
|
||||||
return sh.cbegin();
|
return sh.cbegin();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static TVertexConstIterator<RawShape> cend(const RawShape& sh)
|
inline TVertexConstIterator<RawShape> cend(const RawShape& sh)
|
||||||
{
|
{
|
||||||
return sh.cend();
|
return sh.cend();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static std::string toString(const RawShape& /*sh*/)
|
inline std::string toString(const RawShape& /*sh*/)
|
||||||
{
|
{
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Formats, class RawShape>
|
template<Formats, class RawShape>
|
||||||
static std::string serialize(const RawShape& /*sh*/, double /*scale*/=1)
|
inline std::string serialize(const RawShape& /*sh*/, double /*scale*/=1)
|
||||||
{
|
{
|
||||||
static_assert(always_false<RawShape>::value,
|
static_assert(always_false<RawShape>::value,
|
||||||
"ShapeLike::serialize() unimplemented!");
|
"ShapeLike::serialize() unimplemented!");
|
||||||
|
@ -481,14 +493,14 @@ struct ShapeLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<Formats, class RawShape>
|
template<Formats, class RawShape>
|
||||||
static void unserialize(RawShape& /*sh*/, const std::string& /*str*/)
|
inline void unserialize(RawShape& /*sh*/, const std::string& /*str*/)
|
||||||
{
|
{
|
||||||
static_assert(always_false<RawShape>::value,
|
static_assert(always_false<RawShape>::value,
|
||||||
"ShapeLike::unserialize() unimplemented!");
|
"ShapeLike::unserialize() unimplemented!");
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static double area(const RawShape& /*sh*/)
|
inline double area(const RawShape& /*sh*/, const PolygonTag&)
|
||||||
{
|
{
|
||||||
static_assert(always_false<RawShape>::value,
|
static_assert(always_false<RawShape>::value,
|
||||||
"ShapeLike::area() unimplemented!");
|
"ShapeLike::area() unimplemented!");
|
||||||
|
@ -496,7 +508,7 @@ struct ShapeLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static bool intersects(const RawShape& /*sh*/, const RawShape& /*sh*/)
|
inline bool intersects(const RawShape& /*sh*/, const RawShape& /*sh*/)
|
||||||
{
|
{
|
||||||
static_assert(always_false<RawShape>::value,
|
static_assert(always_false<RawShape>::value,
|
||||||
"ShapeLike::intersects() unimplemented!");
|
"ShapeLike::intersects() unimplemented!");
|
||||||
|
@ -504,7 +516,7 @@ struct ShapeLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static bool isInside(const TPoint<RawShape>& /*point*/,
|
inline bool isInside(const TPoint<RawShape>& /*point*/,
|
||||||
const RawShape& /*shape*/)
|
const RawShape& /*shape*/)
|
||||||
{
|
{
|
||||||
static_assert(always_false<RawShape>::value,
|
static_assert(always_false<RawShape>::value,
|
||||||
|
@ -513,7 +525,7 @@ struct ShapeLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static bool isInside(const RawShape& /*shape*/,
|
inline bool isInside(const RawShape& /*shape*/,
|
||||||
const RawShape& /*shape*/)
|
const RawShape& /*shape*/)
|
||||||
{
|
{
|
||||||
static_assert(always_false<RawShape>::value,
|
static_assert(always_false<RawShape>::value,
|
||||||
|
@ -522,7 +534,7 @@ struct ShapeLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static bool touches( const RawShape& /*shape*/,
|
inline bool touches( const RawShape& /*shape*/,
|
||||||
const RawShape& /*shape*/)
|
const RawShape& /*shape*/)
|
||||||
{
|
{
|
||||||
static_assert(always_false<RawShape>::value,
|
static_assert(always_false<RawShape>::value,
|
||||||
|
@ -531,7 +543,7 @@ struct ShapeLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static bool touches( const TPoint<RawShape>& /*point*/,
|
inline bool touches( const TPoint<RawShape>& /*point*/,
|
||||||
const RawShape& /*shape*/)
|
const RawShape& /*shape*/)
|
||||||
{
|
{
|
||||||
static_assert(always_false<RawShape>::value,
|
static_assert(always_false<RawShape>::value,
|
||||||
|
@ -540,21 +552,22 @@ struct ShapeLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static _Box<TPoint<RawShape>> boundingBox(const RawShape& /*sh*/)
|
inline _Box<TPoint<RawShape>> boundingBox(const RawShape& /*sh*/,
|
||||||
|
const PolygonTag&)
|
||||||
{
|
{
|
||||||
static_assert(always_false<RawShape>::value,
|
static_assert(always_false<RawShape>::value,
|
||||||
"ShapeLike::boundingBox(shape) unimplemented!");
|
"ShapeLike::boundingBox(shape) unimplemented!");
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static _Box<TPoint<RawShape>> boundingBox(const Shapes<RawShape>& /*sh*/)
|
inline _Box<TPoint<RawShape>> boundingBox(const Shapes<RawShape>& /*sh*/)
|
||||||
{
|
{
|
||||||
static_assert(always_false<RawShape>::value,
|
static_assert(always_false<RawShape>::value,
|
||||||
"ShapeLike::boundingBox(shapes) unimplemented!");
|
"ShapeLike::boundingBox(shapes) unimplemented!");
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static RawShape convexHull(const RawShape& /*sh*/)
|
inline RawShape convexHull(const RawShape& /*sh*/)
|
||||||
{
|
{
|
||||||
static_assert(always_false<RawShape>::value,
|
static_assert(always_false<RawShape>::value,
|
||||||
"ShapeLike::convexHull(shape) unimplemented!");
|
"ShapeLike::convexHull(shape) unimplemented!");
|
||||||
|
@ -562,7 +575,7 @@ struct ShapeLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static RawShape convexHull(const Shapes<RawShape>& /*sh*/)
|
inline RawShape convexHull(const Shapes<RawShape>& /*sh*/)
|
||||||
{
|
{
|
||||||
static_assert(always_false<RawShape>::value,
|
static_assert(always_false<RawShape>::value,
|
||||||
"ShapeLike::convexHull(shapes) unimplemented!");
|
"ShapeLike::convexHull(shapes) unimplemented!");
|
||||||
|
@ -570,34 +583,34 @@ struct ShapeLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static void rotate(RawShape& /*sh*/, const Radians& /*rads*/)
|
inline void rotate(RawShape& /*sh*/, const Radians& /*rads*/)
|
||||||
{
|
{
|
||||||
static_assert(always_false<RawShape>::value,
|
static_assert(always_false<RawShape>::value,
|
||||||
"ShapeLike::rotate() unimplemented!");
|
"ShapeLike::rotate() unimplemented!");
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape, class RawPoint>
|
template<class RawShape, class RawPoint>
|
||||||
static void translate(RawShape& /*sh*/, const RawPoint& /*offs*/)
|
inline void translate(RawShape& /*sh*/, const RawPoint& /*offs*/)
|
||||||
{
|
{
|
||||||
static_assert(always_false<RawShape>::value,
|
static_assert(always_false<RawShape>::value,
|
||||||
"ShapeLike::translate() unimplemented!");
|
"ShapeLike::translate() unimplemented!");
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static void offset(RawShape& /*sh*/, TCoord<TPoint<RawShape>> /*distance*/)
|
inline void offset(RawShape& /*sh*/, TCoord<TPoint<RawShape>> /*distance*/)
|
||||||
{
|
{
|
||||||
static_assert(always_false<RawShape>::value,
|
static_assert(always_false<RawShape>::value,
|
||||||
"ShapeLike::offset() unimplemented!");
|
"ShapeLike::offset() unimplemented!");
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static std::pair<bool, std::string> isValid(const RawShape& /*sh*/)
|
inline std::pair<bool, std::string> isValid(const RawShape& /*sh*/)
|
||||||
{
|
{
|
||||||
return {false, "ShapeLike::isValid() unimplemented!"};
|
return {false, "ShapeLike::isValid() unimplemented!"};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static inline bool isConvex(const TContour<RawShape>& sh)
|
inline bool isConvex(const TContour<RawShape>& sh)
|
||||||
{
|
{
|
||||||
using Vertex = TPoint<RawShape>;
|
using Vertex = TPoint<RawShape>;
|
||||||
auto first = sh.begin();
|
auto first = sh.begin();
|
||||||
|
@ -633,43 +646,55 @@ struct ShapeLike {
|
||||||
// No need to implement these
|
// No need to implement these
|
||||||
// *************************************************************************
|
// *************************************************************************
|
||||||
|
|
||||||
template<class RawShape>
|
template<class Box>
|
||||||
static inline _Box<TPoint<RawShape>> boundingBox(
|
inline Box boundingBox(const Box& box, const BoxTag& )
|
||||||
const _Box<TPoint<RawShape>>& box)
|
|
||||||
{
|
{
|
||||||
return box;
|
return box;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class Circle>
|
||||||
static inline _Box<TPoint<RawShape>> boundingBox(
|
inline _Box<typename Circle::PointType> boundingBox(
|
||||||
const _Circle<TPoint<RawShape>>& circ)
|
const Circle& circ, const CircleTag&)
|
||||||
{
|
{
|
||||||
using Coord = TCoord<TPoint<RawShape>>;
|
using Point = typename Circle::PointType;
|
||||||
TPoint<RawShape> pmin = {
|
using Coord = TCoord<Point>;
|
||||||
|
Point pmin = {
|
||||||
static_cast<Coord>(getX(circ.center()) - circ.radius()),
|
static_cast<Coord>(getX(circ.center()) - circ.radius()),
|
||||||
static_cast<Coord>(getY(circ.center()) - circ.radius()) };
|
static_cast<Coord>(getY(circ.center()) - circ.radius()) };
|
||||||
|
|
||||||
TPoint<RawShape> pmax = {
|
Point pmax = {
|
||||||
static_cast<Coord>(getX(circ.center()) + circ.radius()),
|
static_cast<Coord>(getX(circ.center()) + circ.radius()),
|
||||||
static_cast<Coord>(getY(circ.center()) + circ.radius()) };
|
static_cast<Coord>(getY(circ.center()) + circ.radius()) };
|
||||||
|
|
||||||
return {pmin, pmax};
|
return {pmin, pmax};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class S> // Dispatch function
|
||||||
static inline double area(const _Box<TPoint<RawShape>>& box)
|
inline _Box<typename S::PointType> boundingBox(const S& sh)
|
||||||
{
|
{
|
||||||
return static_cast<double>(box.width() * box.height());
|
return boundingBox(sh, typename S::Tag());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class Box>
|
||||||
static inline double area(const _Circle<TPoint<RawShape>>& circ)
|
inline double area(const Box& box, const BoxTag& )
|
||||||
|
{
|
||||||
|
return box.area();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Circle>
|
||||||
|
inline double area(const Circle& circ, const CircleTag& )
|
||||||
{
|
{
|
||||||
return circ.area();
|
return circ.area();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class RawShape> // Dispatching function
|
||||||
|
inline double area(const RawShape& sh)
|
||||||
|
{
|
||||||
|
return area(sh, typename RawShape::Tag());
|
||||||
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static inline double area(const Shapes<RawShape>& shapes)
|
inline double area(const Shapes<RawShape>& shapes)
|
||||||
{
|
{
|
||||||
return std::accumulate(shapes.begin(), shapes.end(), 0.0,
|
return std::accumulate(shapes.begin(), shapes.end(), 0.0,
|
||||||
[](double a, const RawShape& b) {
|
[](double a, const RawShape& b) {
|
||||||
|
@ -678,14 +703,14 @@ struct ShapeLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static bool isInside(const TPoint<RawShape>& point,
|
inline bool isInside(const TPoint<RawShape>& point,
|
||||||
const _Circle<TPoint<RawShape>>& circ)
|
const _Circle<TPoint<RawShape>>& circ)
|
||||||
{
|
{
|
||||||
return PointLike::distance(point, circ.center()) < circ.radius();
|
return pointlike::distance(point, circ.center()) < circ.radius();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static bool isInside(const TPoint<RawShape>& point,
|
inline bool isInside(const TPoint<RawShape>& point,
|
||||||
const _Box<TPoint<RawShape>>& box)
|
const _Box<TPoint<RawShape>>& box)
|
||||||
{
|
{
|
||||||
auto px = getX(point);
|
auto px = getX(point);
|
||||||
|
@ -699,7 +724,7 @@ struct ShapeLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static bool isInside(const RawShape& sh,
|
inline bool isInside(const RawShape& sh,
|
||||||
const _Circle<TPoint<RawShape>>& circ)
|
const _Circle<TPoint<RawShape>>& circ)
|
||||||
{
|
{
|
||||||
return std::all_of(cbegin(sh), cend(sh),
|
return std::all_of(cbegin(sh), cend(sh),
|
||||||
|
@ -709,7 +734,7 @@ struct ShapeLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static bool isInside(const _Box<TPoint<RawShape>>& box,
|
inline bool isInside(const _Box<TPoint<RawShape>>& box,
|
||||||
const _Circle<TPoint<RawShape>>& circ)
|
const _Circle<TPoint<RawShape>>& circ)
|
||||||
{
|
{
|
||||||
return isInside<RawShape>(box.minCorner(), circ) &&
|
return isInside<RawShape>(box.minCorner(), circ) &&
|
||||||
|
@ -717,7 +742,7 @@ struct ShapeLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static bool isInside(const _Box<TPoint<RawShape>>& ibb,
|
inline bool isInside(const _Box<TPoint<RawShape>>& ibb,
|
||||||
const _Box<TPoint<RawShape>>& box)
|
const _Box<TPoint<RawShape>>& box)
|
||||||
{
|
{
|
||||||
auto iminX = getX(ibb.minCorner());
|
auto iminX = getX(ibb.minCorner());
|
||||||
|
@ -734,31 +759,31 @@ struct ShapeLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape> // Potential O(1) implementation may exist
|
template<class RawShape> // Potential O(1) implementation may exist
|
||||||
static inline TPoint<RawShape>& vertex(RawShape& sh, unsigned long idx)
|
inline TPoint<RawShape>& vertex(RawShape& sh, unsigned long idx)
|
||||||
{
|
{
|
||||||
return *(begin(sh) + idx);
|
return *(begin(sh) + idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape> // Potential O(1) implementation may exist
|
template<class RawShape> // Potential O(1) implementation may exist
|
||||||
static inline const TPoint<RawShape>& vertex(const RawShape& sh,
|
inline const TPoint<RawShape>& vertex(const RawShape& sh,
|
||||||
unsigned long idx)
|
unsigned long idx)
|
||||||
{
|
{
|
||||||
return *(cbegin(sh) + idx);
|
return *(cbegin(sh) + idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static inline size_t contourVertexCount(const RawShape& sh)
|
inline size_t contourVertexCount(const RawShape& sh)
|
||||||
{
|
{
|
||||||
return cend(sh) - cbegin(sh);
|
return cend(sh) - cbegin(sh);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape, class Fn>
|
template<class RawShape, class Fn>
|
||||||
static inline void foreachContourVertex(RawShape& sh, Fn fn) {
|
inline void foreachContourVertex(RawShape& sh, Fn fn) {
|
||||||
for(auto it = begin(sh); it != end(sh); ++it) fn(*it);
|
for(auto it = begin(sh); it != end(sh); ++it) fn(*it);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape, class Fn>
|
template<class RawShape, class Fn>
|
||||||
static inline void foreachHoleVertex(RawShape& sh, Fn fn) {
|
inline void foreachHoleVertex(RawShape& sh, Fn fn) {
|
||||||
for(int i = 0; i < holeCount(sh); ++i) {
|
for(int i = 0; i < holeCount(sh); ++i) {
|
||||||
auto& h = getHole(sh, i);
|
auto& h = getHole(sh, i);
|
||||||
for(auto it = begin(h); it != end(h); ++it) fn(*it);
|
for(auto it = begin(h); it != end(h); ++it) fn(*it);
|
||||||
|
@ -766,12 +791,12 @@ struct ShapeLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape, class Fn>
|
template<class RawShape, class Fn>
|
||||||
static inline void foreachContourVertex(const RawShape& sh, Fn fn) {
|
inline void foreachContourVertex(const RawShape& sh, Fn fn) {
|
||||||
for(auto it = cbegin(sh); it != cend(sh); ++it) fn(*it);
|
for(auto it = cbegin(sh); it != cend(sh); ++it) fn(*it);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape, class Fn>
|
template<class RawShape, class Fn>
|
||||||
static inline void foreachHoleVertex(const RawShape& sh, Fn fn) {
|
inline void foreachHoleVertex(const RawShape& sh, Fn fn) {
|
||||||
for(int i = 0; i < holeCount(sh); ++i) {
|
for(int i = 0; i < holeCount(sh); ++i) {
|
||||||
auto& h = getHole(sh, i);
|
auto& h = getHole(sh, i);
|
||||||
for(auto it = cbegin(h); it != cend(h); ++it) fn(*it);
|
for(auto it = cbegin(h); it != cend(h); ++it) fn(*it);
|
||||||
|
@ -779,18 +804,17 @@ struct ShapeLike {
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape, class Fn>
|
template<class RawShape, class Fn>
|
||||||
static inline void foreachVertex(RawShape& sh, Fn fn) {
|
inline void foreachVertex(RawShape& sh, Fn fn) {
|
||||||
foreachContourVertex(sh, fn);
|
foreachContourVertex(sh, fn);
|
||||||
foreachHoleVertex(sh, fn);
|
foreachHoleVertex(sh, fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape, class Fn>
|
template<class RawShape, class Fn>
|
||||||
static inline void foreachVertex(const RawShape& sh, Fn fn) {
|
inline void foreachVertex(const RawShape& sh, Fn fn) {
|
||||||
foreachContourVertex(sh, fn);
|
foreachContourVertex(sh, fn);
|
||||||
foreachHoleVertex(sh, fn);
|
foreachHoleVertex(sh, fn);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,27 @@
|
||||||
|
|
||||||
namespace libnest2d {
|
namespace libnest2d {
|
||||||
|
|
||||||
|
namespace __nfp {
|
||||||
|
// Do not specialize this...
|
||||||
|
template<class RawShape>
|
||||||
|
inline bool _vsort(const TPoint<RawShape>& v1, const TPoint<RawShape>& v2)
|
||||||
|
{
|
||||||
|
using Coord = TCoord<TPoint<RawShape>>;
|
||||||
|
Coord &&x1 = getX(v1), &&x2 = getX(v2), &&y1 = getY(v1), &&y2 = getY(v2);
|
||||||
|
auto diff = y1 - y2;
|
||||||
|
if(std::abs(diff) <= std::numeric_limits<Coord>::epsilon())
|
||||||
|
return x1 < x2;
|
||||||
|
|
||||||
|
return diff < 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A collection of static methods for handling the no fit polygon creation.
|
||||||
|
namespace nfp {
|
||||||
|
|
||||||
|
namespace sl = shapelike;
|
||||||
|
namespace pl = pointlike;
|
||||||
|
|
||||||
/// The complexity level of a polygon that an NFP implementation can handle.
|
/// The complexity level of a polygon that an NFP implementation can handle.
|
||||||
enum class NfpLevel: unsigned {
|
enum class NfpLevel: unsigned {
|
||||||
CONVEX_ONLY,
|
CONVEX_ONLY,
|
||||||
|
@ -18,12 +39,17 @@ enum class NfpLevel: unsigned {
|
||||||
BOTH_CONCAVE_WITH_HOLES
|
BOTH_CONCAVE_WITH_HOLES
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A collection of static methods for handling the no fit polygon creation.
|
template<class RawShape>
|
||||||
struct Nfp {
|
using NfpResult = std::pair<RawShape, TPoint<RawShape>>;
|
||||||
|
|
||||||
|
template<class RawShape> struct MaxNfpLevel {
|
||||||
|
static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Shorthand for a pile of polygons
|
// Shorthand for a pile of polygons
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
using Shapes = typename ShapeLike::Shapes<RawShape>;
|
using Shapes = typename shapelike::Shapes<RawShape>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merge a bunch of polygons with the specified additional polygon.
|
* Merge a bunch of polygons with the specified additional polygon.
|
||||||
|
@ -37,7 +63,7 @@ using Shapes = typename ShapeLike::Shapes<RawShape>;
|
||||||
* polygons are disjuct than the resulting set will contain more polygons.
|
* polygons are disjuct than the resulting set will contain more polygons.
|
||||||
*/
|
*/
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static Shapes<RawShape> merge(const Shapes<RawShape>& /*shc*/)
|
inline Shapes<RawShape> merge(const Shapes<RawShape>& /*shc*/)
|
||||||
{
|
{
|
||||||
static_assert(always_false<RawShape>::value,
|
static_assert(always_false<RawShape>::value,
|
||||||
"Nfp::merge(shapes, shape) unimplemented!");
|
"Nfp::merge(shapes, shape) unimplemented!");
|
||||||
|
@ -55,7 +81,7 @@ static Shapes<RawShape> merge(const Shapes<RawShape>& /*shc*/)
|
||||||
* polygons are disjuct than the resulting set will contain more polygons.
|
* polygons are disjuct than the resulting set will contain more polygons.
|
||||||
*/
|
*/
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static Shapes<RawShape> merge(const Shapes<RawShape>& shc,
|
inline Shapes<RawShape> merge(const Shapes<RawShape>& shc,
|
||||||
const RawShape& sh)
|
const RawShape& sh)
|
||||||
{
|
{
|
||||||
auto m = merge(shc);
|
auto m = merge(shc);
|
||||||
|
@ -63,31 +89,18 @@ static Shapes<RawShape> merge(const Shapes<RawShape>& shc,
|
||||||
return merge(m);
|
return merge(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A method to get a vertex from a polygon that always maintains a relative
|
|
||||||
* position to the coordinate system: It is always the rightmost top vertex.
|
|
||||||
*
|
|
||||||
* This way it does not matter in what order the vertices are stored, the
|
|
||||||
* reference will be always the same for the same polygon.
|
|
||||||
*/
|
|
||||||
template<class RawShape>
|
|
||||||
inline static TPoint<RawShape> referenceVertex(const RawShape& sh)
|
|
||||||
{
|
|
||||||
return rightmostUpVertex(sh);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the vertex of the polygon that is at the lowest values (bottom) in the Y
|
* Get the vertex of the polygon that is at the lowest values (bottom) in the Y
|
||||||
* axis and if there are more than one vertices on the same Y coordinate than
|
* axis and if there are more than one vertices on the same Y coordinate than
|
||||||
* the result will be the leftmost (with the highest X coordinate).
|
* the result will be the leftmost (with the highest X coordinate).
|
||||||
*/
|
*/
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static TPoint<RawShape> leftmostDownVertex(const RawShape& sh)
|
inline TPoint<RawShape> leftmostDownVertex(const RawShape& sh)
|
||||||
{
|
{
|
||||||
|
|
||||||
// find min x and min y vertex
|
// find min x and min y vertex
|
||||||
auto it = std::min_element(ShapeLike::cbegin(sh), ShapeLike::cend(sh),
|
auto it = std::min_element(shapelike::cbegin(sh), shapelike::cend(sh),
|
||||||
_vsort<RawShape>);
|
__nfp::_vsort<RawShape>);
|
||||||
|
|
||||||
return *it;
|
return *it;
|
||||||
}
|
}
|
||||||
|
@ -98,26 +111,27 @@ static TPoint<RawShape> leftmostDownVertex(const RawShape& sh)
|
||||||
* the result will be the rightmost (with the lowest X coordinate).
|
* the result will be the rightmost (with the lowest X coordinate).
|
||||||
*/
|
*/
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static TPoint<RawShape> rightmostUpVertex(const RawShape& sh)
|
TPoint<RawShape> rightmostUpVertex(const RawShape& sh)
|
||||||
{
|
{
|
||||||
|
|
||||||
// find max x and max y vertex
|
// find max x and max y vertex
|
||||||
auto it = std::max_element(ShapeLike::cbegin(sh), ShapeLike::cend(sh),
|
auto it = std::max_element(shapelike::cbegin(sh), shapelike::cend(sh),
|
||||||
_vsort<RawShape>);
|
__nfp::_vsort<RawShape>);
|
||||||
|
|
||||||
return *it;
|
return *it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A method to get a vertex from a polygon that always maintains a relative
|
||||||
|
* position to the coordinate system: It is always the rightmost top vertex.
|
||||||
|
*
|
||||||
|
* This way it does not matter in what order the vertices are stored, the
|
||||||
|
* reference will be always the same for the same polygon.
|
||||||
|
*/
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
using NfpResult = std::pair<RawShape, TPoint<RawShape>>;
|
inline TPoint<RawShape> referenceVertex(const RawShape& sh)
|
||||||
|
|
||||||
/// Helper function to get the NFP
|
|
||||||
template<NfpLevel nfptype, class RawShape>
|
|
||||||
static NfpResult<RawShape> noFitPolygon(const RawShape& sh,
|
|
||||||
const RawShape& other)
|
|
||||||
{
|
{
|
||||||
NfpImpl<RawShape, nfptype> nfp;
|
return rightmostUpVertex(sh);
|
||||||
return nfp(sh, other);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -139,11 +153,11 @@ static NfpResult<RawShape> noFitPolygon(const RawShape& sh,
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
|
inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
|
||||||
const RawShape& other)
|
const RawShape& other)
|
||||||
{
|
{
|
||||||
using Vertex = TPoint<RawShape>; using Edge = _Segment<Vertex>;
|
using Vertex = TPoint<RawShape>; using Edge = _Segment<Vertex>;
|
||||||
using sl = ShapeLike;
|
namespace sl = shapelike;
|
||||||
|
|
||||||
RawShape rsh; // Final nfp placeholder
|
RawShape rsh; // Final nfp placeholder
|
||||||
Vertex top_nfp;
|
Vertex top_nfp;
|
||||||
|
@ -187,7 +201,7 @@ static NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
|
||||||
sl::addVertex(rsh, edgelist.front().second());
|
sl::addVertex(rsh, edgelist.front().second());
|
||||||
|
|
||||||
// Sorting function for the nfp reference vertex search
|
// Sorting function for the nfp reference vertex search
|
||||||
auto& cmp = _vsort<RawShape>;
|
auto& cmp = __nfp::_vsort<RawShape>;
|
||||||
|
|
||||||
// the reference (rightmost top) vertex so far
|
// the reference (rightmost top) vertex so far
|
||||||
top_nfp = *std::max_element(sl::cbegin(rsh), sl::cend(rsh), cmp );
|
top_nfp = *std::max_element(sl::cbegin(rsh), sl::cend(rsh), cmp );
|
||||||
|
@ -214,7 +228,7 @@ static NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
static NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
|
NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
|
||||||
const RawShape& cother)
|
const RawShape& cother)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -233,7 +247,7 @@ static NfpResult<RawShape> nfpSimpleSimple(const RawShape& cstationary,
|
||||||
using Vertex = TPoint<RawShape>;
|
using Vertex = TPoint<RawShape>;
|
||||||
using Coord = TCoord<Vertex>;
|
using Coord = TCoord<Vertex>;
|
||||||
using Edge = _Segment<Vertex>;
|
using Edge = _Segment<Vertex>;
|
||||||
using sl = ShapeLike;
|
namespace sl = shapelike;
|
||||||
using std::signbit;
|
using std::signbit;
|
||||||
using std::sort;
|
using std::sort;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
|
@ -528,27 +542,16 @@ struct NfpImpl {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class RawShape> struct MaxNfpLevel {
|
/// Helper function to get the NFP
|
||||||
static const BP2D_CONSTEXPR NfpLevel value = NfpLevel::CONVEX_ONLY;
|
template<NfpLevel nfptype, class RawShape>
|
||||||
};
|
inline NfpResult<RawShape> noFitPolygon(const RawShape& sh,
|
||||||
|
const RawShape& other)
|
||||||
private:
|
|
||||||
|
|
||||||
// Do not specialize this...
|
|
||||||
template<class RawShape>
|
|
||||||
static inline bool _vsort(const TPoint<RawShape>& v1,
|
|
||||||
const TPoint<RawShape>& v2)
|
|
||||||
{
|
{
|
||||||
using Coord = TCoord<TPoint<RawShape>>;
|
NfpImpl<RawShape, nfptype> nfps;
|
||||||
Coord &&x1 = getX(v1), &&x2 = getX(v2), &&y1 = getY(v1), &&y2 = getY(v2);
|
return nfps(sh, other);
|
||||||
auto diff = y1 - y2;
|
|
||||||
if(std::abs(diff) <= std::numeric_limits<Coord>::epsilon())
|
|
||||||
return x1 < x2;
|
|
||||||
|
|
||||||
return diff < 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,10 +9,12 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include "geometry_traits.hpp"
|
#include "geometry_traits.hpp"
|
||||||
#include "optimizer.hpp"
|
|
||||||
|
|
||||||
namespace libnest2d {
|
namespace libnest2d {
|
||||||
|
|
||||||
|
namespace sl = shapelike;
|
||||||
|
namespace pl = pointlike;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief An item to be placed on a bin.
|
* \brief An item to be placed on a bin.
|
||||||
*
|
*
|
||||||
|
@ -28,7 +30,6 @@ class _Item {
|
||||||
using Coord = TCoord<TPoint<RawShape>>;
|
using Coord = TCoord<TPoint<RawShape>>;
|
||||||
using Vertex = TPoint<RawShape>;
|
using Vertex = TPoint<RawShape>;
|
||||||
using Box = _Box<Vertex>;
|
using Box = _Box<Vertex>;
|
||||||
using sl = ShapeLike;
|
|
||||||
|
|
||||||
// The original shape that gets encapsulated.
|
// The original shape that gets encapsulated.
|
||||||
RawShape sh_;
|
RawShape sh_;
|
||||||
|
@ -438,7 +439,7 @@ public:
|
||||||
inline _Rectangle(Unit width, Unit height,
|
inline _Rectangle(Unit width, Unit height,
|
||||||
// disable this ctor if o != CLOCKWISE
|
// disable this ctor if o != CLOCKWISE
|
||||||
enable_if_t< o == TO::CLOCKWISE, int> = 0 ):
|
enable_if_t< o == TO::CLOCKWISE, int> = 0 ):
|
||||||
_Item<RawShape>( ShapeLike::create<RawShape>( {
|
_Item<RawShape>( sl::create<RawShape>( {
|
||||||
{0, 0},
|
{0, 0},
|
||||||
{0, height},
|
{0, height},
|
||||||
{width, height},
|
{width, height},
|
||||||
|
@ -452,7 +453,7 @@ public:
|
||||||
inline _Rectangle(Unit width, Unit height,
|
inline _Rectangle(Unit width, Unit height,
|
||||||
// disable this ctor if o != COUNTER_CLOCKWISE
|
// disable this ctor if o != COUNTER_CLOCKWISE
|
||||||
enable_if_t< o == TO::COUNTER_CLOCKWISE, int> = 0 ):
|
enable_if_t< o == TO::COUNTER_CLOCKWISE, int> = 0 ):
|
||||||
_Item<RawShape>( ShapeLike::create<RawShape>( {
|
_Item<RawShape>( sl::create<RawShape>( {
|
||||||
{0, 0},
|
{0, 0},
|
||||||
{width, 0},
|
{width, 0},
|
||||||
{width, height},
|
{width, height},
|
||||||
|
@ -473,12 +474,32 @@ public:
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
inline bool _Item<RawShape>::isInside(const _Box<TPoint<RawShape>>& box) const {
|
inline bool _Item<RawShape>::isInside(const _Box<TPoint<RawShape>>& box) const {
|
||||||
return ShapeLike::isInside<RawShape>(boundingBox(), box);
|
return sl::isInside<RawShape>(boundingBox(), box);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape> inline bool
|
template<class RawShape> inline bool
|
||||||
_Item<RawShape>::isInside(const _Circle<TPoint<RawShape>>& circ) const {
|
_Item<RawShape>::isInside(const _Circle<TPoint<RawShape>>& circ) const {
|
||||||
return ShapeLike::isInside<RawShape>(transformedShape(), circ);
|
return sl::isInside<RawShape>(transformedShape(), circ);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template<class I> using _ItemRef = std::reference_wrapper<I>;
|
||||||
|
template<class I> using _ItemGroup = std::vector<_ItemRef<I>>;
|
||||||
|
|
||||||
|
template<class Iterator>
|
||||||
|
struct ConstItemRange {
|
||||||
|
Iterator from;
|
||||||
|
Iterator to;
|
||||||
|
bool valid = false;
|
||||||
|
|
||||||
|
ConstItemRange() = default;
|
||||||
|
ConstItemRange(Iterator f, Iterator t): from(f), to(t), valid(true) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class Container>
|
||||||
|
inline ConstItemRange<typename Container::const_iterator>
|
||||||
|
rem(typename Container::const_iterator it, const Container& cont) {
|
||||||
|
return {std::next(it), cont.end()};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -515,8 +536,9 @@ public:
|
||||||
*/
|
*/
|
||||||
using PackResult = typename PlacementStrategy::PackResult;
|
using PackResult = typename PlacementStrategy::PackResult;
|
||||||
|
|
||||||
using ItemRef = std::reference_wrapper<Item>;
|
using ItemRef = _ItemRef<Item>;
|
||||||
using ItemGroup = std::vector<ItemRef>;
|
using ItemGroup = _ItemGroup<Item>;
|
||||||
|
using DefaultIterator = typename ItemGroup::const_iterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Constructor taking the bin and an optional configuration.
|
* @brief Constructor taking the bin and an optional configuration.
|
||||||
|
@ -544,20 +566,24 @@ public:
|
||||||
* Try to pack an item with a result object that contains the packing
|
* Try to pack an item with a result object that contains the packing
|
||||||
* information for later accepting it.
|
* information for later accepting it.
|
||||||
*
|
*
|
||||||
* \param item_store A container of items
|
* \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.
|
||||||
|
* \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
|
||||||
|
* just the item pointed to by "from" argument should be packed.
|
||||||
*/
|
*/
|
||||||
template<class Container>
|
template<class Iter = DefaultIterator>
|
||||||
inline PackResult trypack(Container& item_store,
|
inline PackResult trypack(
|
||||||
typename Container::iterator from,
|
Item& item,
|
||||||
unsigned count = 1) {
|
const ConstItemRange<Iter>& remaining = ConstItemRange<Iter>())
|
||||||
using V = typename Container::value_type;
|
{
|
||||||
static_assert(std::is_convertible<V, const Item&>::value,
|
return impl_.trypack(item, remaining);
|
||||||
"Invalid Item container!");
|
|
||||||
return impl_.trypack(item_store, from, count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A method to accept a previously tried item.
|
* @brief A method to accept a previously tried item (or items).
|
||||||
*
|
*
|
||||||
* If the pack result is a failure the method should ignore it.
|
* If the pack result is a failure the method should ignore it.
|
||||||
* @param r The result of a previous trypack call.
|
* @param r The result of a previous trypack call.
|
||||||
|
@ -565,10 +591,10 @@ public:
|
||||||
inline void accept(PackResult& r) { impl_.accept(r); }
|
inline void accept(PackResult& r) { impl_.accept(r); }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief pack Try to pack an item and immediately accept it on success.
|
* @brief pack Try to pack and immediately accept it on success.
|
||||||
*
|
*
|
||||||
* A default implementation would be to call
|
* A default implementation would be to call
|
||||||
* { auto&& r = trypack(item); accept(r); return r; } but we should let the
|
* { auto&& r = trypack(...); accept(r); return r; } but we should let the
|
||||||
* implementor of the placement strategy to harvest any optimizations from
|
* implementor of the placement strategy to harvest any optimizations from
|
||||||
* the absence of an intermadiate step. The above version can still be used
|
* the absence of an intermadiate step. The above version can still be used
|
||||||
* in the implementation.
|
* in the implementation.
|
||||||
|
@ -577,15 +603,12 @@ public:
|
||||||
* @return Returns true if the item was packed or false if it could not be
|
* @return Returns true if the item was packed or false if it could not be
|
||||||
* packed.
|
* packed.
|
||||||
*/
|
*/
|
||||||
template<class Container>
|
template<class Range = ConstItemRange<DefaultIterator>>
|
||||||
inline bool pack(Container& item_store,
|
inline bool pack(
|
||||||
typename Container::iterator from,
|
Item& item,
|
||||||
unsigned count = 1)
|
const Range& remaining = Range())
|
||||||
{
|
{
|
||||||
using V = typename Container::value_type;
|
return impl_.pack(item, remaining);
|
||||||
static_assert(std::is_convertible<V, const Item&>::value,
|
|
||||||
"Invalid Item container!");
|
|
||||||
return impl_.pack(item_store, from, count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unpack the last element (remove it from the list of packed items).
|
/// Unpack the last element (remove it from the list of packed items).
|
||||||
|
@ -736,10 +759,9 @@ using _IndexedPackGroup = std::vector<
|
||||||
* inside the provided bin.
|
* inside the provided bin.
|
||||||
*/
|
*/
|
||||||
template<class PlacementStrategy, class SelectionStrategy >
|
template<class PlacementStrategy, class SelectionStrategy >
|
||||||
class Arranger {
|
class Nester {
|
||||||
using TSel = SelectionStrategyLike<SelectionStrategy>;
|
using TSel = SelectionStrategyLike<SelectionStrategy>;
|
||||||
TSel selector_;
|
TSel selector_;
|
||||||
bool use_min_bb_rotation_ = false;
|
|
||||||
public:
|
public:
|
||||||
using Item = typename PlacementStrategy::Item;
|
using Item = typename PlacementStrategy::Item;
|
||||||
using ItemRef = std::reference_wrapper<Item>;
|
using ItemRef = std::reference_wrapper<Item>;
|
||||||
|
@ -777,7 +799,7 @@ public:
|
||||||
template<class TBinType = BinType,
|
template<class TBinType = BinType,
|
||||||
class PConf = PlacementConfig,
|
class PConf = PlacementConfig,
|
||||||
class SConf = SelectionConfig>
|
class SConf = SelectionConfig>
|
||||||
Arranger( TBinType&& bin,
|
Nester( TBinType&& bin,
|
||||||
Unit min_obj_distance = 0,
|
Unit min_obj_distance = 0,
|
||||||
PConf&& pconfig = PConf(),
|
PConf&& pconfig = PConf(),
|
||||||
SConf&& sconfig = SConf()):
|
SConf&& sconfig = SConf()):
|
||||||
|
@ -810,9 +832,9 @@ public:
|
||||||
* the selection algorithm.
|
* the selection algorithm.
|
||||||
*/
|
*/
|
||||||
template<class TIterator>
|
template<class TIterator>
|
||||||
inline PackGroup arrange(TIterator from, TIterator to)
|
inline PackGroup execute(TIterator from, TIterator to)
|
||||||
{
|
{
|
||||||
return _arrange(from, to);
|
return _execute(from, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -823,20 +845,20 @@ public:
|
||||||
* input sequence size.
|
* input sequence size.
|
||||||
*/
|
*/
|
||||||
template<class TIterator>
|
template<class TIterator>
|
||||||
inline IndexedPackGroup arrangeIndexed(TIterator from, TIterator to)
|
inline IndexedPackGroup executeIndexed(TIterator from, TIterator to)
|
||||||
{
|
{
|
||||||
return _arrangeIndexed(from, to);
|
return _executeIndexed(from, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shorthand to normal arrange method.
|
/// Shorthand to normal arrange method.
|
||||||
template<class TIterator>
|
template<class TIterator>
|
||||||
inline PackGroup operator() (TIterator from, TIterator to)
|
inline PackGroup operator() (TIterator from, TIterator to)
|
||||||
{
|
{
|
||||||
return _arrange(from, to);
|
return _execute(from, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set a progress indicatior function object for the selector.
|
/// Set a progress indicatior function object for the selector.
|
||||||
inline Arranger& progressIndicator(ProgressFunction func)
|
inline Nester& progressIndicator(ProgressFunction func)
|
||||||
{
|
{
|
||||||
selector_.progressIndicator(func); return *this;
|
selector_.progressIndicator(func); return *this;
|
||||||
}
|
}
|
||||||
|
@ -850,10 +872,6 @@ public:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline Arranger& useMinimumBoundigBoxRotation(bool s = true) {
|
|
||||||
use_min_bb_rotation_ = s; return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
template<class TIterator,
|
template<class TIterator,
|
||||||
|
@ -865,9 +883,9 @@ private:
|
||||||
// have to exist for the lifetime of this call.
|
// have to exist for the lifetime of this call.
|
||||||
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT>
|
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT>
|
||||||
>
|
>
|
||||||
inline PackGroup _arrange(TIterator from, TIterator to, bool = false)
|
inline PackGroup _execute(TIterator from, TIterator to, bool = false)
|
||||||
{
|
{
|
||||||
__arrange(from, to);
|
__execute(from, to);
|
||||||
return lastResult();
|
return lastResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -875,11 +893,11 @@ private:
|
||||||
class IT = remove_cvref_t<typename TIterator::value_type>,
|
class IT = remove_cvref_t<typename TIterator::value_type>,
|
||||||
class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT>
|
class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT>
|
||||||
>
|
>
|
||||||
inline PackGroup _arrange(TIterator from, TIterator to, int = false)
|
inline PackGroup _execute(TIterator from, TIterator to, int = false)
|
||||||
{
|
{
|
||||||
item_cache_ = {from, to};
|
item_cache_ = {from, to};
|
||||||
|
|
||||||
__arrange(item_cache_.begin(), item_cache_.end());
|
__execute(item_cache_.begin(), item_cache_.end());
|
||||||
return lastResult();
|
return lastResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -892,11 +910,11 @@ private:
|
||||||
// have to exist for the lifetime of this call.
|
// have to exist for the lifetime of this call.
|
||||||
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT>
|
class T = enable_if_t< std::is_convertible<IT, TPItem>::value, IT>
|
||||||
>
|
>
|
||||||
inline IndexedPackGroup _arrangeIndexed(TIterator from,
|
inline IndexedPackGroup _executeIndexed(TIterator from,
|
||||||
TIterator to,
|
TIterator to,
|
||||||
bool = false)
|
bool = false)
|
||||||
{
|
{
|
||||||
__arrange(from, to);
|
__execute(from, to);
|
||||||
return createIndexedPackGroup(from, to, selector_);
|
return createIndexedPackGroup(from, to, selector_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -904,12 +922,12 @@ private:
|
||||||
class IT = remove_cvref_t<typename TIterator::value_type>,
|
class IT = remove_cvref_t<typename TIterator::value_type>,
|
||||||
class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT>
|
class T = enable_if_t<!std::is_convertible<IT, TPItem>::value, IT>
|
||||||
>
|
>
|
||||||
inline IndexedPackGroup _arrangeIndexed(TIterator from,
|
inline IndexedPackGroup _executeIndexed(TIterator from,
|
||||||
TIterator to,
|
TIterator to,
|
||||||
int = false)
|
int = false)
|
||||||
{
|
{
|
||||||
item_cache_ = {from, to};
|
item_cache_ = {from, to};
|
||||||
__arrange(item_cache_.begin(), item_cache_.end());
|
__execute(item_cache_.begin(), item_cache_.end());
|
||||||
return createIndexedPackGroup(from, to, selector_);
|
return createIndexedPackGroup(from, to, selector_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -941,37 +959,12 @@ private:
|
||||||
return pg;
|
return pg;
|
||||||
}
|
}
|
||||||
|
|
||||||
Radians findBestRotation(Item& item) {
|
template<class TIter> inline void __execute(TIter from, TIter to)
|
||||||
opt::StopCriteria stopcr;
|
|
||||||
stopcr.absolute_score_difference = 0.01;
|
|
||||||
stopcr.max_iterations = 10000;
|
|
||||||
opt::TOptimizer<opt::Method::G_GENETIC> solver(stopcr);
|
|
||||||
|
|
||||||
auto orig_rot = item.rotation();
|
|
||||||
|
|
||||||
auto result = solver.optimize_min([&item, &orig_rot](Radians rot){
|
|
||||||
item.rotation(orig_rot + rot);
|
|
||||||
auto bb = item.boundingBox();
|
|
||||||
return std::sqrt(bb.height()*bb.width());
|
|
||||||
}, opt::initvals(Radians(0)), opt::bound<Radians>(-Pi/2, Pi/2));
|
|
||||||
|
|
||||||
item.rotation(orig_rot);
|
|
||||||
|
|
||||||
return std::get<0>(result.optimum);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class TIter> inline void __arrange(TIter from, TIter to)
|
|
||||||
{
|
{
|
||||||
if(min_obj_distance_ > 0) std::for_each(from, to, [this](Item& item) {
|
if(min_obj_distance_ > 0) std::for_each(from, to, [this](Item& item) {
|
||||||
item.addOffset(static_cast<Unit>(std::ceil(min_obj_distance_/2.0)));
|
item.addOffset(static_cast<Unit>(std::ceil(min_obj_distance_/2.0)));
|
||||||
});
|
});
|
||||||
|
|
||||||
if(use_min_bb_rotation_)
|
|
||||||
std::for_each(from, to, [this](Item& item){
|
|
||||||
Radians rot = findBestRotation(item);
|
|
||||||
item.rotate(rot);
|
|
||||||
});
|
|
||||||
|
|
||||||
selector_.template packItems<PlacementStrategy>(
|
selector_.template packItems<PlacementStrategy>(
|
||||||
from, to, bin_, pconfig_);
|
from, to, bin_, pconfig_);
|
||||||
|
|
||||||
|
|
|
@ -27,11 +27,10 @@ public:
|
||||||
|
|
||||||
explicit _BottomLeftPlacer(const BinType& bin): Base(bin) {}
|
explicit _BottomLeftPlacer(const BinType& bin): Base(bin) {}
|
||||||
|
|
||||||
template<class Store>
|
template<class Range = ConstItemRange<typename Base::DefaultIter>>
|
||||||
PackResult trypack(Store& /*s*/, typename Store::iterator from,
|
PackResult trypack(Item& item,
|
||||||
unsigned /*count*/ = 1)
|
const Range& = Range())
|
||||||
{
|
{
|
||||||
Item& item = *from;
|
|
||||||
auto r = _trypack(item);
|
auto r = _trypack(item);
|
||||||
if(!r && Base::config_.allow_rotations) {
|
if(!r && Base::config_.allow_rotations) {
|
||||||
|
|
||||||
|
@ -117,10 +116,10 @@ protected:
|
||||||
const RawShape& scanpoly)
|
const RawShape& scanpoly)
|
||||||
{
|
{
|
||||||
auto tsh = other.transformedShape();
|
auto tsh = other.transformedShape();
|
||||||
return ( ShapeLike::intersects(tsh, scanpoly) ||
|
return ( sl::intersects(tsh, scanpoly) ||
|
||||||
ShapeLike::isInside(tsh, scanpoly) ) &&
|
sl::isInside(tsh, scanpoly) ) &&
|
||||||
( !ShapeLike::intersects(tsh, item.rawShape()) &&
|
( !sl::intersects(tsh, item.rawShape()) &&
|
||||||
!ShapeLike::isInside(tsh, item.rawShape()) );
|
!sl::isInside(tsh, item.rawShape()) );
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class C = Coord>
|
template<class C = Coord>
|
||||||
|
@ -131,25 +130,25 @@ protected:
|
||||||
{
|
{
|
||||||
auto tsh = other.transformedShape();
|
auto tsh = other.transformedShape();
|
||||||
|
|
||||||
bool inters_scanpoly = ShapeLike::intersects(tsh, scanpoly) &&
|
bool inters_scanpoly = sl::intersects(tsh, scanpoly) &&
|
||||||
!ShapeLike::touches(tsh, scanpoly);
|
!sl::touches(tsh, scanpoly);
|
||||||
bool inters_item = ShapeLike::intersects(tsh, item.rawShape()) &&
|
bool inters_item = sl::intersects(tsh, item.rawShape()) &&
|
||||||
!ShapeLike::touches(tsh, item.rawShape());
|
!sl::touches(tsh, item.rawShape());
|
||||||
|
|
||||||
return ( inters_scanpoly ||
|
return ( inters_scanpoly ||
|
||||||
ShapeLike::isInside(tsh, scanpoly)) &&
|
sl::isInside(tsh, scanpoly)) &&
|
||||||
( !inters_item &&
|
( !inters_item &&
|
||||||
!ShapeLike::isInside(tsh, item.rawShape())
|
!sl::isInside(tsh, item.rawShape())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Container itemsInTheWayOf(const Item& item, const Dir dir) {
|
ItemGroup itemsInTheWayOf(const Item& item, const Dir dir) {
|
||||||
// Get the left or down polygon, that has the same area as the shadow
|
// Get the left or down polygon, that has the same area as the shadow
|
||||||
// of input item reflected to the left or downwards
|
// of input item reflected to the left or downwards
|
||||||
auto&& scanpoly = dir == Dir::LEFT? leftPoly(item) :
|
auto&& scanpoly = dir == Dir::LEFT? leftPoly(item) :
|
||||||
downPoly(item);
|
downPoly(item);
|
||||||
|
|
||||||
Container ret; // packed items 'in the way' of item
|
ItemGroup ret; // packed items 'in the way' of item
|
||||||
ret.reserve(items_.size());
|
ret.reserve(items_.size());
|
||||||
|
|
||||||
// Predicate to find items that are 'in the way' for left (down) move
|
// Predicate to find items that are 'in the way' for left (down) move
|
||||||
|
@ -178,18 +177,18 @@ protected:
|
||||||
|
|
||||||
if(dir == Dir::LEFT) {
|
if(dir == Dir::LEFT) {
|
||||||
getCoord = [](const Vertex& v) { return getX(v); };
|
getCoord = [](const Vertex& v) { return getX(v); };
|
||||||
availableDistance = PointLike::horizontalDistance<Vertex>;
|
availableDistance = pointlike::horizontalDistance<Vertex>;
|
||||||
availableDistanceSV = [](const Segment& s, const Vertex& v) {
|
availableDistanceSV = [](const Segment& s, const Vertex& v) {
|
||||||
auto ret = PointLike::horizontalDistance<Vertex>(v, s);
|
auto ret = pointlike::horizontalDistance<Vertex>(v, s);
|
||||||
if(ret.second) ret.first = -ret.first;
|
if(ret.second) ret.first = -ret.first;
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
getCoord = [](const Vertex& v) { return getY(v); };
|
getCoord = [](const Vertex& v) { return getY(v); };
|
||||||
availableDistance = PointLike::verticalDistance<Vertex>;
|
availableDistance = pointlike::verticalDistance<Vertex>;
|
||||||
availableDistanceSV = [](const Segment& s, const Vertex& v) {
|
availableDistanceSV = [](const Segment& s, const Vertex& v) {
|
||||||
auto ret = PointLike::verticalDistance<Vertex>(v, s);
|
auto ret = pointlike::verticalDistance<Vertex>(v, s);
|
||||||
if(ret.second) ret.first = -ret.first;
|
if(ret.second) ret.first = -ret.first;
|
||||||
return ret;
|
return ret;
|
||||||
};
|
};
|
||||||
|
@ -219,9 +218,9 @@ protected:
|
||||||
assert(pleft.vertexCount() > 0);
|
assert(pleft.vertexCount() > 0);
|
||||||
|
|
||||||
auto trpleft = pleft.transformedShape();
|
auto trpleft = pleft.transformedShape();
|
||||||
auto first = ShapeLike::begin(trpleft);
|
auto first = sl::begin(trpleft);
|
||||||
auto next = first + 1;
|
auto next = first + 1;
|
||||||
auto endit = ShapeLike::end(trpleft);
|
auto endit = sl::end(trpleft);
|
||||||
|
|
||||||
while(next != endit) {
|
while(next != endit) {
|
||||||
Segment seg(*(first++), *(next++));
|
Segment seg(*(first++), *(next++));
|
||||||
|
@ -345,16 +344,16 @@ protected:
|
||||||
|
|
||||||
// reserve for all vertices plus 2 for the left horizontal wall, 2 for
|
// reserve for all vertices plus 2 for the left horizontal wall, 2 for
|
||||||
// the additional vertices for maintaning min object distance
|
// the additional vertices for maintaning min object distance
|
||||||
ShapeLike::reserve(rsh, finish-start+4);
|
sl::reserve(rsh, finish-start+4);
|
||||||
|
|
||||||
/*auto addOthers = [&rsh, finish, start, &item](){
|
/*auto addOthers = [&rsh, finish, start, &item](){
|
||||||
for(size_t i = start+1; i < finish; i++)
|
for(size_t i = start+1; i < finish; i++)
|
||||||
ShapeLike::addVertex(rsh, item.vertex(i));
|
sl::addVertex(rsh, item.vertex(i));
|
||||||
};*/
|
};*/
|
||||||
|
|
||||||
auto reverseAddOthers = [&rsh, finish, start, &item](){
|
auto reverseAddOthers = [&rsh, finish, start, &item](){
|
||||||
for(auto i = finish-1; i > start; i--)
|
for(auto i = finish-1; i > start; i--)
|
||||||
ShapeLike::addVertex(rsh, item.vertex(
|
sl::addVertex(rsh, item.vertex(
|
||||||
static_cast<unsigned long>(i)));
|
static_cast<unsigned long>(i)));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -366,25 +365,25 @@ protected:
|
||||||
|
|
||||||
// Clockwise polygon construction
|
// Clockwise polygon construction
|
||||||
|
|
||||||
ShapeLike::addVertex(rsh, topleft_vertex);
|
sl::addVertex(rsh, topleft_vertex);
|
||||||
|
|
||||||
if(dir == Dir::LEFT) reverseAddOthers();
|
if(dir == Dir::LEFT) reverseAddOthers();
|
||||||
else {
|
else {
|
||||||
ShapeLike::addVertex(rsh, getX(topleft_vertex), 0);
|
sl::addVertex(rsh, getX(topleft_vertex), 0);
|
||||||
ShapeLike::addVertex(rsh, getX(bottomleft_vertex), 0);
|
sl::addVertex(rsh, getX(bottomleft_vertex), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShapeLike::addVertex(rsh, bottomleft_vertex);
|
sl::addVertex(rsh, bottomleft_vertex);
|
||||||
|
|
||||||
if(dir == Dir::LEFT) {
|
if(dir == Dir::LEFT) {
|
||||||
ShapeLike::addVertex(rsh, 0, getY(bottomleft_vertex));
|
sl::addVertex(rsh, 0, getY(bottomleft_vertex));
|
||||||
ShapeLike::addVertex(rsh, 0, getY(topleft_vertex));
|
sl::addVertex(rsh, 0, getY(topleft_vertex));
|
||||||
}
|
}
|
||||||
else reverseAddOthers();
|
else reverseAddOthers();
|
||||||
|
|
||||||
|
|
||||||
// Close the polygon
|
// Close the polygon
|
||||||
ShapeLike::addVertex(rsh, topleft_vertex);
|
sl::addVertex(rsh, topleft_vertex);
|
||||||
|
|
||||||
return rsh;
|
return rsh;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace libnest2d { namespace strategies {
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
struct NfpPConfig {
|
struct NfpPConfig {
|
||||||
|
|
||||||
using ItemGroup = std::vector<std::reference_wrapper<_Item<RawShape>>>;
|
using ItemGroup = _ItemGroup<_Item<RawShape>>;
|
||||||
|
|
||||||
enum class Alignment {
|
enum class Alignment {
|
||||||
CENTER,
|
CENTER,
|
||||||
|
@ -58,16 +58,6 @@ struct NfpPConfig {
|
||||||
*
|
*
|
||||||
* \param item The second parameter is the candidate item.
|
* \param item The second parameter is the candidate item.
|
||||||
*
|
*
|
||||||
* \param occupied_area The third parameter is the sum of areas of the
|
|
||||||
* items in the first parameter (no candidate item there) so you don't have
|
|
||||||
* to iterate through them if you only need their accumulated area.
|
|
||||||
*
|
|
||||||
* \param norm A norming factor for physical dimensions. E.g. if your score
|
|
||||||
* is the distance between the item and the bin center, you should divide
|
|
||||||
* that distance with the norming factor. If the score is an area than
|
|
||||||
* divide it with the square of the norming factor. Imagine it as a unit of
|
|
||||||
* distance.
|
|
||||||
*
|
|
||||||
* \param remaining A container with the remaining items waiting to be
|
* \param remaining A container with the remaining items waiting to be
|
||||||
* placed. You can use some features about the remaining items to alter to
|
* placed. You can use some features about the remaining items to alter to
|
||||||
* score of the current placement. If you know that you have to leave place
|
* score of the current placement. If you know that you have to leave place
|
||||||
|
@ -81,8 +71,8 @@ struct NfpPConfig {
|
||||||
* decisions (for you or a more intelligent AI).
|
* decisions (for you or a more intelligent AI).
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
std::function<double(Nfp::Shapes<RawShape>&, const _Item<RawShape>&,
|
std::function<double(nfp::Shapes<RawShape>&, const _Item<RawShape>&,
|
||||||
double, double, const ItemGroup&)>
|
const ItemGroup&)>
|
||||||
object_function;
|
object_function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -134,11 +124,11 @@ template<class RawShape> class EdgeCache {
|
||||||
|
|
||||||
void createCache(const RawShape& sh) {
|
void createCache(const RawShape& sh) {
|
||||||
{ // For the contour
|
{ // For the contour
|
||||||
auto first = ShapeLike::cbegin(sh);
|
auto first = shapelike::cbegin(sh);
|
||||||
auto next = std::next(first);
|
auto next = std::next(first);
|
||||||
auto endit = ShapeLike::cend(sh);
|
auto endit = shapelike::cend(sh);
|
||||||
|
|
||||||
contour_.distances.reserve(ShapeLike::contourVertexCount(sh));
|
contour_.distances.reserve(shapelike::contourVertexCount(sh));
|
||||||
|
|
||||||
while(next != endit) {
|
while(next != endit) {
|
||||||
contour_.emap.emplace_back(*(first++), *(next++));
|
contour_.emap.emplace_back(*(first++), *(next++));
|
||||||
|
@ -147,7 +137,7 @@ template<class RawShape> class EdgeCache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& h : ShapeLike::holes(sh)) { // For the holes
|
for(auto& h : shapelike::holes(sh)) { // For the holes
|
||||||
auto first = h.begin();
|
auto first = h.begin();
|
||||||
auto next = std::next(first);
|
auto next = std::next(first);
|
||||||
auto endit = h.end();
|
auto endit = h.end();
|
||||||
|
@ -295,11 +285,11 @@ public:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<NfpLevel lvl>
|
template<nfp::NfpLevel lvl>
|
||||||
struct Lvl { static const NfpLevel value = lvl; };
|
struct Lvl { static const nfp::NfpLevel value = lvl; };
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
inline void correctNfpPosition(Nfp::NfpResult<RawShape>& nfp,
|
inline void correctNfpPosition(nfp::NfpResult<RawShape>& nfp,
|
||||||
const _Item<RawShape>& stationary,
|
const _Item<RawShape>& stationary,
|
||||||
const _Item<RawShape>& orbiter)
|
const _Item<RawShape>& orbiter)
|
||||||
{
|
{
|
||||||
|
@ -319,46 +309,47 @@ inline void correctNfpPosition(Nfp::NfpResult<RawShape>& nfp,
|
||||||
auto dtouch = touch_sh - touch_other;
|
auto dtouch = touch_sh - touch_other;
|
||||||
auto top_other = orbiter.rightmostTopVertex() + dtouch;
|
auto top_other = orbiter.rightmostTopVertex() + dtouch;
|
||||||
auto dnfp = top_other - nfp.second; // nfp.second is the nfp reference point
|
auto dnfp = top_other - nfp.second; // nfp.second is the nfp reference point
|
||||||
ShapeLike::translate(nfp.first, dnfp);
|
shapelike::translate(nfp.first, dnfp);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
inline void correctNfpPosition(Nfp::NfpResult<RawShape>& nfp,
|
inline void correctNfpPosition(nfp::NfpResult<RawShape>& nfp,
|
||||||
const RawShape& stationary,
|
const RawShape& stationary,
|
||||||
const _Item<RawShape>& orbiter)
|
const _Item<RawShape>& orbiter)
|
||||||
{
|
{
|
||||||
auto touch_sh = Nfp::rightmostUpVertex(stationary);
|
auto touch_sh = nfp::rightmostUpVertex(stationary);
|
||||||
auto touch_other = orbiter.leftmostBottomVertex();
|
auto touch_other = orbiter.leftmostBottomVertex();
|
||||||
auto dtouch = touch_sh - touch_other;
|
auto dtouch = touch_sh - touch_other;
|
||||||
auto top_other = orbiter.rightmostTopVertex() + dtouch;
|
auto top_other = orbiter.rightmostTopVertex() + dtouch;
|
||||||
auto dnfp = top_other - nfp.second;
|
auto dnfp = top_other - nfp.second;
|
||||||
ShapeLike::translate(nfp.first, dnfp);
|
shapelike::translate(nfp.first, dnfp);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape, class Container>
|
template<class RawShape, class Container>
|
||||||
Nfp::Shapes<RawShape> nfp( const Container& polygons,
|
nfp::Shapes<RawShape> calcnfp( const Container& polygons,
|
||||||
const _Item<RawShape>& trsh,
|
const _Item<RawShape>& trsh,
|
||||||
Lvl<NfpLevel::CONVEX_ONLY>)
|
Lvl<nfp::NfpLevel::CONVEX_ONLY>)
|
||||||
{
|
{
|
||||||
using Item = _Item<RawShape>;
|
using Item = _Item<RawShape>;
|
||||||
|
using namespace nfp;
|
||||||
|
|
||||||
Nfp::Shapes<RawShape> nfps;
|
nfp::Shapes<RawShape> nfps;
|
||||||
|
|
||||||
// int pi = 0;
|
// int pi = 0;
|
||||||
for(Item& sh : polygons) {
|
for(Item& sh : polygons) {
|
||||||
auto subnfp_r = Nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(
|
auto subnfp_r = noFitPolygon<NfpLevel::CONVEX_ONLY>(
|
||||||
sh.transformedShape(), trsh.transformedShape());
|
sh.transformedShape(), trsh.transformedShape());
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
auto vv = ShapeLike::isValid(sh.transformedShape());
|
auto vv = sl::isValid(sh.transformedShape());
|
||||||
assert(vv.first);
|
assert(vv.first);
|
||||||
|
|
||||||
auto vnfp = ShapeLike::isValid(subnfp_r.first);
|
auto vnfp = sl::isValid(subnfp_r.first);
|
||||||
assert(vnfp.first);
|
assert(vnfp.first);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
correctNfpPosition(subnfp_r, sh, trsh);
|
correctNfpPosition(subnfp_r, sh, trsh);
|
||||||
|
|
||||||
nfps = Nfp::merge(nfps, subnfp_r.first);
|
nfps = nfp::merge(nfps, subnfp_r.first);
|
||||||
|
|
||||||
// double SCALE = 1000000;
|
// double SCALE = 1000000;
|
||||||
// using SVGWriter = svg::SVGWriter<RawShape>;
|
// using SVGWriter = svg::SVGWriter<RawShape>;
|
||||||
|
@ -379,31 +370,32 @@ Nfp::Shapes<RawShape> nfp( const Container& polygons,
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class RawShape, class Container, class Level>
|
template<class RawShape, class Container, class Level>
|
||||||
Nfp::Shapes<RawShape> nfp( const Container& polygons,
|
nfp::Shapes<RawShape> calcnfp( const Container& polygons,
|
||||||
const _Item<RawShape>& trsh,
|
const _Item<RawShape>& trsh,
|
||||||
Level)
|
Level)
|
||||||
{
|
{
|
||||||
|
using namespace nfp;
|
||||||
using Item = _Item<RawShape>;
|
using Item = _Item<RawShape>;
|
||||||
|
|
||||||
Nfp::Shapes<RawShape> nfps;
|
Shapes<RawShape> nfps;
|
||||||
|
|
||||||
auto& orb = trsh.transformedShape();
|
auto& orb = trsh.transformedShape();
|
||||||
bool orbconvex = trsh.isContourConvex();
|
bool orbconvex = trsh.isContourConvex();
|
||||||
|
|
||||||
for(Item& sh : polygons) {
|
for(Item& sh : polygons) {
|
||||||
Nfp::NfpResult<RawShape> subnfp;
|
nfp::NfpResult<RawShape> subnfp;
|
||||||
auto& stat = sh.transformedShape();
|
auto& stat = sh.transformedShape();
|
||||||
|
|
||||||
if(sh.isContourConvex() && orbconvex)
|
if(sh.isContourConvex() && orbconvex)
|
||||||
subnfp = Nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(stat, orb);
|
subnfp = nfp::noFitPolygon<NfpLevel::CONVEX_ONLY>(stat, orb);
|
||||||
else if(orbconvex)
|
else if(orbconvex)
|
||||||
subnfp = Nfp::noFitPolygon<NfpLevel::ONE_CONVEX>(stat, orb);
|
subnfp = nfp::noFitPolygon<NfpLevel::ONE_CONVEX>(stat, orb);
|
||||||
else
|
else
|
||||||
subnfp = Nfp::noFitPolygon<Level::value>(stat, orb);
|
subnfp = nfp::noFitPolygon<Level::value>(stat, orb);
|
||||||
|
|
||||||
correctNfpPosition(subnfp, sh, trsh);
|
correctNfpPosition(subnfp, sh, trsh);
|
||||||
|
|
||||||
nfps = Nfp::merge(nfps, subnfp.first);
|
nfps = nfp::merge(nfps, subnfp.first);
|
||||||
}
|
}
|
||||||
|
|
||||||
return nfps;
|
return nfps;
|
||||||
|
@ -448,7 +440,6 @@ Nfp::Shapes<RawShape> nfp( const Container& polygons,
|
||||||
|
|
||||||
template<class RawShape>
|
template<class RawShape>
|
||||||
_Circle<TPoint<RawShape>> minimizeCircle(const RawShape& sh) {
|
_Circle<TPoint<RawShape>> minimizeCircle(const RawShape& sh) {
|
||||||
using sl = ShapeLike; using pl = PointLike;
|
|
||||||
using Point = TPoint<RawShape>;
|
using Point = TPoint<RawShape>;
|
||||||
using Coord = TCoord<Point>;
|
using Coord = TCoord<Point>;
|
||||||
|
|
||||||
|
@ -518,16 +509,14 @@ class _NofitPolyPlacer: public PlacerBoilerplate<_NofitPolyPlacer<RawShape, TBin
|
||||||
|
|
||||||
const double norm_;
|
const double norm_;
|
||||||
|
|
||||||
using MaxNfpLevel = Nfp::MaxNfpLevel<RawShape>;
|
using MaxNfpLevel = nfp::MaxNfpLevel<RawShape>;
|
||||||
using sl = ShapeLike;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
using Pile = Nfp::Shapes<RawShape>;
|
using Pile = nfp::Shapes<RawShape>;
|
||||||
|
|
||||||
inline explicit _NofitPolyPlacer(const BinType& bin):
|
inline explicit _NofitPolyPlacer(const BinType& bin):
|
||||||
Base(bin),
|
Base(bin),
|
||||||
norm_(std::sqrt(sl::area<RawShape>(bin))) {}
|
norm_(std::sqrt(sl::area(bin))) {}
|
||||||
|
|
||||||
_NofitPolyPlacer(const _NofitPolyPlacer&) = default;
|
_NofitPolyPlacer(const _NofitPolyPlacer&) = default;
|
||||||
_NofitPolyPlacer& operator=(const _NofitPolyPlacer&) = default;
|
_NofitPolyPlacer& operator=(const _NofitPolyPlacer&) = default;
|
||||||
|
@ -577,20 +566,17 @@ public:
|
||||||
return boundingCircle(chull).radius() < bin.radius();
|
return boundingCircle(chull).radius() < bin.radius();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Container>
|
template<class Range = ConstItemRange<typename Base::DefaultIter>>
|
||||||
PackResult trypack(Container& items,
|
PackResult trypack(
|
||||||
typename Container::iterator from,
|
Item& item,
|
||||||
unsigned /*count*/ = 1)
|
const Range& remaining = Range()) {
|
||||||
{
|
|
||||||
return trypack(*from, {std::next(from), items.end()});
|
|
||||||
}
|
|
||||||
|
|
||||||
PackResult trypack(Item& item, ItemGroup remaining) {
|
|
||||||
|
|
||||||
PackResult ret;
|
PackResult ret;
|
||||||
|
|
||||||
bool can_pack = false;
|
bool can_pack = false;
|
||||||
|
|
||||||
|
auto remlist = ItemGroup(remaining.from, remaining.to);
|
||||||
|
|
||||||
if(items_.empty()) {
|
if(items_.empty()) {
|
||||||
setInitialPosition(item);
|
setInitialPosition(item);
|
||||||
can_pack = item.isInside(bin_);
|
can_pack = item.isInside(bin_);
|
||||||
|
@ -602,7 +588,7 @@ public:
|
||||||
auto initial_rot = item.rotation();
|
auto initial_rot = item.rotation();
|
||||||
Vertex final_tr = {0, 0};
|
Vertex final_tr = {0, 0};
|
||||||
Radians final_rot = initial_rot;
|
Radians final_rot = initial_rot;
|
||||||
Nfp::Shapes<RawShape> nfps;
|
nfp::Shapes<RawShape> nfps;
|
||||||
|
|
||||||
for(auto rot : config_.rotations) {
|
for(auto rot : config_.rotations) {
|
||||||
|
|
||||||
|
@ -615,8 +601,8 @@ public:
|
||||||
|
|
||||||
auto trsh = item.transformedShape();
|
auto trsh = item.transformedShape();
|
||||||
|
|
||||||
nfps = nfp(items_, item, Lvl<MaxNfpLevel::value>());
|
nfps = calcnfp(items_, item, Lvl<MaxNfpLevel::value>());
|
||||||
auto iv = Nfp::referenceVertex(trsh);
|
auto iv = nfp::referenceVertex(trsh);
|
||||||
|
|
||||||
auto startpos = item.translation();
|
auto startpos = item.translation();
|
||||||
|
|
||||||
|
@ -644,7 +630,7 @@ public:
|
||||||
ecache[opt.nfpidx].coords(opt.hidx, opt.relpos);
|
ecache[opt.nfpidx].coords(opt.hidx, opt.relpos);
|
||||||
};
|
};
|
||||||
|
|
||||||
Nfp::Shapes<RawShape> pile;
|
nfp::Shapes<RawShape> pile;
|
||||||
pile.reserve(items_.size()+1);
|
pile.reserve(items_.size()+1);
|
||||||
double pile_area = 0;
|
double pile_area = 0;
|
||||||
for(Item& mitem : items_) {
|
for(Item& mitem : items_) {
|
||||||
|
@ -652,17 +638,15 @@ public:
|
||||||
pile_area += mitem.area();
|
pile_area += mitem.area();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto merged_pile = Nfp::merge(pile);
|
auto merged_pile = nfp::merge(pile);
|
||||||
|
|
||||||
// This is the kernel part of the object function that is
|
// This is the kernel part of the object function that is
|
||||||
// customizable by the library client
|
// customizable by the library client
|
||||||
auto _objfunc = config_.object_function?
|
auto _objfunc = config_.object_function?
|
||||||
config_.object_function :
|
config_.object_function :
|
||||||
[this, &merged_pile](
|
[this, &merged_pile, &pile_area](
|
||||||
Nfp::Shapes<RawShape>& /*pile*/,
|
nfp::Shapes<RawShape>& /*pile*/,
|
||||||
const Item& item,
|
const Item& item,
|
||||||
double occupied_area,
|
|
||||||
double norm,
|
|
||||||
const ItemGroup& /*remaining*/)
|
const ItemGroup& /*remaining*/)
|
||||||
{
|
{
|
||||||
merged_pile.emplace_back(item.transformedShape());
|
merged_pile.emplace_back(item.transformedShape());
|
||||||
|
@ -670,7 +654,7 @@ public:
|
||||||
merged_pile.pop_back();
|
merged_pile.pop_back();
|
||||||
|
|
||||||
// The pack ratio -- how much is the convex hull occupied
|
// The pack ratio -- how much is the convex hull occupied
|
||||||
double pack_rate = occupied_area/sl::area(ch);
|
double pack_rate = (pile_area + item.area())/sl::area(ch);
|
||||||
|
|
||||||
// ratio of waste
|
// ratio of waste
|
||||||
double waste = 1.0 - pack_rate;
|
double waste = 1.0 - pack_rate;
|
||||||
|
@ -680,7 +664,7 @@ public:
|
||||||
// (larger) values.
|
// (larger) values.
|
||||||
auto score = std::sqrt(waste);
|
auto score = std::sqrt(waste);
|
||||||
|
|
||||||
if(!wouldFit(ch, bin_)) score += norm;
|
if(!wouldFit(ch, bin_)) score += norm_;
|
||||||
|
|
||||||
return score;
|
return score;
|
||||||
};
|
};
|
||||||
|
@ -692,10 +676,7 @@ public:
|
||||||
d += startpos;
|
d += startpos;
|
||||||
item.translation(d);
|
item.translation(d);
|
||||||
|
|
||||||
double occupied_area = pile_area + item.area();
|
double score = _objfunc(pile, item, remlist);
|
||||||
|
|
||||||
double score = _objfunc(pile, item, occupied_area,
|
|
||||||
norm_, remaining);
|
|
||||||
|
|
||||||
return score;
|
return score;
|
||||||
};
|
};
|
||||||
|
@ -830,7 +811,7 @@ private:
|
||||||
|
|
||||||
inline void finalAlign(_Circle<TPoint<RawShape>> cbin) {
|
inline void finalAlign(_Circle<TPoint<RawShape>> cbin) {
|
||||||
if(items_.empty()) return;
|
if(items_.empty()) return;
|
||||||
Nfp::Shapes<RawShape> m;
|
nfp::Shapes<RawShape> m;
|
||||||
m.reserve(items_.size());
|
m.reserve(items_.size());
|
||||||
for(Item& item : items_) m.emplace_back(item.transformedShape());
|
for(Item& item : items_) m.emplace_back(item.transformedShape());
|
||||||
|
|
||||||
|
@ -842,7 +823,7 @@ private:
|
||||||
|
|
||||||
inline void finalAlign(Box bbin) {
|
inline void finalAlign(Box bbin) {
|
||||||
if(items_.empty()) return;
|
if(items_.empty()) return;
|
||||||
Nfp::Shapes<RawShape> m;
|
nfp::Shapes<RawShape> m;
|
||||||
m.reserve(items_.size());
|
m.reserve(items_.size());
|
||||||
for(Item& item : items_) m.emplace_back(item.transformedShape());
|
for(Item& item : items_) m.emplace_back(item.transformedShape());
|
||||||
auto&& bb = sl::boundingBox<RawShape>(m);
|
auto&& bb = sl::boundingBox<RawShape>(m);
|
||||||
|
@ -884,7 +865,7 @@ private:
|
||||||
void setInitialPosition(Item& item) {
|
void setInitialPosition(Item& item) {
|
||||||
Box&& bb = item.boundingBox();
|
Box&& bb = item.boundingBox();
|
||||||
Vertex ci, cb;
|
Vertex ci, cb;
|
||||||
auto bbin = sl::boundingBox<RawShape>(bin_);
|
auto bbin = sl::boundingBox(bin_);
|
||||||
|
|
||||||
switch(config_.starting_point) {
|
switch(config_.starting_point) {
|
||||||
case Config::Alignment::CENTER: {
|
case Config::Alignment::CENTER: {
|
||||||
|
@ -920,7 +901,7 @@ private:
|
||||||
|
|
||||||
void placeOutsideOfBin(Item& item) {
|
void placeOutsideOfBin(Item& item) {
|
||||||
auto&& bb = item.boundingBox();
|
auto&& bb = item.boundingBox();
|
||||||
Box binbb = sl::boundingBox<RawShape>(bin_);
|
Box binbb = sl::boundingBox(bin_);
|
||||||
|
|
||||||
Vertex v = { getX(bb.maxCorner()), getY(bb.minCorner()) };
|
Vertex v = { getX(bb.maxCorner()), getY(bb.minCorner()) };
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,7 @@ namespace libnest2d { namespace strategies {
|
||||||
|
|
||||||
struct EmptyConfig {};
|
struct EmptyConfig {};
|
||||||
|
|
||||||
template<class Subclass, class RawShape, class TBin,
|
template<class Subclass, class RawShape, class TBin, class Cfg = EmptyConfig>
|
||||||
class Cfg = EmptyConfig,
|
|
||||||
class Store = std::vector<std::reference_wrapper<_Item<RawShape>>>
|
|
||||||
>
|
|
||||||
class PlacerBoilerplate {
|
class PlacerBoilerplate {
|
||||||
mutable bool farea_valid_ = false;
|
mutable bool farea_valid_ = false;
|
||||||
mutable double farea_ = 0.0;
|
mutable double farea_ = 0.0;
|
||||||
|
@ -22,7 +19,8 @@ public:
|
||||||
using Coord = TCoord<Vertex>;
|
using Coord = TCoord<Vertex>;
|
||||||
using Unit = Coord;
|
using Unit = Coord;
|
||||||
using Config = Cfg;
|
using Config = Cfg;
|
||||||
using Container = Store;
|
using ItemGroup = _ItemGroup<Item>;
|
||||||
|
using DefaultIter = typename ItemGroup::const_iterator;
|
||||||
|
|
||||||
class PackResult {
|
class PackResult {
|
||||||
Item *item_ptr_;
|
Item *item_ptr_;
|
||||||
|
@ -39,8 +37,6 @@ public:
|
||||||
operator bool() { return item_ptr_ != nullptr; }
|
operator bool() { return item_ptr_ != nullptr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
using ItemGroup = const Container&;
|
|
||||||
|
|
||||||
inline PlacerBoilerplate(const BinType& bin, unsigned cap = 50): bin_(bin)
|
inline PlacerBoilerplate(const BinType& bin, unsigned cap = 50): bin_(bin)
|
||||||
{
|
{
|
||||||
items_.reserve(cap);
|
items_.reserve(cap);
|
||||||
|
@ -56,11 +52,10 @@ public:
|
||||||
config_ = config;
|
config_ = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Container>
|
template<class Range = ConstItemRange<DefaultIter>>
|
||||||
bool pack(Container& items,
|
bool pack(Item& item,
|
||||||
typename Container::iterator from,
|
const Range& rem = Range()) {
|
||||||
unsigned count = 1) {
|
auto&& r = static_cast<Subclass*>(this)->trypack(item, rem);
|
||||||
auto&& r = static_cast<Subclass*>(this)->trypack(items, from, count);
|
|
||||||
if(r) {
|
if(r) {
|
||||||
items_.push_back(*(r.item_ptr_));
|
items_.push_back(*(r.item_ptr_));
|
||||||
farea_valid_ = false;
|
farea_valid_ = false;
|
||||||
|
@ -82,7 +77,7 @@ public:
|
||||||
farea_valid_ = false;
|
farea_valid_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ItemGroup getItems() const { return items_; }
|
inline const ItemGroup& getItems() const { return items_; }
|
||||||
|
|
||||||
inline void clearItems() {
|
inline void clearItems() {
|
||||||
items_.clear();
|
items_.clear();
|
||||||
|
@ -113,7 +108,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
BinType bin_;
|
BinType bin_;
|
||||||
Container items_;
|
ItemGroup items_;
|
||||||
Cfg config_;
|
Cfg config_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -124,6 +119,7 @@ using Base::items_; \
|
||||||
using Base::config_; \
|
using Base::config_; \
|
||||||
public: \
|
public: \
|
||||||
using typename Base::Item; \
|
using typename Base::Item; \
|
||||||
|
using typename Base::ItemGroup; \
|
||||||
using typename Base::BinType; \
|
using typename Base::BinType; \
|
||||||
using typename Base::Config; \
|
using typename Base::Config; \
|
||||||
using typename Base::Vertex; \
|
using typename Base::Vertex; \
|
||||||
|
@ -131,7 +127,6 @@ using typename Base::Segment; \
|
||||||
using typename Base::PackResult; \
|
using typename Base::PackResult; \
|
||||||
using typename Base::Coord; \
|
using typename Base::Coord; \
|
||||||
using typename Base::Unit; \
|
using typename Base::Unit; \
|
||||||
using typename Base::Container; \
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
41
xs/src/libnest2d/libnest2d/rotfinder.hpp
Normal file
41
xs/src/libnest2d/libnest2d/rotfinder.hpp
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#ifndef ROTFINDER_HPP
|
||||||
|
#define ROTFINDER_HPP
|
||||||
|
|
||||||
|
#include <libnest2d/libnest2d.hpp>
|
||||||
|
#include <libnest2d/optimizer.hpp>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
namespace libnest2d {
|
||||||
|
|
||||||
|
template<class RawShape>
|
||||||
|
Radians findBestRotation(_Item<RawShape>& item) {
|
||||||
|
opt::StopCriteria stopcr;
|
||||||
|
stopcr.absolute_score_difference = 0.01;
|
||||||
|
stopcr.max_iterations = 10000;
|
||||||
|
opt::TOptimizer<opt::Method::G_GENETIC> solver(stopcr);
|
||||||
|
|
||||||
|
auto orig_rot = item.rotation();
|
||||||
|
|
||||||
|
auto result = solver.optimize_min([&item, &orig_rot](Radians rot){
|
||||||
|
item.rotation(orig_rot + rot);
|
||||||
|
auto bb = item.boundingBox();
|
||||||
|
return std::sqrt(bb.height()*bb.width());
|
||||||
|
}, opt::initvals(Radians(0)), opt::bound<Radians>(-Pi/2, Pi/2));
|
||||||
|
|
||||||
|
item.rotation(orig_rot);
|
||||||
|
|
||||||
|
return std::get<0>(result.optimum);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Iterator>
|
||||||
|
void findMinimumBoundingBoxRotations(Iterator from, Iterator to) {
|
||||||
|
using V = typename std::iterator_traits<Iterator>::value_type;
|
||||||
|
std::for_each(from, to, [](V& item){
|
||||||
|
Radians rot = findBestRotation(item);
|
||||||
|
item.rotate(rot);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // ROTFINDER_HPP
|
|
@ -118,7 +118,7 @@ public:
|
||||||
using Placer = PlacementStrategyLike<TPlacer>;
|
using Placer = PlacementStrategyLike<TPlacer>;
|
||||||
using ItemList = std::list<ItemRef>;
|
using ItemList = std::list<ItemRef>;
|
||||||
|
|
||||||
const double bin_area = ShapeLike::area<RawShape>(bin);
|
const double bin_area = sl::area(bin);
|
||||||
const double w = bin_area * config_.waste_increment;
|
const double w = bin_area * config_.waste_increment;
|
||||||
|
|
||||||
const double INITIAL_FILL_PROPORTION = config_.initial_fill_proportion;
|
const double INITIAL_FILL_PROPORTION = config_.initial_fill_proportion;
|
||||||
|
@ -227,10 +227,14 @@ public:
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
auto it = not_packed.begin();
|
auto it = not_packed.begin();
|
||||||
|
|
||||||
|
auto pack = [&placer, ¬_packed](ItemListIt it) {
|
||||||
|
return placer.pack(*it, rem(it, not_packed));
|
||||||
|
};
|
||||||
|
|
||||||
while(it != not_packed.end() && !ret &&
|
while(it != not_packed.end() && !ret &&
|
||||||
free_area - (item_area = it->get().area()) <= waste)
|
free_area - (item_area = it->get().area()) <= waste)
|
||||||
{
|
{
|
||||||
if(item_area <= free_area && placer.pack(not_packed, it) ) {
|
if(item_area <= free_area && pack(it) ) {
|
||||||
free_area -= item_area;
|
free_area -= item_area;
|
||||||
filled_area = bin_area - free_area;
|
filled_area = bin_area - free_area;
|
||||||
ret = true;
|
ret = true;
|
||||||
|
@ -270,6 +274,11 @@ public:
|
||||||
auto it2 = it;
|
auto it2 = it;
|
||||||
|
|
||||||
std::vector<TPair> wrong_pairs;
|
std::vector<TPair> wrong_pairs;
|
||||||
|
using std::placeholders::_1;
|
||||||
|
|
||||||
|
auto trypack = [&placer, ¬_packed](ItemListIt it) {
|
||||||
|
return placer.trypack(*it, rem(it, not_packed));
|
||||||
|
};
|
||||||
|
|
||||||
while(it != endit && !ret &&
|
while(it != endit && !ret &&
|
||||||
free_area - (item_area = it->get().area()) -
|
free_area - (item_area = it->get().area()) -
|
||||||
|
@ -278,7 +287,7 @@ public:
|
||||||
if(item_area + smallestPiece(it, not_packed)->get().area() >
|
if(item_area + smallestPiece(it, not_packed)->get().area() >
|
||||||
free_area ) { it++; continue; }
|
free_area ) { it++; continue; }
|
||||||
|
|
||||||
auto pr = placer.trypack(not_packed, it);
|
auto pr = trypack(it);
|
||||||
|
|
||||||
// First would fit
|
// First would fit
|
||||||
it2 = not_packed.begin();
|
it2 = not_packed.begin();
|
||||||
|
@ -294,14 +303,14 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
placer.accept(pr);
|
placer.accept(pr);
|
||||||
auto pr2 = placer.trypack(not_packed, it2);
|
auto pr2 = trypack(it2);
|
||||||
if(!pr2) {
|
if(!pr2) {
|
||||||
placer.unpackLast(); // remove first
|
placer.unpackLast(); // remove first
|
||||||
if(try_reverse) {
|
if(try_reverse) {
|
||||||
pr2 = placer.trypack(not_packed, it2);
|
pr2 = trypack(it2);
|
||||||
if(pr2) {
|
if(pr2) {
|
||||||
placer.accept(pr2);
|
placer.accept(pr2);
|
||||||
auto pr12 = placer.trypack(not_packed, it);
|
auto pr12 = trypack(it);
|
||||||
if(pr12) {
|
if(pr12) {
|
||||||
placer.accept(pr12);
|
placer.accept(pr12);
|
||||||
ret = true;
|
ret = true;
|
||||||
|
@ -365,6 +374,14 @@ public:
|
||||||
return it->get().area();
|
return it->get().area();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
auto trypack = [&placer, ¬_packed](ItemListIt it) {
|
||||||
|
return placer.trypack(*it, rem(it, not_packed));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto pack = [&placer, ¬_packed](ItemListIt it) {
|
||||||
|
return placer.pack(*it, rem(it, not_packed));
|
||||||
|
};
|
||||||
|
|
||||||
while (it != endit && !ret) { // drill down 1st level
|
while (it != endit && !ret) { // drill down 1st level
|
||||||
|
|
||||||
// We need to determine in each iteration the largest, second
|
// We need to determine in each iteration the largest, second
|
||||||
|
@ -394,7 +411,7 @@ public:
|
||||||
it++; continue;
|
it++; continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto pr = placer.trypack(not_packed, it);
|
auto pr = trypack(it);
|
||||||
|
|
||||||
// Check for free area and try to pack the 1st item...
|
// Check for free area and try to pack the 1st item...
|
||||||
if(!pr) { it++; continue; }
|
if(!pr) { it++; continue; }
|
||||||
|
@ -420,15 +437,15 @@ public:
|
||||||
bool can_pack2 = false;
|
bool can_pack2 = false;
|
||||||
|
|
||||||
placer.accept(pr);
|
placer.accept(pr);
|
||||||
auto pr2 = placer.trypack(not_packed, it2);
|
auto pr2 = trypack(it2);
|
||||||
auto pr12 = pr;
|
auto pr12 = pr;
|
||||||
if(!pr2) {
|
if(!pr2) {
|
||||||
placer.unpackLast(); // remove first
|
placer.unpackLast(); // remove first
|
||||||
if(try_reverse) {
|
if(try_reverse) {
|
||||||
pr2 = placer.trypack(not_packed, it2);
|
pr2 = trypack(it2);
|
||||||
if(pr2) {
|
if(pr2) {
|
||||||
placer.accept(pr2);
|
placer.accept(pr2);
|
||||||
pr12 = placer.trypack(not_packed, it);
|
pr12 = trypack(it);
|
||||||
if(pr12) can_pack2 = true;
|
if(pr12) can_pack2 = true;
|
||||||
placer.unpackLast();
|
placer.unpackLast();
|
||||||
}
|
}
|
||||||
|
@ -463,7 +480,7 @@ public:
|
||||||
if(a3_sum > free_area) { it3++; continue; }
|
if(a3_sum > free_area) { it3++; continue; }
|
||||||
|
|
||||||
placer.accept(pr12); placer.accept(pr2);
|
placer.accept(pr12); placer.accept(pr2);
|
||||||
bool can_pack3 = placer.pack(not_packed, it3);
|
bool can_pack3 = pack(it3);
|
||||||
|
|
||||||
if(!can_pack3) {
|
if(!can_pack3) {
|
||||||
placer.unpackLast();
|
placer.unpackLast();
|
||||||
|
@ -476,13 +493,14 @@ public:
|
||||||
std::array<typename ItemList::iterator, 3>
|
std::array<typename ItemList::iterator, 3>
|
||||||
candidates = {it, it2, it3};
|
candidates = {it, it2, it3};
|
||||||
|
|
||||||
auto tryPack = [&placer, &candidates, ¬_packed](
|
auto tryPack = [&placer, &candidates, ¬_packed,
|
||||||
|
&pack](
|
||||||
const decltype(indices)& idx)
|
const decltype(indices)& idx)
|
||||||
{
|
{
|
||||||
std::array<bool, 3> packed = {false};
|
std::array<bool, 3> packed = {false};
|
||||||
|
|
||||||
for(auto id : idx) packed.at(id) =
|
for(auto id : idx) packed.at(id) =
|
||||||
placer.pack(not_packed, candidates[id]);
|
pack(candidates[id]);
|
||||||
|
|
||||||
bool check =
|
bool check =
|
||||||
std::all_of(packed.begin(),
|
std::all_of(packed.begin(),
|
||||||
|
@ -536,7 +554,7 @@ public:
|
||||||
{ auto it = store_.begin();
|
{ auto it = store_.begin();
|
||||||
while (it != store_.end()) {
|
while (it != store_.end()) {
|
||||||
Placer p(bin); p.configure(pconfig);
|
Placer p(bin); p.configure(pconfig);
|
||||||
if(!p.pack(store_, it)) {
|
if(!p.pack(*it, rem(it, store_))) {
|
||||||
it = store_.erase(it);
|
it = store_.erase(it);
|
||||||
} else it++;
|
} else it++;
|
||||||
}
|
}
|
||||||
|
@ -601,7 +619,7 @@ public:
|
||||||
while(it != not_packed.end() &&
|
while(it != not_packed.end() &&
|
||||||
filled_area < INITIAL_FILL_AREA)
|
filled_area < INITIAL_FILL_AREA)
|
||||||
{
|
{
|
||||||
if(placer.pack(not_packed, it)) {
|
if(placer.pack(*it, rem(it, not_packed))) {
|
||||||
filled_area += it->get().area();
|
filled_area += it->get().area();
|
||||||
free_area = bin_area - filled_area;
|
free_area = bin_area - filled_area;
|
||||||
it = not_packed.erase(it);
|
it = not_packed.erase(it);
|
||||||
|
|
|
@ -56,18 +56,13 @@ public:
|
||||||
|
|
||||||
std::sort(store_.begin(), store_.end(), sortfunc);
|
std::sort(store_.begin(), store_.end(), sortfunc);
|
||||||
|
|
||||||
// Container a = {store_[0], store_[1], store_[4], store_[5] };
|
|
||||||
//// a.insert(a.end(), store_.end()-10, store_.end());
|
|
||||||
// store_ = a;
|
|
||||||
|
|
||||||
PlacementStrategyLike<TPlacer> placer(bin);
|
PlacementStrategyLike<TPlacer> placer(bin);
|
||||||
placer.configure(pconfig);
|
placer.configure(pconfig);
|
||||||
|
|
||||||
auto it = store_.begin();
|
auto it = store_.begin();
|
||||||
while(it != store_.end()) {
|
while(it != store_.end()) {
|
||||||
if(!placer.pack(store_, it)) {
|
if(!placer.pack(*it, {std::next(it), store_.end()})) {
|
||||||
if(packed_bins_.back().empty()) ++it;
|
if(packed_bins_.back().empty()) ++it;
|
||||||
// makeProgress(placer);
|
|
||||||
placer.clearItems();
|
placer.clearItems();
|
||||||
packed_bins_.emplace_back();
|
packed_bins_.emplace_back();
|
||||||
} else {
|
} else {
|
||||||
|
@ -76,9 +71,6 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if(was_packed) {
|
|
||||||
// packed_bins_.push_back(placer.getItems());
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ public:
|
||||||
{ auto it = store_.begin();
|
{ auto it = store_.begin();
|
||||||
while (it != store_.end()) {
|
while (it != store_.end()) {
|
||||||
Placer p(bin); p.configure(pconfig);
|
Placer p(bin); p.configure(pconfig);
|
||||||
if(!p.pack(store_, it)) {
|
if(!p.pack(*it)) {
|
||||||
it = store_.erase(it);
|
it = store_.erase(it);
|
||||||
} else it++;
|
} else it++;
|
||||||
}
|
}
|
||||||
|
@ -73,8 +73,9 @@ public:
|
||||||
while(!was_packed) {
|
while(!was_packed) {
|
||||||
|
|
||||||
for(size_t j = 0; j < placers.size() && !was_packed; j++) {
|
for(size_t j = 0; j < placers.size() && !was_packed; j++) {
|
||||||
if((was_packed = placers[j].pack(store_, it)))
|
if((was_packed =
|
||||||
makeProgress(placers[j], j);
|
placers[j].pack(*it, rem(it, store_) )))
|
||||||
|
makeProgress(placers[j], j);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!was_packed) {
|
if(!was_packed) {
|
||||||
|
|
|
@ -110,7 +110,7 @@ TEST(GeometryAlgorithms, boundingCircle) {
|
||||||
ASSERT_EQ(c.center().Y, 0);
|
ASSERT_EQ(c.center().Y, 0);
|
||||||
ASSERT_DOUBLE_EQ(c.radius(), 10);
|
ASSERT_DOUBLE_EQ(c.radius(), 10);
|
||||||
|
|
||||||
ShapeLike::translate(p, PointImpl{10, 10});
|
shapelike::translate(p, PointImpl{10, 10});
|
||||||
c = boundingCircle<PolygonImpl>(p);
|
c = boundingCircle<PolygonImpl>(p);
|
||||||
|
|
||||||
ASSERT_EQ(c.center().X, 10);
|
ASSERT_EQ(c.center().X, 10);
|
||||||
|
@ -124,8 +124,8 @@ TEST(GeometryAlgorithms, boundingCircle) {
|
||||||
c = boundingCircle(part.transformedShape());
|
c = boundingCircle(part.transformedShape());
|
||||||
if(std::isnan(c.radius())) std::cout << "fail: radius is nan" << std::endl;
|
if(std::isnan(c.radius())) std::cout << "fail: radius is nan" << std::endl;
|
||||||
|
|
||||||
else for(auto v : ShapeLike::getContour(part.transformedShape()) ) {
|
else for(auto v : shapelike::getContour(part.transformedShape()) ) {
|
||||||
auto d = PointLike::distance(v, c.center());
|
auto d = pointlike::distance(v, c.center());
|
||||||
if(d > c.radius() ) {
|
if(d > c.radius() ) {
|
||||||
auto e = std::abs( 1.0 - d/c.radius());
|
auto e = std::abs( 1.0 - d/c.radius());
|
||||||
ASSERT_LE(e, 1e-3);
|
ASSERT_LE(e, 1e-3);
|
||||||
|
@ -144,14 +144,14 @@ TEST(GeometryAlgorithms, Distance) {
|
||||||
Point p2 = {10, 0};
|
Point p2 = {10, 0};
|
||||||
Point p3 = {10, 10};
|
Point p3 = {10, 10};
|
||||||
|
|
||||||
ASSERT_DOUBLE_EQ(PointLike::distance(p1, p2), 10);
|
ASSERT_DOUBLE_EQ(pointlike::distance(p1, p2), 10);
|
||||||
ASSERT_DOUBLE_EQ(PointLike::distance(p1, p3), sqrt(200));
|
ASSERT_DOUBLE_EQ(pointlike::distance(p1, p3), sqrt(200));
|
||||||
|
|
||||||
Segment seg(p1, p3);
|
Segment seg(p1, p3);
|
||||||
|
|
||||||
ASSERT_DOUBLE_EQ(PointLike::distance(p2, seg), 7.0710678118654755);
|
ASSERT_DOUBLE_EQ(pointlike::distance(p2, seg), 7.0710678118654755);
|
||||||
|
|
||||||
auto result = PointLike::horizontalDistance(p2, seg);
|
auto result = pointlike::horizontalDistance(p2, seg);
|
||||||
|
|
||||||
auto check = [](Coord val, Coord expected) {
|
auto check = [](Coord val, Coord expected) {
|
||||||
if(std::is_floating_point<Coord>::value)
|
if(std::is_floating_point<Coord>::value)
|
||||||
|
@ -164,11 +164,11 @@ TEST(GeometryAlgorithms, Distance) {
|
||||||
ASSERT_TRUE(result.second);
|
ASSERT_TRUE(result.second);
|
||||||
check(result.first, 10);
|
check(result.first, 10);
|
||||||
|
|
||||||
result = PointLike::verticalDistance(p2, seg);
|
result = pointlike::verticalDistance(p2, seg);
|
||||||
ASSERT_TRUE(result.second);
|
ASSERT_TRUE(result.second);
|
||||||
check(result.first, -10);
|
check(result.first, -10);
|
||||||
|
|
||||||
result = PointLike::verticalDistance(Point{10, 20}, seg);
|
result = pointlike::verticalDistance(Point{10, 20}, seg);
|
||||||
ASSERT_TRUE(result.second);
|
ASSERT_TRUE(result.second);
|
||||||
check(result.first, 10);
|
check(result.first, 10);
|
||||||
|
|
||||||
|
@ -176,12 +176,12 @@ TEST(GeometryAlgorithms, Distance) {
|
||||||
Point p4 = {80, 0};
|
Point p4 = {80, 0};
|
||||||
Segment seg2 = { {0, 0}, {0, 40} };
|
Segment seg2 = { {0, 0}, {0, 40} };
|
||||||
|
|
||||||
result = PointLike::horizontalDistance(p4, seg2);
|
result = pointlike::horizontalDistance(p4, seg2);
|
||||||
|
|
||||||
ASSERT_TRUE(result.second);
|
ASSERT_TRUE(result.second);
|
||||||
check(result.first, 80);
|
check(result.first, 80);
|
||||||
|
|
||||||
result = PointLike::verticalDistance(p4, seg2);
|
result = pointlike::verticalDistance(p4, seg2);
|
||||||
// Point should not be related to the segment
|
// Point should not be related to the segment
|
||||||
ASSERT_FALSE(result.second);
|
ASSERT_FALSE(result.second);
|
||||||
|
|
||||||
|
@ -209,7 +209,7 @@ TEST(GeometryAlgorithms, Area) {
|
||||||
{61, 97}
|
{61, 97}
|
||||||
};
|
};
|
||||||
|
|
||||||
ASSERT_TRUE(ShapeLike::area(item.transformedShape()) > 0 );
|
ASSERT_TRUE(shapelike::area(item.transformedShape()) > 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(GeometryAlgorithms, IsPointInsidePolygon) {
|
TEST(GeometryAlgorithms, IsPointInsidePolygon) {
|
||||||
|
@ -287,7 +287,7 @@ TEST(GeometryAlgorithms, LeftAndDownPolygon)
|
||||||
|
|
||||||
Item leftp(placer.leftPoly(item));
|
Item leftp(placer.leftPoly(item));
|
||||||
|
|
||||||
ASSERT_TRUE(ShapeLike::isValid(leftp.rawShape()).first);
|
ASSERT_TRUE(shapelike::isValid(leftp.rawShape()).first);
|
||||||
ASSERT_EQ(leftp.vertexCount(), leftControl.vertexCount());
|
ASSERT_EQ(leftp.vertexCount(), leftControl.vertexCount());
|
||||||
|
|
||||||
for(unsigned long i = 0; i < leftControl.vertexCount(); i++) {
|
for(unsigned long i = 0; i < leftControl.vertexCount(); i++) {
|
||||||
|
@ -297,7 +297,7 @@ TEST(GeometryAlgorithms, LeftAndDownPolygon)
|
||||||
|
|
||||||
Item downp(placer.downPoly(item));
|
Item downp(placer.downPoly(item));
|
||||||
|
|
||||||
ASSERT_TRUE(ShapeLike::isValid(downp.rawShape()).first);
|
ASSERT_TRUE(shapelike::isValid(downp.rawShape()).first);
|
||||||
ASSERT_EQ(downp.vertexCount(), downControl.vertexCount());
|
ASSERT_EQ(downp.vertexCount(), downControl.vertexCount());
|
||||||
|
|
||||||
for(unsigned long i = 0; i < downControl.vertexCount(); i++) {
|
for(unsigned long i = 0; i < downControl.vertexCount(); i++) {
|
||||||
|
@ -334,7 +334,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesTight)
|
||||||
{20, 20} };
|
{20, 20} };
|
||||||
|
|
||||||
|
|
||||||
Arranger<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250));
|
Nester<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250));
|
||||||
|
|
||||||
auto groups = arrange(rects.begin(), rects.end());
|
auto groups = arrange(rects.begin(), rects.end());
|
||||||
|
|
||||||
|
@ -387,7 +387,7 @@ TEST(GeometryAlgorithms, ArrangeRectanglesLoose)
|
||||||
|
|
||||||
Coord min_obj_distance = 5;
|
Coord min_obj_distance = 5;
|
||||||
|
|
||||||
Arranger<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250),
|
Nester<BottomLeftPlacer, DJDHeuristic> arrange(Box(210, 250),
|
||||||
min_obj_distance);
|
min_obj_distance);
|
||||||
|
|
||||||
auto groups = arrange(rects.begin(), rects.end());
|
auto groups = arrange(rects.begin(), rects.end());
|
||||||
|
@ -438,7 +438,7 @@ R"raw(<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
setX(v, getX(v)/SCALE);
|
setX(v, getX(v)/SCALE);
|
||||||
rbin.setVertex(i, v);
|
rbin.setVertex(i, v);
|
||||||
}
|
}
|
||||||
out << ShapeLike::serialize<Formats::SVG>(rbin.rawShape()) << std::endl;
|
out << shapelike::serialize<Formats::SVG>(rbin.rawShape()) << std::endl;
|
||||||
for(Item& sh : r) {
|
for(Item& sh : r) {
|
||||||
Item tsh(sh.transformedShape());
|
Item tsh(sh.transformedShape());
|
||||||
for(unsigned i = 0; i < tsh.vertexCount(); i++) {
|
for(unsigned i = 0; i < tsh.vertexCount(); i++) {
|
||||||
|
@ -447,7 +447,7 @@ R"raw(<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
setX(v, getX(v)/SCALE);
|
setX(v, getX(v)/SCALE);
|
||||||
tsh.setVertex(i, v);
|
tsh.setVertex(i, v);
|
||||||
}
|
}
|
||||||
out << ShapeLike::serialize<Formats::SVG>(tsh.rawShape()) << std::endl;
|
out << shapelike::serialize<Formats::SVG>(tsh.rawShape()) << std::endl;
|
||||||
}
|
}
|
||||||
out << "\n</svg>" << std::endl;
|
out << "\n</svg>" << std::endl;
|
||||||
}
|
}
|
||||||
|
@ -471,8 +471,8 @@ TEST(GeometryAlgorithms, BottomLeftStressTest) {
|
||||||
auto next = it;
|
auto next = it;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while(it != input.end() && ++next != input.end()) {
|
while(it != input.end() && ++next != input.end()) {
|
||||||
placer.pack(input, it);
|
placer.pack(*it);
|
||||||
placer.pack(input, next);
|
placer.pack(*next);
|
||||||
|
|
||||||
auto result = placer.getItems();
|
auto result = placer.getItems();
|
||||||
bool valid = true;
|
bool valid = true;
|
||||||
|
@ -701,7 +701,7 @@ std::vector<ItemPair> nfp_concave_testdata = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<NfpLevel lvl, Coord SCALE>
|
template<nfp::NfpLevel lvl, Coord SCALE>
|
||||||
void testNfp(const std::vector<ItemPair>& testdata) {
|
void testNfp(const std::vector<ItemPair>& testdata) {
|
||||||
using namespace libnest2d;
|
using namespace libnest2d;
|
||||||
|
|
||||||
|
@ -716,12 +716,12 @@ void testNfp(const std::vector<ItemPair>& testdata) {
|
||||||
|
|
||||||
orbiter.translate({210*SCALE, 0});
|
orbiter.translate({210*SCALE, 0});
|
||||||
|
|
||||||
auto&& nfp = Nfp::noFitPolygon<lvl>(stationary.rawShape(),
|
auto&& nfp = nfp::noFitPolygon<lvl>(stationary.rawShape(),
|
||||||
orbiter.transformedShape());
|
orbiter.transformedShape());
|
||||||
|
|
||||||
strategies::correctNfpPosition(nfp, stationary, orbiter);
|
strategies::correctNfpPosition(nfp, stationary, orbiter);
|
||||||
|
|
||||||
auto v = ShapeLike::isValid(nfp.first);
|
auto v = shapelike::isValid(nfp.first);
|
||||||
|
|
||||||
if(!v.first) {
|
if(!v.first) {
|
||||||
std::cout << v.second << std::endl;
|
std::cout << v.second << std::endl;
|
||||||
|
@ -733,7 +733,7 @@ void testNfp(const std::vector<ItemPair>& testdata) {
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
auto rorbiter = orbiter.transformedShape();
|
auto rorbiter = orbiter.transformedShape();
|
||||||
auto vo = Nfp::referenceVertex(rorbiter);
|
auto vo = nfp::referenceVertex(rorbiter);
|
||||||
|
|
||||||
ASSERT_TRUE(stationary.isInside(infp));
|
ASSERT_TRUE(stationary.isInside(infp));
|
||||||
|
|
||||||
|
@ -774,7 +774,7 @@ void testNfp(const std::vector<ItemPair>& testdata) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(GeometryAlgorithms, nfpConvexConvex) {
|
TEST(GeometryAlgorithms, nfpConvexConvex) {
|
||||||
testNfp<NfpLevel::CONVEX_ONLY, 1>(nfp_testdata);
|
testNfp<nfp::NfpLevel::CONVEX_ONLY, 1>(nfp_testdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TEST(GeometryAlgorithms, nfpConcaveConcave) {
|
//TEST(GeometryAlgorithms, nfpConcaveConcave) {
|
||||||
|
@ -807,7 +807,7 @@ TEST(GeometryAlgorithms, pointOnPolygonContour) {
|
||||||
|
|
||||||
for(int i = 0; i <= 100; i++) {
|
for(int i = 0; i <= 100; i++) {
|
||||||
auto v = ecache.coords(i*(0.01));
|
auto v = ecache.coords(i*(0.01));
|
||||||
ASSERT_TRUE(ShapeLike::touches(v, input.transformedShape()));
|
ASSERT_TRUE(shapelike::touches(v, input.transformedShape()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -821,17 +821,17 @@ TEST(GeometryAlgorithms, mergePileWithPolygon) {
|
||||||
rect2.translate({10, 0});
|
rect2.translate({10, 0});
|
||||||
rect3.translate({25, 0});
|
rect3.translate({25, 0});
|
||||||
|
|
||||||
ShapeLike::Shapes<PolygonImpl> pile;
|
shapelike::Shapes<PolygonImpl> pile;
|
||||||
pile.push_back(rect1.transformedShape());
|
pile.push_back(rect1.transformedShape());
|
||||||
pile.push_back(rect2.transformedShape());
|
pile.push_back(rect2.transformedShape());
|
||||||
|
|
||||||
auto result = Nfp::merge(pile, rect3.transformedShape());
|
auto result = nfp::merge(pile, rect3.transformedShape());
|
||||||
|
|
||||||
ASSERT_EQ(result.size(), 1);
|
ASSERT_EQ(result.size(), 1);
|
||||||
|
|
||||||
Rectangle ref(45, 15);
|
Rectangle ref(45, 15);
|
||||||
|
|
||||||
ASSERT_EQ(ShapeLike::area(result.front()), ref.area());
|
ASSERT_EQ(shapelike::area(result.front()), ref.area());
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
|
|
@ -56,14 +56,14 @@ public:
|
||||||
auto d = static_cast<Coord>(
|
auto d = static_cast<Coord>(
|
||||||
std::round(conf_.height*conf_.mm_in_coord_units) );
|
std::round(conf_.height*conf_.mm_in_coord_units) );
|
||||||
|
|
||||||
auto& contour = ShapeLike::getContour(tsh);
|
auto& contour = shapelike::getContour(tsh);
|
||||||
for(auto& v : contour) setY(v, -getY(v) + d);
|
for(auto& v : contour) setY(v, -getY(v) + d);
|
||||||
|
|
||||||
auto& holes = ShapeLike::holes(tsh);
|
auto& holes = shapelike::holes(tsh);
|
||||||
for(auto& h : holes) for(auto& v : h) setY(v, -getY(v) + d);
|
for(auto& h : holes) for(auto& v : h) setY(v, -getY(v) + d);
|
||||||
|
|
||||||
}
|
}
|
||||||
currentLayer() += ShapeLike::serialize<Formats::SVG>(tsh,
|
currentLayer() += shapelike::serialize<Formats::SVG>(tsh,
|
||||||
1.0/conf_.mm_in_coord_units) + "\n";
|
1.0/conf_.mm_in_coord_units) + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,8 +104,7 @@ using ItemGroup = std::vector<std::reference_wrapper<Item>>;
|
||||||
std::tuple<double /*score*/, Box /*farthest point from bin center*/>
|
std::tuple<double /*score*/, Box /*farthest point from bin center*/>
|
||||||
objfunc(const PointImpl& bincenter,
|
objfunc(const PointImpl& bincenter,
|
||||||
double bin_area,
|
double bin_area,
|
||||||
ShapeLike::Shapes<PolygonImpl>& pile, // The currently arranged pile
|
sl::Shapes<PolygonImpl>& pile, // The currently arranged pile
|
||||||
double pile_area,
|
|
||||||
const Item &item,
|
const Item &item,
|
||||||
double norm, // A norming factor for physical dimensions
|
double norm, // A norming factor for physical dimensions
|
||||||
std::vector<double>& areacache, // pile item areas will be cached
|
std::vector<double>& areacache, // pile item areas will be cached
|
||||||
|
@ -114,8 +113,6 @@ objfunc(const PointImpl& bincenter,
|
||||||
const ItemGroup& remaining
|
const ItemGroup& remaining
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
using pl = PointLike;
|
|
||||||
using sl = ShapeLike;
|
|
||||||
using Coord = TCoord<PointImpl>;
|
using Coord = TCoord<PointImpl>;
|
||||||
|
|
||||||
static const double BIG_ITEM_TRESHOLD = 0.02;
|
static const double BIG_ITEM_TRESHOLD = 0.02;
|
||||||
|
@ -150,7 +147,7 @@ objfunc(const PointImpl& bincenter,
|
||||||
|
|
||||||
// Calculate the full bounding box of the pile with the candidate item
|
// Calculate the full bounding box of the pile with the candidate item
|
||||||
pile.emplace_back(item.transformedShape());
|
pile.emplace_back(item.transformedShape());
|
||||||
auto fullbb = ShapeLike::boundingBox(pile);
|
auto fullbb = sl::boundingBox(pile);
|
||||||
pile.pop_back();
|
pile.pop_back();
|
||||||
|
|
||||||
// The bounding box of the big items (they will accumulate in the center
|
// The bounding box of the big items (they will accumulate in the center
|
||||||
|
@ -283,21 +280,23 @@ class _ArrBase {
|
||||||
protected:
|
protected:
|
||||||
using Placer = strategies::_NofitPolyPlacer<PolygonImpl, TBin>;
|
using Placer = strategies::_NofitPolyPlacer<PolygonImpl, TBin>;
|
||||||
using Selector = FirstFitSelection;
|
using Selector = FirstFitSelection;
|
||||||
using Packer = Arranger<Placer, Selector>;
|
using Packer = Nester<Placer, Selector>;
|
||||||
using PConfig = typename Packer::PlacementConfig;
|
using PConfig = typename Packer::PlacementConfig;
|
||||||
using Distance = TCoord<PointImpl>;
|
using Distance = TCoord<PointImpl>;
|
||||||
using Pile = ShapeLike::Shapes<PolygonImpl>;
|
using Pile = sl::Shapes<PolygonImpl>;
|
||||||
|
|
||||||
Packer pck_;
|
Packer pck_;
|
||||||
PConfig pconf_; // Placement configuration
|
PConfig pconf_; // Placement configuration
|
||||||
double bin_area_;
|
double bin_area_;
|
||||||
std::vector<double> areacache_;
|
std::vector<double> areacache_;
|
||||||
SpatIndex rtree_;
|
SpatIndex rtree_;
|
||||||
|
double norm_;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
_ArrBase(const TBin& bin, Distance dist,
|
_ArrBase(const TBin& bin, Distance dist,
|
||||||
std::function<void(unsigned)> progressind):
|
std::function<void(unsigned)> progressind):
|
||||||
pck_(bin, dist), bin_area_(ShapeLike::area<PolygonImpl>(bin))
|
pck_(bin, dist), bin_area_(sl::area(bin)),
|
||||||
|
norm_(std::sqrt(sl::area(bin)))
|
||||||
{
|
{
|
||||||
fillConfig(pconf_);
|
fillConfig(pconf_);
|
||||||
pck_.progressIndicator(progressind);
|
pck_.progressIndicator(progressind);
|
||||||
|
@ -306,7 +305,7 @@ public:
|
||||||
template<class...Args> inline IndexedPackGroup operator()(Args&&...args) {
|
template<class...Args> inline IndexedPackGroup operator()(Args&&...args) {
|
||||||
areacache_.clear();
|
areacache_.clear();
|
||||||
rtree_.clear();
|
rtree_.clear();
|
||||||
return pck_.arrangeIndexed(std::forward<Args>(args)...);
|
return pck_.executeIndexed(std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -321,21 +320,17 @@ public:
|
||||||
pconf_.object_function = [this, bin] (
|
pconf_.object_function = [this, bin] (
|
||||||
Pile& pile,
|
Pile& pile,
|
||||||
const Item &item,
|
const Item &item,
|
||||||
double pile_area,
|
|
||||||
double norm,
|
|
||||||
const ItemGroup& rem) {
|
const ItemGroup& rem) {
|
||||||
|
|
||||||
auto result = objfunc(bin.center(), bin_area_, pile,
|
auto result = objfunc(bin.center(), bin_area_, pile,
|
||||||
pile_area, item, norm, areacache_,
|
item, norm_, areacache_, rtree_, rem);
|
||||||
rtree_,
|
|
||||||
rem);
|
|
||||||
double score = std::get<0>(result);
|
double score = std::get<0>(result);
|
||||||
auto& fullbb = std::get<1>(result);
|
auto& fullbb = std::get<1>(result);
|
||||||
|
|
||||||
auto wdiff = fullbb.width() - bin.width();
|
auto wdiff = fullbb.width() - bin.width();
|
||||||
auto hdiff = fullbb.height() - bin.height();
|
auto hdiff = fullbb.height() - bin.height();
|
||||||
if(wdiff > 0) score += std::pow(wdiff, 2) / norm;
|
if(wdiff > 0) score += std::pow(wdiff, 2) / norm_;
|
||||||
if(hdiff > 0) score += std::pow(hdiff, 2) / norm;
|
if(hdiff > 0) score += std::pow(hdiff, 2) / norm_;
|
||||||
|
|
||||||
return score;
|
return score;
|
||||||
};
|
};
|
||||||
|
@ -357,31 +352,28 @@ public:
|
||||||
pconf_.object_function = [this, &bin] (
|
pconf_.object_function = [this, &bin] (
|
||||||
Pile& pile,
|
Pile& pile,
|
||||||
const Item &item,
|
const Item &item,
|
||||||
double pile_area,
|
|
||||||
double norm,
|
|
||||||
const ItemGroup& rem) {
|
const ItemGroup& rem) {
|
||||||
|
|
||||||
auto result = objfunc(bin.center(), bin_area_, pile,
|
auto result = objfunc(bin.center(), bin_area_, pile, item, norm_,
|
||||||
pile_area, item, norm, areacache_,
|
areacache_, rtree_, rem);
|
||||||
rtree_, rem);
|
|
||||||
double score = std::get<0>(result);
|
double score = std::get<0>(result);
|
||||||
auto& fullbb = std::get<1>(result);
|
auto& fullbb = std::get<1>(result);
|
||||||
|
|
||||||
auto d = PointLike::distance(fullbb.minCorner(),
|
auto d = pl::distance(fullbb.minCorner(),
|
||||||
fullbb.maxCorner());
|
fullbb.maxCorner());
|
||||||
auto diff = d - 2*bin.radius();
|
auto diff = d - 2*bin.radius();
|
||||||
|
|
||||||
if(diff > 0) {
|
if(diff > 0) {
|
||||||
if( item.area() > 0.01*bin_area_ && item.vertexCount() < 30) {
|
if( item.area() > 0.01*bin_area_ && item.vertexCount() < 30) {
|
||||||
pile.emplace_back(item.transformedShape());
|
pile.emplace_back(item.transformedShape());
|
||||||
auto chull = ShapeLike::convexHull(pile);
|
auto chull = sl::convexHull(pile);
|
||||||
pile.pop_back();
|
pile.pop_back();
|
||||||
|
|
||||||
auto C = strategies::boundingCircle(chull);
|
auto C = strategies::boundingCircle(chull);
|
||||||
auto rdiff = C.radius() - bin.radius();
|
auto rdiff = C.radius() - bin.radius();
|
||||||
|
|
||||||
if(rdiff > 0) {
|
if(rdiff > 0) {
|
||||||
score += std::pow(rdiff, 3) / norm;
|
score += std::pow(rdiff, 3) / norm_;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -403,14 +395,11 @@ public:
|
||||||
pconf_.object_function = [this, &bin] (
|
pconf_.object_function = [this, &bin] (
|
||||||
Pile& pile,
|
Pile& pile,
|
||||||
const Item &item,
|
const Item &item,
|
||||||
double pile_area,
|
|
||||||
double norm,
|
|
||||||
const ItemGroup& rem) {
|
const ItemGroup& rem) {
|
||||||
|
|
||||||
auto binbb = ShapeLike::boundingBox(bin);
|
auto binbb = sl::boundingBox(bin);
|
||||||
auto result = objfunc(binbb.center(), bin_area_, pile,
|
auto result = objfunc(binbb.center(), bin_area_, pile, item, norm_,
|
||||||
pile_area, item, norm, areacache_,
|
areacache_, rtree_, rem);
|
||||||
rtree_, rem);
|
|
||||||
double score = std::get<0>(result);
|
double score = std::get<0>(result);
|
||||||
|
|
||||||
return score;
|
return score;
|
||||||
|
@ -430,13 +419,10 @@ public:
|
||||||
this->pconf_.object_function = [this] (
|
this->pconf_.object_function = [this] (
|
||||||
Pile& pile,
|
Pile& pile,
|
||||||
const Item &item,
|
const Item &item,
|
||||||
double pile_area,
|
|
||||||
double norm,
|
|
||||||
const ItemGroup& rem) {
|
const ItemGroup& rem) {
|
||||||
|
|
||||||
auto result = objfunc({0, 0}, 0, pile, pile_area,
|
auto result = objfunc({0, 0}, 0, pile, item, norm_,
|
||||||
item, norm, areacache_,
|
areacache_, rtree_, rem);
|
||||||
rtree_, rem);
|
|
||||||
return std::get<0>(result);
|
return std::get<0>(result);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -711,7 +697,7 @@ bool arrange(Model &model, coordf_t min_obj_distance,
|
||||||
using P = libnest2d::PolygonImpl;
|
using P = libnest2d::PolygonImpl;
|
||||||
|
|
||||||
auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
|
auto ctour = Slic3rMultiPoint_to_ClipperPath(bed);
|
||||||
P irrbed = ShapeLike::create<PolygonImpl>(std::move(ctour));
|
P irrbed = sl::create<PolygonImpl>(std::move(ctour));
|
||||||
|
|
||||||
AutoArranger<P> arrange(irrbed, min_obj_distance, progressind);
|
AutoArranger<P> arrange(irrbed, min_obj_distance, progressind);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue