Files
OrcaSlicer-bambulab/src/libnest2d/include/libnest2d/backends/clipper/geometries.hpp
tamasmeszaros d4fe7b5a96 Adding rotating calipers algorithm for minimum are bounding box rotation.
Cleanup, fix build on windows and add test for rotcalipers.

Try to fix compilation on windows

With updates from libnest2d
Another build fix.


Clean up and add comments.


adding rotcalipers test  and some cleanup


Trying to fix on OSX


Fix rotcalipers array indexing


Get rid of boost convex hull.


Adding helper function 'remove_collinear_points'


Importing new libnest2d upgrades.


Disable using __int128 in NFP on OSX
2019-06-06 14:27:07 +02:00

331 lines
8.8 KiB
C++

#ifndef CLIPPER_BACKEND_HPP
#define CLIPPER_BACKEND_HPP
#include <sstream>
#include <unordered_map>
#include <cassert>
#include <vector>
#include <iostream>
#include <libnest2d/geometry_traits.hpp>
#include <libnest2d/geometry_traits_nfp.hpp>
#include "clipper_polygon.hpp"
namespace libnest2d {
// Aliases for convinience
using PointImpl = ClipperLib::IntPoint;
using PathImpl = ClipperLib::Path;
using HoleStore = ClipperLib::Paths;
using PolygonImpl = ClipperLib::Polygon;
template<> struct ShapeTag<PolygonImpl> { using Type = PolygonTag; };
template<> struct ShapeTag<PathImpl> { using Type = PathTag; };
template<> struct ShapeTag<PointImpl> { using Type = PointTag; };
// Type of coordinate units used by Clipper. Enough to specialize for point,
// the rest of the types will work (Path, Polygon)
template<> struct CoordType<PointImpl> { using Type = ClipperLib::cInt; };
// Enough to specialize for path, it will work for multishape and Polygon
template<> struct PointType<PathImpl> { using Type = PointImpl; };
// This is crucial. CountourType refers to itself by default, so we don't have
// to secialize for clipper Path. ContourType<PathImpl>::Type is PathImpl.
template<> struct ContourType<PolygonImpl> { using Type = PathImpl; };
// The holes are contained in Clipper::Paths
template<> struct HolesContainer<PolygonImpl> { using Type = ClipperLib::Paths; };
namespace pointlike {
// Tell libnest2d how to extract the X coord from a ClipperPoint object
template<> inline TCoord<PointImpl> x(const PointImpl& p)
{
return p.X;
}
// Tell libnest2d how to extract the Y coord from a ClipperPoint object
template<> inline TCoord<PointImpl> y(const PointImpl& p)
{
return p.Y;
}
// Tell libnest2d how to extract the X coord from a ClipperPoint object
template<> inline TCoord<PointImpl>& x(PointImpl& p)
{
return p.X;
}
// Tell libnest2d how to extract the Y coord from a ClipperPoint object
template<> inline TCoord<PointImpl>& y(PointImpl& p)
{
return p.Y;
}
}
// Using the libnest2d default area implementation
#define DISABLE_BOOST_AREA
namespace shapelike {
template<> inline void offset(PolygonImpl& sh, TCoord<PointImpl> distance)
{
#define DISABLE_BOOST_OFFSET
using ClipperLib::ClipperOffset;
using ClipperLib::jtMiter;
using ClipperLib::etClosedPolygon;
using ClipperLib::Paths;
// If the input is not at least a triangle, we can not do this algorithm
if(sh.Contour.size() <= 3 ||
std::any_of(sh.Holes.begin(), sh.Holes.end(),
[](const PathImpl& p) { return p.size() <= 3; })
) throw GeometryException(GeomErr::OFFSET);
ClipperOffset offs;
Paths result;
offs.AddPath(sh.Contour, jtMiter, etClosedPolygon);
offs.AddPaths(sh.Holes, jtMiter, etClosedPolygon);
offs.Execute(result, static_cast<double>(distance));
// Offsetting reverts the orientation and also removes the last vertex
// so boost will not have a closed polygon.
bool found_the_contour = false;
for(auto& r : result) {
if(ClipperLib::Orientation(r)) {
// We don't like if the offsetting generates more than one contour
// but throwing would be an overkill. Instead, we should warn the
// caller about the inability to create correct geometries
if(!found_the_contour) {
sh.Contour = std::move(r);
ClipperLib::ReversePath(sh.Contour);
auto front_p = sh.Contour.front();
sh.Contour.emplace_back(std::move(front_p));
found_the_contour = true;
} else {
dout() << "Warning: offsetting result is invalid!";
/* TODO warning */
}
} else {
// TODO If there are multiple contours we can't be sure which hole
// belongs to the first contour. (But in this case the situation is
// bad enough to let it go...)
sh.Holes.emplace_back(std::move(r));
ClipperLib::ReversePath(sh.Holes.back());
auto front_p = sh.Holes.back().front();
sh.Holes.back().emplace_back(std::move(front_p));
}
}
}
// Tell libnest2d how to make string out of a ClipperPolygon object
template<> inline std::string toString(const PolygonImpl& sh)
{
std::stringstream ss;
ss << "Contour {\n";
for(auto p : sh.Contour) {
ss << "\t" << p.X << " " << p.Y << "\n";
}
ss << "}\n";
for(auto& h : sh.Holes) {
ss << "Holes {\n";
for(auto p : h) {
ss << "\t{\n";
ss << "\t\t" << p.X << " " << p.Y << "\n";
ss << "\t}\n";
}
ss << "}\n";
}
return ss.str();
}
template<>
inline PolygonImpl create(const PathImpl& path, const HoleStore& holes)
{
PolygonImpl p;
p.Contour = path;
p.Holes = holes;
return p;
}
template<> inline PolygonImpl create( PathImpl&& path, HoleStore&& holes) {
PolygonImpl p;
p.Contour.swap(path);
p.Holes.swap(holes);
return p;
}
template<>
inline const THolesContainer<PolygonImpl>& holes(const PolygonImpl& sh)
{
return sh.Holes;
}
template<> inline THolesContainer<PolygonImpl>& holes(PolygonImpl& sh)
{
return sh.Holes;
}
template<>
inline TContour<PolygonImpl>& hole(PolygonImpl& sh, unsigned long idx)
{
return sh.Holes[idx];
}
template<>
inline const TContour<PolygonImpl>& hole(const PolygonImpl& sh,
unsigned long idx)
{
return sh.Holes[idx];
}
template<> inline size_t holeCount(const PolygonImpl& sh)
{
return sh.Holes.size();
}
template<> inline PathImpl& contour(PolygonImpl& sh)
{
return sh.Contour;
}
template<>
inline const PathImpl& contour(const PolygonImpl& sh)
{
return sh.Contour;
}
#define DISABLE_BOOST_TRANSLATE
template<>
inline void translate(PolygonImpl& sh, const PointImpl& offs)
{
for(auto& p : sh.Contour) { p += offs; }
for(auto& hole : sh.Holes) for(auto& p : hole) { p += offs; }
}
#define DISABLE_BOOST_ROTATE
template<>
inline void rotate(PolygonImpl& sh, const Radians& rads)
{
using Coord = TCoord<PointImpl>;
auto cosa = rads.cos();
auto sina = rads.sin();
for(auto& p : sh.Contour) {
p = {
static_cast<Coord>(p.X * cosa - p.Y * sina),
static_cast<Coord>(p.X * sina + p.Y * cosa)
};
}
for(auto& hole : sh.Holes) for(auto& p : hole) {
p = {
static_cast<Coord>(p.X * cosa - p.Y * sina),
static_cast<Coord>(p.X * sina + p.Y * cosa)
};
}
}
} // namespace shapelike
#define DISABLE_BOOST_NFP_MERGE
inline TMultiShape<PolygonImpl> clipper_execute(
ClipperLib::Clipper& clipper,
ClipperLib::ClipType clipType,
ClipperLib::PolyFillType subjFillType = ClipperLib::pftEvenOdd,
ClipperLib::PolyFillType clipFillType = ClipperLib::pftEvenOdd)
{
TMultiShape<PolygonImpl> retv;
ClipperLib::PolyTree result;
clipper.Execute(clipType, result, subjFillType, clipFillType);
retv.reserve(static_cast<size_t>(result.Total()));
std::function<void(ClipperLib::PolyNode*, PolygonImpl&)> processHole;
auto processPoly = [&retv, &processHole](ClipperLib::PolyNode *pptr) {
PolygonImpl poly;
poly.Contour.swap(pptr->Contour);
assert(!pptr->IsHole());
if(pptr->IsOpen()) {
auto front_p = poly.Contour.front();
poly.Contour.emplace_back(front_p);
}
for(auto h : pptr->Childs) { processHole(h, poly); }
retv.push_back(poly);
};
processHole = [&processPoly](ClipperLib::PolyNode *pptr, PolygonImpl& poly)
{
poly.Holes.emplace_back(std::move(pptr->Contour));
assert(pptr->IsHole());
if(pptr->IsOpen()) {
auto front_p = poly.Holes.back().front();
poly.Holes.back().emplace_back(front_p);
}
for(auto c : pptr->Childs) processPoly(c);
};
auto traverse = [&processPoly] (ClipperLib::PolyNode *node)
{
for(auto ch : node->Childs) processPoly(ch);
};
traverse(&result);
return retv;
}
namespace nfp {
template<> inline TMultiShape<PolygonImpl>
merge(const TMultiShape<PolygonImpl>& shapes)
{
ClipperLib::Clipper clipper(ClipperLib::ioReverseSolution);
bool closed = true;
bool valid = true;
for(auto& path : shapes) {
valid &= clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
for(auto& h : path.Holes)
valid &= clipper.AddPath(h, ClipperLib::ptSubject, closed);
}
if(!valid) throw GeometryException(GeomErr::MERGE);
return clipper_execute(clipper, ClipperLib::ctUnion, ClipperLib::pftNegative);
}
}
}
#define DISABLE_BOOST_CONVEX_HULL
//#define DISABLE_BOOST_SERIALIZE
//#define DISABLE_BOOST_UNSERIALIZE
// All other operators and algorithms are implemented with boost
#include <libnest2d/utils/boost_alg.hpp>
#endif // CLIPPER_BACKEND_HPP