Merge branch 'tm_vb_clipper_eigen'

This commit is contained in:
Vojtech Bubnik 2021-04-23 11:44:25 +02:00
commit 9f4e9da5c6
50 changed files with 1209 additions and 1291 deletions

View File

@ -268,8 +268,6 @@ set(LIBDIR_BIN ${CMAKE_CURRENT_BINARY_DIR}/src)
include_directories(${LIBDIR}) include_directories(${LIBDIR})
# For generated header files # For generated header files
include_directories(${LIBDIR_BIN}/platform) include_directories(${LIBDIR_BIN}/platform)
# For libslic3r.h
include_directories(${LIBDIR}/clipper)
if(WIN32) if(WIN32)
add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS) add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS)

View File

@ -2,8 +2,9 @@ project(clipper)
cmake_minimum_required(VERSION 2.6) cmake_minimum_required(VERSION 2.6)
add_library(clipper STATIC add_library(clipper STATIC
clipper.cpp # We are using ClipperLib compiled as part of the libslic3r project using Slic3r::Point as its base type.
clipper.hpp # clipper.cpp
# clipper.hpp
clipper_z.cpp clipper_z.cpp
clipper_z.hpp clipper_z.hpp
) )

File diff suppressed because it is too large Load Diff

View File

@ -37,10 +37,12 @@
#include <inttypes.h> #include <inttypes.h>
#include <functional> #include <functional>
#include <Eigen/Geometry>
#define CLIPPER_VERSION "6.2.6" #define CLIPPER_VERSION "6.2.6"
//use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. //CLIPPERLIB_USE_XYZ: adds a Z member to IntPoint. Adds a minor cost to perfomance.
//#define use_xyz //#define CLIPPERLIB_USE_XYZ
//use_lines: Enables line clipping. Adds a very minor cost to performance. //use_lines: Enables line clipping. Adds a very minor cost to performance.
#define use_lines #define use_lines
@ -57,11 +59,15 @@
#include <functional> #include <functional>
#include <queue> #include <queue>
#ifdef use_xyz #ifdef CLIPPERLIB_NAMESPACE_PREFIX
namespace ClipperLib_Z { namespace CLIPPERLIB_NAMESPACE_PREFIX {
#else /* use_xyz */ #endif // CLIPPERLIB_NAMESPACE_PREFIX
namespace ClipperLib {
#endif /* use_xyz */ #ifdef CLIPPERLIB_USE_XYZ
namespace ClipperLib_Z {
#else
namespace ClipperLib {
#endif
enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
enum PolyType { ptSubject, ptClip }; enum PolyType { ptSubject, ptClip };
@ -88,29 +94,24 @@ enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
static constexpr cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; static constexpr cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
#endif // CLIPPERLIB_INT32 #endif // CLIPPERLIB_INT32
struct IntPoint { #ifdef CLIPPERLIB_INTPOINT_TYPE
cInt X; using IntPoint = CLIPPERLIB_INTPOINT_TYPE;
cInt Y; #else // CLIPPERLIB_INTPOINT_TYPE
#ifdef use_xyz using IntPoint = Eigen::Matrix<cInt,
cInt Z; #ifdef CLIPPERLIB_USE_XYZ
IntPoint(cInt x = 0, cInt y = 0, cInt z = 0): X(x), Y(y), Z(z) {}; 3
#else #else // CLIPPERLIB_USE_XYZ
IntPoint(cInt x = 0, cInt y = 0): X(x), Y(y) {}; 2
#endif #endif // CLIPPERLIB_USE_XYZ
, 1, Eigen::DontAlign>;
#endif // CLIPPERLIB_INTPOINT_TYPE
using DoublePoint = Eigen::Matrix<double, 2, 1, Eigen::DontAlign>;
friend inline bool operator== (const IntPoint& a, const IntPoint& b)
{
return a.X == b.X && a.Y == b.Y;
}
friend inline bool operator!= (const IntPoint& a, const IntPoint& b)
{
return a.X != b.X || a.Y != b.Y;
}
};
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
typedef std::vector< IntPoint > Path; typedef std::vector<IntPoint> Path;
typedef std::vector< Path > Paths; typedef std::vector<Path> Paths;
inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;} inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;}
inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;} inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;}
@ -119,16 +120,9 @@ std::ostream& operator <<(std::ostream &s, const IntPoint &p);
std::ostream& operator <<(std::ostream &s, const Path &p); std::ostream& operator <<(std::ostream &s, const Path &p);
std::ostream& operator <<(std::ostream &s, const Paths &p); std::ostream& operator <<(std::ostream &s, const Paths &p);
struct DoublePoint
{
double X;
double Y;
DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {}
DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {}
};
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#ifdef use_xyz #ifdef CLIPPERLIB_USE_XYZ
typedef std::function<void(const IntPoint& e1bot, const IntPoint& e1top, const IntPoint& e2bot, const IntPoint& e2top, IntPoint& pt)> ZFillCallback; typedef std::function<void(const IntPoint& e1bot, const IntPoint& e1top, const IntPoint& e2bot, const IntPoint& e2top, IntPoint& pt)> ZFillCallback;
#endif #endif
@ -269,11 +263,11 @@ enum EdgeSide { esLeft = 1, esRight = 2};
}; };
// Point of an output polygon. // Point of an output polygon.
// 36B on 64bit system without use_xyz. // 36B on 64bit system without CLIPPERLIB_USE_XYZ.
struct OutPt { struct OutPt {
// 4B // 4B
int Idx; int Idx;
// 16B without use_xyz / 24B with use_xyz // 16B without CLIPPERLIB_USE_XYZ / 24B with CLIPPERLIB_USE_XYZ
IntPoint Pt; IntPoint Pt;
// 4B on 32bit system, 8B on 64bit system // 4B on 32bit system, 8B on 64bit system
OutPt *Next; OutPt *Next;
@ -368,7 +362,7 @@ public:
bool StrictlySimple() const {return m_StrictSimple;}; bool StrictlySimple() const {return m_StrictSimple;};
void StrictlySimple(bool value) {m_StrictSimple = value;}; void StrictlySimple(bool value) {m_StrictSimple = value;};
//set the callback function for z value filling on intersections (otherwise Z is 0) //set the callback function for z value filling on intersections (otherwise Z is 0)
#ifdef use_xyz #ifdef CLIPPERLIB_USE_XYZ
void ZFillFunction(ZFillCallback zFillFunc) { m_ZFill = zFillFunc; } void ZFillFunction(ZFillCallback zFillFunc) { m_ZFill = zFillFunc; }
#endif #endif
protected: protected:
@ -401,7 +395,7 @@ private:
// Does the result go to a PolyTree or Paths? // Does the result go to a PolyTree or Paths?
bool m_UsingPolyTree; bool m_UsingPolyTree;
bool m_StrictSimple; bool m_StrictSimple;
#ifdef use_xyz #ifdef CLIPPERLIB_USE_XYZ
ZFillCallback m_ZFill; //custom callback ZFillCallback m_ZFill; //custom callback
#endif #endif
void SetWindingCount(TEdge& edge) const; void SetWindingCount(TEdge& edge) const;
@ -454,7 +448,7 @@ private:
void DoSimplePolygons(); void DoSimplePolygons();
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const; void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec) const;
void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const; void FixupFirstLefts2(OutRec* OldOutRec, OutRec* NewOutRec) const;
#ifdef use_xyz #ifdef CLIPPERLIB_USE_XYZ
void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2);
#endif #endif
}; };
@ -506,6 +500,8 @@ class clipperException : public std::exception
} //ClipperLib namespace } //ClipperLib namespace
#ifdef CLIPPERLIB_NAMESPACE_PREFIX
} // namespace CLIPPERLIB_NAMESPACE_PREFIX
#endif // CLIPPERLIB_NAMESPACE_PREFIX
#endif //clipper_hpp #endif //clipper_hpp

View File

@ -1,7 +1,7 @@
// Hackish wrapper around the ClipperLib library to compile the Clipper library with the Z support. // Hackish wrapper around the ClipperLib library to compile the Clipper library with the Z support.
// Enable the Z coordinate support. // Enable the Z coordinate support.
#define use_xyz #define CLIPPERLIB_USE_XYZ
// and let it compile // and let it compile
#include "clipper.cpp" #include "clipper.cpp"

View File

@ -2,17 +2,17 @@
#ifndef clipper_z_hpp #ifndef clipper_z_hpp
#ifdef clipper_hpp #ifdef clipper_hpp
#error "You should include the clipper_z.hpp first" #error "You should include clipper_z.hpp before clipper.hpp"
#endif #endif
#define clipper_z_hpp #define clipper_z_hpp
// Enable the Z coordinate support. // Enable the Z coordinate support.
#define use_xyz #define CLIPPERLIB_USE_XYZ
#include "clipper.hpp" #include "clipper.hpp"
#undef clipper_hpp #undef clipper_hpp
#undef use_xyz #undef CLIPPERLIB_USE_XYZ
#endif // clipper_z_hpp #endif // clipper_z_hpp

View File

@ -12,11 +12,8 @@ set(LIBNEST2D_SRCFILES
include/libnest2d/placers/bottomleftplacer.hpp include/libnest2d/placers/bottomleftplacer.hpp
include/libnest2d/placers/nfpplacer.hpp include/libnest2d/placers/nfpplacer.hpp
include/libnest2d/selections/selection_boilerplate.hpp include/libnest2d/selections/selection_boilerplate.hpp
#include/libnest2d/selections/filler.hpp
include/libnest2d/selections/firstfit.hpp include/libnest2d/selections/firstfit.hpp
#include/libnest2d/selections/djd_heuristic.hpp include/libnest2d/backends/libslic3r/geometries.hpp
include/libnest2d/backends/clipper/geometries.hpp
include/libnest2d/backends/clipper/clipper_polygon.hpp
include/libnest2d/optimizers/nlopt/nlopt_boilerplate.hpp include/libnest2d/optimizers/nlopt/nlopt_boilerplate.hpp
include/libnest2d/optimizers/nlopt/simplex.hpp include/libnest2d/optimizers/nlopt/simplex.hpp
include/libnest2d/optimizers/nlopt/subplex.hpp include/libnest2d/optimizers/nlopt/subplex.hpp
@ -27,5 +24,5 @@ set(LIBNEST2D_SRCFILES
add_library(libnest2d STATIC ${LIBNEST2D_SRCFILES}) add_library(libnest2d STATIC ${LIBNEST2D_SRCFILES})
target_include_directories(libnest2d PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include) target_include_directories(libnest2d PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)
target_link_libraries(libnest2d PUBLIC clipper NLopt::nlopt TBB::tbb Boost::boost) target_link_libraries(libnest2d PUBLIC NLopt::nlopt TBB::tbb Boost::boost libslic3r)
target_compile_definitions(libnest2d PUBLIC LIBNEST2D_THREADING_tbb LIBNEST2D_STATIC LIBNEST2D_OPTIMIZER_nlopt LIBNEST2D_GEOMETRIES_clipper) target_compile_definitions(libnest2d PUBLIC LIBNEST2D_THREADING_tbb LIBNEST2D_STATIC LIBNEST2D_OPTIMIZER_nlopt LIBNEST2D_GEOMETRIES_libslic3r)

View File

@ -1,72 +0,0 @@
#ifndef CLIPPER_POLYGON_HPP
#define CLIPPER_POLYGON_HPP
#include <clipper.hpp>
namespace ClipperLib {
struct Polygon {
Path Contour;
Paths Holes;
inline Polygon() = default;
inline explicit Polygon(const Path& cont): Contour(cont) {}
// inline explicit Polygon(const Paths& holes):
// Holes(holes) {}
inline Polygon(const Path& cont, const Paths& holes):
Contour(cont), Holes(holes) {}
inline explicit Polygon(Path&& cont): Contour(std::move(cont)) {}
// inline explicit Polygon(Paths&& holes): Holes(std::move(holes)) {}
inline Polygon(Path&& cont, Paths&& holes):
Contour(std::move(cont)), Holes(std::move(holes)) {}
};
inline IntPoint& operator +=(IntPoint& p, const IntPoint& pa ) {
// This could be done with SIMD
p.X += pa.X;
p.Y += pa.Y;
return p;
}
inline IntPoint operator+(const IntPoint& p1, const IntPoint& p2) {
IntPoint ret = p1;
ret += p2;
return ret;
}
inline IntPoint& operator -=(IntPoint& p, const IntPoint& pa ) {
p.X -= pa.X;
p.Y -= pa.Y;
return p;
}
inline IntPoint operator -(const IntPoint& p ) {
IntPoint ret = p;
ret.X = -ret.X;
ret.Y = -ret.Y;
return ret;
}
inline IntPoint operator-(const IntPoint& p1, const IntPoint& p2) {
IntPoint ret = p1;
ret -= p2;
return ret;
}
inline IntPoint& operator *=(IntPoint& p, const IntPoint& pa ) {
p.X *= pa.X;
p.Y *= pa.Y;
return p;
}
inline IntPoint operator*(const IntPoint& p1, const IntPoint& p2) {
IntPoint ret = p1;
ret *= p2;
return ret;
}
}
#endif // CLIPPER_POLYGON_HPP

View File

@ -1,356 +0,0 @@
#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;
static const constexpr ClipperLib::cInt MM_IN_COORDS = 1000000;
};
// 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 ClipperLib::cInt x(const PointImpl& p)
{
return p.X;
}
// Tell libnest2d how to extract the Y coord from a ClipperPoint object
template<> inline ClipperLib::cInt y(const PointImpl& p)
{
return p.Y;
}
// Tell libnest2d how to extract the X coord from a ClipperPoint object
template<> inline ClipperLib::cInt& x(PointImpl& p)
{
return p.X;
}
// Tell libnest2d how to extract the Y coord from a ClipperPoint object
template<> inline ClipperLib::cInt& 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, const PolygonTag&)
{
#define DISABLE_BOOST_OFFSET
using ClipperLib::ClipperOffset;
using ClipperLib::jtSquare;
using ClipperLib::etClosedPolygon;
using ClipperLib::Paths;
Paths result;
try {
ClipperOffset offs;
offs.AddPath(sh.Contour, jtSquare, etClosedPolygon);
offs.AddPaths(sh.Holes, jtSquare, etClosedPolygon);
offs.Execute(result, static_cast<double>(distance));
} catch (ClipperLib::clipperException &) {
throw GeometryException(GeomErr::OFFSET);
}
// Offsetting reverts the orientation and also removes the last vertex
// so boost will not have a closed polygon.
// we plan to replace contours
sh.Holes.clear();
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));
}
}
}
template<>
inline void offset(PathImpl& sh, TCoord<PointImpl> distance, const PathTag&)
{
PolygonImpl p(std::move(sh));
offset(p, distance, PolygonTag());
sh = p.Contour;
}
// 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(!poly.Contour.empty() ) {
auto front_p = poly.Contour.front();
auto &back_p = poly.Contour.back();
if(front_p.X != back_p.X || front_p.Y != back_p.X)
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(!poly.Contour.empty() ) {
auto front_p = poly.Contour.front();
auto &back_p = poly.Contour.back();
if(front_p.X != back_p.X || front_p.Y != back_p.X)
poly.Contour.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
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4244)
#pragma warning(disable: 4267)
#endif
// All other operators and algorithms are implemented with boost
#include <libnest2d/utils/boost_alg.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // CLIPPER_BACKEND_HPP

View File

@ -0,0 +1,283 @@
#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 <libslic3r/ExPolygon.hpp>
#include <libslic3r/ClipperUtils.hpp>
namespace Slic3r {
template<class T, class En = void> struct IsVec_ : public std::false_type {};
template<class T> struct IsVec_< Vec<2, T> >: public std::true_type {};
template<class T>
static constexpr const bool IsVec = IsVec_<libnest2d::remove_cvref_t<T>>::value;
template<class T, class O> using VecOnly = std::enable_if_t<IsVec<T>, O>;
inline Point operator+(const Point& p1, const Point& p2) {
Point ret = p1;
ret += p2;
return ret;
}
inline Point operator -(const Point& p ) {
Point ret = p;
ret.x() = -ret.x();
ret.y() = -ret.y();
return ret;
}
inline Point operator-(const Point& p1, const Point& p2) {
Point ret = p1;
ret -= p2;
return ret;
}
inline Point& operator *=(Point& p, const Point& pa ) {
p.x() *= pa.x();
p.y() *= pa.y();
return p;
}
inline Point operator*(const Point& p1, const Point& p2) {
Point ret = p1;
ret *= p2;
return ret;
}
} // namespace Slic3r
namespace libnest2d {
template<class T> using Vec = Slic3r::Vec<2, T>;
// Aliases for convinience
using PointImpl = Slic3r::Point;
using PathImpl = Slic3r::Polygon;
using HoleStore = Slic3r::Polygons;
using PolygonImpl = Slic3r::ExPolygon;
template<> struct ShapeTag<Slic3r::Vec2crd> { using Type = PointTag; };
template<> struct ShapeTag<Slic3r::Point> { using Type = PointTag; };
template<> struct ShapeTag<std::vector<Slic3r::Vec2crd>> { using Type = PathTag; };
template<> struct ShapeTag<Slic3r::Polygon> { using Type = PathTag; };
template<> struct ShapeTag<Slic3r::Points> { using Type = PathTag; };
template<> struct ShapeTag<Slic3r::ExPolygon> { using Type = PolygonTag; };
template<> struct ShapeTag<Slic3r::ExPolygons> { using Type = MultiPolygonTag; };
// Type of coordinate units used by Clipper. Enough to specialize for point,
// the rest of the types will work (Path, Polygon)
template<> struct CoordType<Slic3r::Point> {
using Type = coord_t;
static const constexpr coord_t MM_IN_COORDS = 1000000;
};
template<> struct CoordType<Slic3r::Vec2crd> {
using Type = coord_t;
static const constexpr coord_t MM_IN_COORDS = 1000000;
};
// Enough to specialize for path, it will work for multishape and Polygon
template<> struct PointType<std::vector<Slic3r::Vec2crd>> { using Type = Slic3r::Vec2crd; };
template<> struct PointType<Slic3r::Polygon> { using Type = Slic3r::Point; };
template<> struct PointType<Slic3r::Points> { using Type = Slic3r::Point; };
// 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<Slic3r::ExPolygon> { using Type = Slic3r::Polygon; };
// The holes are contained in Clipper::Paths
template<> struct HolesContainer<Slic3r::ExPolygon> { using Type = Slic3r::Polygons; };
template<>
struct OrientationType<Slic3r::Polygon> {
static const constexpr Orientation Value = Orientation::COUNTER_CLOCKWISE;
};
template<>
struct OrientationType<Slic3r::Points> {
static const constexpr Orientation Value = Orientation::COUNTER_CLOCKWISE;
};
template<>
struct ClosureType<Slic3r::Polygon> {
static const constexpr Closure Value = Closure::OPEN;
};
template<>
struct ClosureType<Slic3r::Points> {
static const constexpr Closure Value = Closure::OPEN;
};
template<> struct MultiShape<Slic3r::ExPolygon> { using Type = Slic3r::ExPolygons; };
template<> struct ContourType<Slic3r::ExPolygons> { using Type = Slic3r::Polygon; };
// Using the libnest2d default area implementation
#define DISABLE_BOOST_AREA
namespace shapelike {
template<>
inline void offset(Slic3r::ExPolygon& sh, coord_t distance, const PolygonTag&)
{
#define DISABLE_BOOST_OFFSET
auto res = Slic3r::offset_ex(sh, distance, Slic3r::ClipperLib::jtSquare);
if (!res.empty()) sh = res.front();
}
template<>
inline void offset(Slic3r::Polygon& sh, coord_t distance, const PathTag&)
{
auto res = Slic3r::offset(sh, distance, Slic3r::ClipperLib::jtSquare);
if (!res.empty()) sh = res.front();
}
// Tell libnest2d how to make string out of a ClipperPolygon object
template<> inline std::string toString(const Slic3r::ExPolygon& sh)
{
std::stringstream ss;
ss << "Contour {\n";
for(auto &p : sh.contour.points) {
ss << "\t" << p.x() << " " << p.y() << "\n";
}
ss << "}\n";
for(auto& h : sh.holes) {
ss << "Holes {\n";
for(auto p : h.points) {
ss << "\t{\n";
ss << "\t\t" << p.x() << " " << p.y() << "\n";
ss << "\t}\n";
}
ss << "}\n";
}
return ss.str();
}
template<>
inline Slic3r::ExPolygon create(const Slic3r::Polygon& path, const Slic3r::Polygons& holes)
{
Slic3r::ExPolygon p;
p.contour = path;
p.holes = holes;
return p;
}
template<> inline Slic3r::ExPolygon create(Slic3r::Polygon&& path, Slic3r::Polygons&& holes) {
Slic3r::ExPolygon p;
p.contour.points.swap(path.points);
p.holes.swap(holes);
return p;
}
template<>
inline const THolesContainer<PolygonImpl>& holes(const Slic3r::ExPolygon& sh)
{
return sh.holes;
}
template<> inline THolesContainer<PolygonImpl>& holes(Slic3r::ExPolygon& sh)
{
return sh.holes;
}
template<>
inline Slic3r::Polygon& hole(Slic3r::ExPolygon& sh, unsigned long idx)
{
return sh.holes[idx];
}
template<>
inline const Slic3r::Polygon& hole(const Slic3r::ExPolygon& sh, unsigned long idx)
{
return sh.holes[idx];
}
template<> inline size_t holeCount(const Slic3r::ExPolygon& sh)
{
return sh.holes.size();
}
template<> inline Slic3r::Polygon& contour(Slic3r::ExPolygon& sh)
{
return sh.contour;
}
template<>
inline const Slic3r::Polygon& contour(const Slic3r::ExPolygon& sh)
{
return sh.contour;
}
template<>
inline void reserve(Slic3r::Polygon& p, size_t vertex_capacity, const PathTag&)
{
p.points.reserve(vertex_capacity);
}
template<>
inline void addVertex(Slic3r::Polygon& sh, const PathTag&, const Slic3r::Point &p)
{
sh.points.emplace_back(p);
}
#define DISABLE_BOOST_TRANSLATE
template<>
inline void translate(Slic3r::ExPolygon& sh, const Slic3r::Point& offs)
{
sh.translate(offs);
}
#define DISABLE_BOOST_ROTATE
template<>
inline void rotate(Slic3r::ExPolygon& sh, const Radians& rads)
{
sh.rotate(rads);
}
} // namespace shapelike
namespace nfp {
#define DISABLE_BOOST_NFP_MERGE
template<>
inline TMultiShape<PolygonImpl> merge(const TMultiShape<PolygonImpl>& shapes)
{
return Slic3r::union_ex(shapes);
}
} // namespace nfp
} // namespace libnest2d
#define DISABLE_BOOST_CONVEX_HULL
//#define DISABLE_BOOST_SERIALIZE
//#define DISABLE_BOOST_UNSERIALIZE
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4244)
#pragma warning(disable: 4267)
#endif
// All other operators and algorithms are implemented with boost
#include <libnest2d/utils/boost_alg.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // CLIPPER_BACKEND_HPP

View File

@ -128,22 +128,32 @@ template<class S> struct ContourType<DefaultMultiShape<S>> {
using Type = typename ContourType<S>::Type; using Type = typename ContourType<S>::Type;
}; };
enum class Orientation { enum class Orientation { CLOCKWISE, COUNTER_CLOCKWISE };
CLOCKWISE,
COUNTER_CLOCKWISE
};
template<class S> template<class S>
struct OrientationType { struct OrientationType {
// Default Polygon orientation that the library expects // Default Polygon orientation that the library expects
static const Orientation Value = Orientation::CLOCKWISE; static const constexpr Orientation Value = Orientation::CLOCKWISE;
}; };
template<class T> inline /*constexpr*/ bool is_clockwise() { template<class T> inline constexpr bool is_clockwise() {
return OrientationType<TContour<T>>::Value == Orientation::CLOCKWISE; return OrientationType<TContour<T>>::Value == Orientation::CLOCKWISE;
} }
template<class T>
inline const constexpr Orientation OrientationTypeV =
OrientationType<TContour<T>>::Value;
enum class Closure { OPEN, CLOSED };
template<class S> struct ClosureType {
static const constexpr Closure Value = Closure::CLOSED;
};
template<class T>
inline const constexpr Closure ClosureTypeV =
ClosureType<TContour<T>>::Value;
/** /**
* \brief A point pair base class for other point pairs (segment, box, ...). * \brief A point pair base class for other point pairs (segment, box, ...).
@ -587,9 +597,9 @@ inline void reserve(RawPath& p, size_t vertex_capacity, const PathTag&)
} }
template<class S, class...Args> template<class S, class...Args>
inline void addVertex(S& sh, const PathTag&, Args...args) inline void addVertex(S& sh, const PathTag&, const TPoint<S> &p)
{ {
sh.emplace_back(std::forward<Args>(args)...); sh.emplace_back(p);
} }
template<class S, class Fn> template<class S, class Fn>
@ -841,9 +851,9 @@ template<class P> auto rbegin(P& p) -> decltype(_backward(end(p)))
return _backward(end(p)); return _backward(end(p));
} }
template<class P> auto rcbegin(const P& p) -> decltype(_backward(end(p))) template<class P> auto rcbegin(const P& p) -> decltype(_backward(cend(p)))
{ {
return _backward(end(p)); return _backward(cend(p));
} }
template<class P> auto rend(P& p) -> decltype(_backward(begin(p))) template<class P> auto rend(P& p) -> decltype(_backward(begin(p)))
@ -873,16 +883,16 @@ inline void reserve(T& sh, size_t vertex_capacity) {
reserve(sh, vertex_capacity, Tag<T>()); reserve(sh, vertex_capacity, Tag<T>());
} }
template<class S, class...Args> template<class S>
inline void addVertex(S& sh, const PolygonTag&, Args...args) inline void addVertex(S& sh, const PolygonTag&, const TPoint<S> &p)
{ {
addVertex(contour(sh), PathTag(), std::forward<Args>(args)...); addVertex(contour(sh), PathTag(), p);
} }
template<class S, class...Args> // Tag dispatcher template<class S> // Tag dispatcher
inline void addVertex(S& sh, Args...args) inline void addVertex(S& sh, const TPoint<S> &p)
{ {
addVertex(sh, Tag<S>(), std::forward<Args>(args)...); addVertex(sh, Tag<S>(), p);
} }
template<class S> template<class S>

View File

@ -28,7 +28,7 @@ inline void buildPolygon(const EdgeList& edgelist,
auto& rsh = sl::contour(rpoly); auto& rsh = sl::contour(rpoly);
sl::reserve(rsh, 2*edgelist.size()); sl::reserve(rsh, 2 * edgelist.size());
// Add the two vertices from the first edge into the final polygon. // Add the two vertices from the first edge into the final polygon.
sl::addVertex(rsh, edgelist.front().first()); sl::addVertex(rsh, edgelist.front().first());
@ -57,7 +57,6 @@ inline void buildPolygon(const EdgeList& edgelist,
tmp = std::next(tmp); tmp = std::next(tmp);
} }
} }
template<class Container, class Iterator = typename Container::iterator> template<class Container, class Iterator = typename Container::iterator>
@ -214,17 +213,24 @@ inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
// Reserve the needed memory // Reserve the needed memory
edgelist.reserve(cap); edgelist.reserve(cap);
sl::reserve(rsh, static_cast<unsigned long>(cap)); sl::reserve(rsh, static_cast<unsigned long>(cap));
auto add_edge = [&edgelist](const Vertex &v1, const Vertex &v2) {
Edge e{v1, v2};
if (e.sqlength() > 0)
edgelist.emplace_back(e);
};
{ // place all edges from sh into edgelist { // place all edges from sh into edgelist
auto first = sl::cbegin(sh); auto first = sl::cbegin(sh);
auto next = std::next(first); auto next = std::next(first);
while(next != sl::cend(sh)) { while(next != sl::cend(sh)) {
if (pl::magnsq(*next - *first) > 0) add_edge(*(first), *(next));
edgelist.emplace_back(*(first), *(next));
++first; ++next; ++first; ++next;
} }
if constexpr (ClosureTypeV<RawShape> == Closure::OPEN)
add_edge(*sl::rcbegin(sh), *sl::cbegin(sh));
} }
{ // place all edges from other into edgelist { // place all edges from other into edgelist
@ -232,17 +238,19 @@ inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
auto next = std::next(first); auto next = std::next(first);
while(next != sl::cend(other)) { while(next != sl::cend(other)) {
if (pl::magnsq(*next - *first) > 0) add_edge(*(next), *(first));
edgelist.emplace_back(*(next), *(first));
++first; ++next; ++first; ++next;
} }
if constexpr (ClosureTypeV<RawShape> == Closure::OPEN)
add_edge(*sl::cbegin(other), *sl::rcbegin(other));
} }
std::sort(edgelist.begin(), edgelist.end(), std::sort(edgelist.begin(), edgelist.end(),
[](const Edge& e1, const Edge& e2) [](const Edge& e1, const Edge& e2)
{ {
Vertex ax(1, 0); // Unit vector for the X axis const Vertex ax(1, 0); // Unit vector for the X axis
// get cectors from the edges // get cectors from the edges
Vertex p1 = e1.second() - e1.first(); Vertex p1 = e1.second() - e1.first();
@ -289,11 +297,17 @@ inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
auto pcos1 = Ratio(lcos[0]) / lsq1 * sign * lcos[0]; auto pcos1 = Ratio(lcos[0]) / lsq1 * sign * lcos[0];
auto pcos2 = Ratio(lcos[1]) / lsq2 * sign * lcos[1]; auto pcos2 = Ratio(lcos[1]) / lsq2 * sign * lcos[1];
if constexpr (is_clockwise<RawShape>())
return q[0] < 2 ? pcos1 < pcos2 : pcos1 > pcos2; return q[0] < 2 ? pcos1 < pcos2 : pcos1 > pcos2;
else
return q[0] < 2 ? pcos1 > pcos2 : pcos1 < pcos2;
} }
// If in different quadrants, compare the quadrant indices only. // If in different quadrants, compare the quadrant indices only.
if constexpr (is_clockwise<RawShape>())
return q[0] > q[1]; return q[0] > q[1];
else
return q[0] < q[1];
}); });
__nfp::buildPolygon(edgelist, rsh, top_nfp); __nfp::buildPolygon(edgelist, rsh, top_nfp);

View File

@ -7,6 +7,10 @@
#include <libnest2d/backends/clipper/geometries.hpp> #include <libnest2d/backends/clipper/geometries.hpp>
#endif #endif
#ifdef LIBNEST2D_GEOMETRIES_libslic3r
#include <libnest2d/backends/libslic3r/geometries.hpp>
#endif
#ifdef LIBNEST2D_OPTIMIZER_nlopt #ifdef LIBNEST2D_OPTIMIZER_nlopt
// We include the stock optimizers for local and global optimization // We include the stock optimizers for local and global optimization
#include <libnest2d/optimizers/nlopt/subplex.hpp> // Local subplex for NfpPlacer #include <libnest2d/optimizers/nlopt/subplex.hpp> // Local subplex for NfpPlacer

View File

@ -96,7 +96,7 @@ public:
* @return The orientation type identifier for the _Item type. * @return The orientation type identifier for the _Item type.
*/ */
static BP2D_CONSTEXPR Orientation orientation() { static BP2D_CONSTEXPR Orientation orientation() {
return OrientationType<RawShape>::Value; return OrientationType<TContour<RawShape>>::Value;
} }
/** /**
@ -446,44 +446,32 @@ private:
} }
}; };
template<class Sh> Sh create_rect(TCoord<Sh> width, TCoord<Sh> height)
{
auto sh = sl::create<Sh>(
{{0, 0}, {0, height}, {width, height}, {width, 0}});
if constexpr (ClosureTypeV<Sh> == Closure::CLOSED)
sl::addVertex(sh, {0, 0});
if constexpr (OrientationTypeV<Sh> == Orientation::COUNTER_CLOCKWISE)
std::reverse(sl::begin(sh), sl::end(sh));
return sh;
}
/** /**
* \brief Subclass of _Item for regular rectangle items. * \brief Subclass of _Item for regular rectangle items.
*/ */
template<class RawShape> template<class Sh>
class _Rectangle: public _Item<RawShape> { class _Rectangle: public _Item<Sh> {
using _Item<RawShape>::vertex; using _Item<Sh>::vertex;
using TO = Orientation; using TO = Orientation;
public: public:
using Unit = TCoord<TPoint<RawShape>>; using Unit = TCoord<Sh>;
template<TO o = OrientationType<RawShape>::Value> inline _Rectangle(Unit w, Unit h): _Item<Sh>{create_rect<Sh>(w, h)} {}
inline _Rectangle(Unit width, Unit height,
// disable this ctor if o != CLOCKWISE
enable_if_t< o == TO::CLOCKWISE, int> = 0 ):
_Item<RawShape>( sl::create<RawShape>( {
{0, 0},
{0, height},
{width, height},
{width, 0},
{0, 0}
} ))
{
}
template<TO o = OrientationType<RawShape>::Value>
inline _Rectangle(Unit width, Unit height,
// disable this ctor if o != COUNTER_CLOCKWISE
enable_if_t< o == TO::COUNTER_CLOCKWISE, int> = 0 ):
_Item<RawShape>( sl::create<RawShape>( {
{0, 0},
{width, 0},
{width, height},
{0, height},
{0, 0}
} ))
{
}
inline Unit width() const BP2D_NOEXCEPT { inline Unit width() const BP2D_NOEXCEPT {
return getX(vertex(2)); return getX(vertex(2));

View File

@ -365,45 +365,51 @@ protected:
// the additional vertices for maintaning min object distance // the additional vertices for maintaning min object distance
sl::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++)
sl::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--)
sl::addVertex(rsh, item.vertex( sl::addVertex(rsh, item.vertex(static_cast<unsigned long>(i)));
static_cast<unsigned long>(i))); };
auto addOthers = [&addOthers_, &reverseAddOthers_]() {
if constexpr (!is_clockwise<RawShape>())
addOthers_();
else
reverseAddOthers_();
}; };
// Final polygon construction... // Final polygon construction...
static_assert(OrientationType<RawShape>::Value ==
Orientation::CLOCKWISE,
"Counter clockwise toWallPoly() Unimplemented!");
// Clockwise polygon construction // Clockwise polygon construction
sl::addVertex(rsh, topleft_vertex); sl::addVertex(rsh, topleft_vertex);
if(dir == Dir::LEFT) reverseAddOthers(); if(dir == Dir::LEFT) addOthers();
else { else {
sl::addVertex(rsh, getX(topleft_vertex), 0); sl::addVertex(rsh, {getX(topleft_vertex), 0});
sl::addVertex(rsh, getX(bottomleft_vertex), 0); sl::addVertex(rsh, {getX(bottomleft_vertex), 0});
} }
sl::addVertex(rsh, bottomleft_vertex); sl::addVertex(rsh, bottomleft_vertex);
if(dir == Dir::LEFT) { if(dir == Dir::LEFT) {
sl::addVertex(rsh, 0, getY(bottomleft_vertex)); sl::addVertex(rsh, {0, getY(bottomleft_vertex)});
sl::addVertex(rsh, 0, getY(topleft_vertex)); sl::addVertex(rsh, {0, getY(topleft_vertex)});
} }
else reverseAddOthers(); else addOthers();
// Close the polygon // Close the polygon
if constexpr (ClosureTypeV<RawShape> == Closure::CLOSED)
sl::addVertex(rsh, topleft_vertex); sl::addVertex(rsh, topleft_vertex);
if constexpr (!is_clockwise<RawShape>())
std::reverse(rsh.begin(), rsh.end());
return ret; return ret;
} }

View File

@ -250,8 +250,8 @@ template<class RawShape> class EdgeCache {
Vertex ret = edge.first(); Vertex ret = edge.first();
// Get the point on the edge which lies in ed distance from the start // Get the point on the edge which lies in ed distance from the start
ret += { static_cast<Coord>(std::round(ed*std::cos(angle))), ret += Vertex(static_cast<Coord>(std::round(ed*std::cos(angle))),
static_cast<Coord>(std::round(ed*std::sin(angle))) }; static_cast<Coord>(std::round(ed*std::sin(angle))));
return ret; return ret;
} }
@ -724,8 +724,7 @@ private:
auto rawobjfunc = [_objfunc, iv, startpos] auto rawobjfunc = [_objfunc, iv, startpos]
(Vertex v, Item& itm) (Vertex v, Item& itm)
{ {
auto d = v - iv; auto d = (v - iv) + startpos;
d += startpos;
itm.translation(d); itm.translation(d);
return _objfunc(itm); return _objfunc(itm);
}; };
@ -742,8 +741,7 @@ private:
&item, &bin, &iv, &startpos] (const Optimum& o) &item, &bin, &iv, &startpos] (const Optimum& o)
{ {
auto v = getNfpPoint(o); auto v = getNfpPoint(o);
auto d = v - iv; auto d = (v - iv) + startpos;
d += startpos;
item.translation(d); item.translation(d);
merged_pile.emplace_back(item.transformedShape()); merged_pile.emplace_back(item.transformedShape());
@ -877,8 +875,7 @@ private:
} }
if( best_score < global_score ) { if( best_score < global_score ) {
auto d = getNfpPoint(optimum) - iv; auto d = (getNfpPoint(optimum) - iv) + startpos;
d += startpos;
final_tr = d; final_tr = d;
final_rot = initial_rot + rot; final_rot = initial_rot + rot;
can_pack = true; can_pack = true;

View File

@ -19,7 +19,7 @@
#pragma warning(pop) #pragma warning(pop)
#endif #endif
// this should be removed to not confuse the compiler // this should be removed to not confuse the compiler
// #include <libnest2d.h> // #include "../libnest2d.hpp"
namespace bp2d { namespace bp2d {
@ -30,6 +30,10 @@ using libnest2d::PolygonImpl;
using libnest2d::PathImpl; using libnest2d::PathImpl;
using libnest2d::Orientation; using libnest2d::Orientation;
using libnest2d::OrientationType; using libnest2d::OrientationType;
using libnest2d::OrientationTypeV;
using libnest2d::ClosureType;
using libnest2d::Closure;
using libnest2d::ClosureTypeV;
using libnest2d::getX; using libnest2d::getX;
using libnest2d::getY; using libnest2d::getY;
using libnest2d::setX; using libnest2d::setX;
@ -213,8 +217,15 @@ struct ToBoostOrienation<bp2d::Orientation::COUNTER_CLOCKWISE> {
static const order_selector Value = counterclockwise; static const order_selector Value = counterclockwise;
}; };
static const bp2d::Orientation RealOrientation = template<bp2d::Closure> struct ToBoostClosure {};
bp2d::OrientationType<bp2d::PolygonImpl>::Value;
template<> struct ToBoostClosure<bp2d::Closure::OPEN> {
static const constexpr closure_selector Value = closure_selector::open;
};
template<> struct ToBoostClosure<bp2d::Closure::CLOSED> {
static const constexpr closure_selector Value = closure_selector::closed;
};
// Ring implementation ///////////////////////////////////////////////////////// // Ring implementation /////////////////////////////////////////////////////////
@ -225,12 +236,13 @@ template<> struct tag<bp2d::PathImpl> {
template<> struct point_order<bp2d::PathImpl> { template<> struct point_order<bp2d::PathImpl> {
static const order_selector value = static const order_selector value =
ToBoostOrienation<RealOrientation>::Value; ToBoostOrienation<bp2d::OrientationTypeV<bp2d::PathImpl>>::Value;
}; };
// All our Paths should be closed for the bin packing application // All our Paths should be closed for the bin packing application
template<> struct closure<bp2d::PathImpl> { template<> struct closure<bp2d::PathImpl> {
static const closure_selector value = closed; static const constexpr closure_selector value =
ToBoostClosure< bp2d::ClosureTypeV<bp2d::PathImpl> >::Value;
}; };
// Polygon implementation ////////////////////////////////////////////////////// // Polygon implementation //////////////////////////////////////////////////////

View File

@ -3,7 +3,7 @@
#include "BoundingBox.hpp" #include "BoundingBox.hpp"
#include <libnest2d/backends/clipper/geometries.hpp> #include <libnest2d/backends/libslic3r/geometries.hpp>
#include <libnest2d/optimizers/nlopt/subplex.hpp> #include <libnest2d/optimizers/nlopt/subplex.hpp>
#include <libnest2d/placers/nfpplacer.hpp> #include <libnest2d/placers/nfpplacer.hpp>
#include <libnest2d/selections/firstfit.hpp> #include <libnest2d/selections/firstfit.hpp>
@ -54,23 +54,22 @@ namespace Slic3r {
template<class Tout = double, class = FloatingOnly<Tout>, int...EigenArgs> template<class Tout = double, class = FloatingOnly<Tout>, int...EigenArgs>
inline constexpr Eigen::Matrix<Tout, 2, EigenArgs...> unscaled( inline constexpr Eigen::Matrix<Tout, 2, EigenArgs...> unscaled(
const ClipperLib::IntPoint &v) noexcept const Slic3r::ClipperLib::IntPoint &v) noexcept
{ {
return Eigen::Matrix<Tout, 2, EigenArgs...>{unscaled<Tout>(v.X), return Eigen::Matrix<Tout, 2, EigenArgs...>{unscaled<Tout>(v.x()),
unscaled<Tout>(v.Y)}; unscaled<Tout>(v.y())};
} }
namespace arrangement { namespace arrangement {
using namespace libnest2d; using namespace libnest2d;
namespace clppr = ClipperLib;
// Get the libnest2d types for clipper backend // Get the libnest2d types for clipper backend
using Item = _Item<clppr::Polygon>; using Item = _Item<ExPolygon>;
using Box = _Box<clppr::IntPoint>; using Box = _Box<Point>;
using Circle = _Circle<clppr::IntPoint>; using Circle = _Circle<Point>;
using Segment = _Segment<clppr::IntPoint>; using Segment = _Segment<Point>;
using MultiPolygon = TMultiShape<clppr::Polygon>; using MultiPolygon = ExPolygons;
// Summon the spatial indexing facilities from boost // Summon the spatial indexing facilities from boost
namespace bgi = boost::geometry::index; namespace bgi = boost::geometry::index;
@ -127,8 +126,8 @@ template<class TBin>
class AutoArranger { class AutoArranger {
public: public:
// Useful type shortcuts... // Useful type shortcuts...
using Placer = typename placers::_NofitPolyPlacer<clppr::Polygon, TBin>; using Placer = typename placers::_NofitPolyPlacer<ExPolygon, TBin>;
using Selector = selections::_FirstFitSelection<clppr::Polygon>; using Selector = selections::_FirstFitSelection<ExPolygon>;
using Packer = _Nester<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>;
@ -168,7 +167,7 @@ protected:
// as it possibly can be but at the same time, it has to provide // as it possibly can be but at the same time, it has to provide
// reasonable results. // reasonable results.
std::tuple<double /*score*/, Box /*farthest point from bin center*/> std::tuple<double /*score*/, Box /*farthest point from bin center*/>
objfunc(const Item &item, const clppr::IntPoint &bincenter) objfunc(const Item &item, const Point &bincenter)
{ {
const double bin_area = m_bin_area; const double bin_area = m_bin_area;
const SpatIndex& spatindex = m_rtree; const SpatIndex& spatindex = m_rtree;
@ -220,12 +219,12 @@ protected:
switch (compute_case) { switch (compute_case) {
case BIG_ITEM: { case BIG_ITEM: {
const clppr::IntPoint& minc = ibb.minCorner(); // bottom left corner const Point& minc = ibb.minCorner(); // bottom left corner
const clppr::IntPoint& maxc = ibb.maxCorner(); // top right corner const Point& maxc = ibb.maxCorner(); // top right corner
// top left and bottom right corners // top left and bottom right corners
clppr::IntPoint top_left{getX(minc), getY(maxc)}; Point top_left{getX(minc), getY(maxc)};
clppr::IntPoint bottom_right{getX(maxc), getY(minc)}; Point bottom_right{getX(maxc), getY(minc)};
// Now the distance of the gravity center will be calculated to the // Now the distance of the gravity center will be calculated to the
// five anchor points and the smallest will be chosen. // five anchor points and the smallest will be chosen.
@ -452,7 +451,7 @@ template<> std::function<double(const Item&)> AutoArranger<Circle>::get_objfn()
// Specialization for a generalized polygon. // Specialization for a generalized polygon.
// Warning: this is unfinished business. It may or may not work. // Warning: this is unfinished business. It may or may not work.
template<> template<>
std::function<double(const Item &)> AutoArranger<clppr::Polygon>::get_objfn() std::function<double(const Item &)> AutoArranger<ExPolygon>::get_objfn()
{ {
auto bincenter = sl::boundingBox(m_bin).center(); auto bincenter = sl::boundingBox(m_bin).center();
return [this, bincenter](const Item &item) { return [this, bincenter](const Item &item) {
@ -521,7 +520,7 @@ void _arrange(
inline Box to_nestbin(const BoundingBox &bb) { return Box{{bb.min(X), bb.min(Y)}, {bb.max(X), bb.max(Y)}};} inline Box to_nestbin(const BoundingBox &bb) { return Box{{bb.min(X), bb.min(Y)}, {bb.max(X), bb.max(Y)}};}
inline Circle to_nestbin(const CircleBed &c) { return Circle({c.center()(0), c.center()(1)}, c.radius()); } inline Circle to_nestbin(const CircleBed &c) { return Circle({c.center()(0), c.center()(1)}, c.radius()); }
inline clppr::Polygon to_nestbin(const Polygon &p) { return sl::create<clppr::Polygon>(Slic3rMultiPoint_to_ClipperPath(p)); } inline ExPolygon to_nestbin(const Polygon &p) { return ExPolygon{p}; }
inline Box to_nestbin(const InfiniteBed &bed) { return Box::infinite({bed.center.x(), bed.center.y()}); } inline Box to_nestbin(const InfiniteBed &bed) { return Box::infinite({bed.center.x(), bed.center.y()}); }
inline coord_t width(const BoundingBox& box) { return box.max.x() - box.min.x(); } inline coord_t width(const BoundingBox& box) { return box.max.x() - box.min.x(); }
@ -568,19 +567,12 @@ static void process_arrangeable(const ArrangePolygon &arrpoly,
const Vec2crd &offs = arrpoly.translation; const Vec2crd &offs = arrpoly.translation;
double rotation = arrpoly.rotation; double rotation = arrpoly.rotation;
if (p.is_counter_clockwise()) p.reverse();
clppr::Polygon clpath(Slic3rMultiPoint_to_ClipperPath(p));
// This fixes: // This fixes:
// https://github.com/prusa3d/PrusaSlicer/issues/2209 // https://github.com/prusa3d/PrusaSlicer/issues/2209
if (clpath.Contour.size() < 3) if (p.points.size() < 3)
return; return;
auto firstp = clpath.Contour.front(); outp.emplace_back(std::move(p));
clpath.Contour.emplace_back(firstp);
outp.emplace_back(std::move(clpath));
outp.back().rotation(rotation); outp.back().rotation(rotation);
outp.back().translation({offs.x(), offs.y()}); outp.back().translation({offs.x(), offs.y()});
outp.back().binId(arrpoly.bed_idx); outp.back().binId(arrpoly.bed_idx);
@ -624,7 +616,7 @@ void arrange(ArrangePolygons & arrangables,
const BedT & bed, const BedT & bed,
const ArrangeParams & params) const ArrangeParams & params)
{ {
namespace clppr = ClipperLib; namespace clppr = Slic3r::ClipperLib;
std::vector<Item> items, fixeditems; std::vector<Item> items, fixeditems;
items.reserve(arrangables.size()); items.reserve(arrangables.size());
@ -643,8 +635,8 @@ void arrange(ArrangePolygons & arrangables,
_arrange(items, fixeditems, to_nestbin(bed), params, pri, cfn); _arrange(items, fixeditems, to_nestbin(bed), params, pri, cfn);
for(size_t i = 0; i < items.size(); ++i) { for(size_t i = 0; i < items.size(); ++i) {
clppr::IntPoint tr = items[i].translation(); Point tr = items[i].translation();
arrangables[i].translation = {coord_t(tr.X), coord_t(tr.Y)}; arrangables[i].translation = {coord_t(tr.x()), coord_t(tr.y())};
arrangables[i].rotation = items[i].rotation(); arrangables[i].rotation = items[i].rotation();
arrangables[i].bed_idx = items[i].binId(); arrangables[i].bed_idx = items[i].binId();
} }

View File

@ -78,7 +78,7 @@ static ConstPrintObjectPtrs get_top_level_objects_with_brim(const Print &print)
// Assign the maximum Z from four points. This values is valid index of the island // Assign the maximum Z from four points. This values is valid index of the island
clipper.ZFillFunction([](const ClipperLib_Z::IntPoint &e1bot, const ClipperLib_Z::IntPoint &e1top, const ClipperLib_Z::IntPoint &e2bot, clipper.ZFillFunction([](const ClipperLib_Z::IntPoint &e1bot, const ClipperLib_Z::IntPoint &e1top, const ClipperLib_Z::IntPoint &e2bot,
const ClipperLib_Z::IntPoint &e2top, ClipperLib_Z::IntPoint &pt) { const ClipperLib_Z::IntPoint &e2top, ClipperLib_Z::IntPoint &pt) {
pt.Z = std::max(std::max(e1bot.Z, e1top.Z), std::max(e2bot.Z, e2top.Z)); pt.z() = std::max(std::max(e1bot.z(), e1top.z()), std::max(e2bot.z(), e2top.z()));
}); });
// Add islands // Add islands
clipper.AddPaths(islands_clip, ClipperLib_Z::ptSubject, true); clipper.AddPaths(islands_clip, ClipperLib_Z::ptSubject, true);
@ -90,9 +90,9 @@ static ConstPrintObjectPtrs get_top_level_objects_with_brim(const Print &print)
ConstPrintObjectPtrs top_level_objects_with_brim; ConstPrintObjectPtrs top_level_objects_with_brim;
for (int i = 0; i < islands_polytree.ChildCount(); ++i) { for (int i = 0; i < islands_polytree.ChildCount(); ++i) {
for (const ClipperLib_Z::IntPoint &point : islands_polytree.Childs[i]->Contour) { for (const ClipperLib_Z::IntPoint &point : islands_polytree.Childs[i]->Contour) {
if (point.Z != 0 && processed_objects_idx.find(island_to_object[point.Z - 1]->id().id) == processed_objects_idx.end()) { if (point.z() != 0 && processed_objects_idx.find(island_to_object[point.z() - 1]->id().id) == processed_objects_idx.end()) {
top_level_objects_with_brim.emplace_back(island_to_object[point.Z - 1]); top_level_objects_with_brim.emplace_back(island_to_object[point.z() - 1]);
processed_objects_idx.insert(island_to_object[point.Z - 1]->id().id); processed_objects_idx.insert(island_to_object[point.z() - 1]->id().id);
} }
} }
} }
@ -456,7 +456,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
clipper.ZFillFunction([](const ClipperLib_Z::IntPoint& e1bot, const ClipperLib_Z::IntPoint& e1top, const ClipperLib_Z::IntPoint& e2bot, const ClipperLib_Z::IntPoint& e2top, ClipperLib_Z::IntPoint& pt) { clipper.ZFillFunction([](const ClipperLib_Z::IntPoint& e1bot, const ClipperLib_Z::IntPoint& e1top, const ClipperLib_Z::IntPoint& e2bot, const ClipperLib_Z::IntPoint& e2top, ClipperLib_Z::IntPoint& pt) {
// Assign a valid input loop identifier. Such an identifier is strictly positive, the next line is safe even in case one side of a segment // Assign a valid input loop identifier. Such an identifier is strictly positive, the next line is safe even in case one side of a segment
// hat the Z coordinate not set to the contour coordinate. // hat the Z coordinate not set to the contour coordinate.
pt.Z = std::max(std::max(e1bot.Z, e1top.Z), std::max(e2bot.Z, e2top.Z)); pt.z() = std::max(std::max(e1bot.z(), e1top.z()), std::max(e2bot.z(), e2top.z()));
}); });
// add polygons // add polygons
clipper.AddPaths(input_clip, ClipperLib_Z::ptSubject, false); clipper.AddPaths(input_clip, ClipperLib_Z::ptSubject, false);
@ -474,8 +474,8 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
for (const ClipperLib_Z::Path &path : loops_trimmed) { for (const ClipperLib_Z::Path &path : loops_trimmed) {
size_t input_idx = 0; size_t input_idx = 0;
for (const ClipperLib_Z::IntPoint &pt : path) for (const ClipperLib_Z::IntPoint &pt : path)
if (pt.Z > 0) { if (pt.z() > 0) {
input_idx = (size_t)pt.Z; input_idx = (size_t)pt.z();
break; break;
} }
assert(input_idx != 0); assert(input_idx != 0);
@ -492,14 +492,14 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
size_t j = i + 1; size_t j = i + 1;
for (; j < loops_trimmed_order.size() && loops_trimmed_order[i].second == loops_trimmed_order[j].second; ++ j) ; for (; j < loops_trimmed_order.size() && loops_trimmed_order[i].second == loops_trimmed_order[j].second; ++ j) ;
const ClipperLib_Z::Path &first_path = *loops_trimmed_order[i].first; const ClipperLib_Z::Path &first_path = *loops_trimmed_order[i].first;
if (i + 1 == j && first_path.size() > 3 && first_path.front().X == first_path.back().X && first_path.front().Y == first_path.back().Y) { if (i + 1 == j && first_path.size() > 3 && first_path.front().x() == first_path.back().x() && first_path.front().y() == first_path.back().y()) {
auto *loop = new ExtrusionLoop(); auto *loop = new ExtrusionLoop();
brim.entities.emplace_back(loop); brim.entities.emplace_back(loop);
loop->paths.emplace_back(erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height())); loop->paths.emplace_back(erSkirt, float(flow.mm3_per_mm()), float(flow.width()), float(print.skirt_first_layer_height()));
Points &points = loop->paths.front().polyline.points; Points &points = loop->paths.front().polyline.points;
points.reserve(first_path.size()); points.reserve(first_path.size());
for (const ClipperLib_Z::IntPoint &pt : first_path) for (const ClipperLib_Z::IntPoint &pt : first_path)
points.emplace_back(coord_t(pt.X), coord_t(pt.Y)); points.emplace_back(coord_t(pt.x()), coord_t(pt.y()));
i = j; i = j;
} else { } else {
//FIXME The path chaining here may not be optimal. //FIXME The path chaining here may not be optimal.
@ -511,7 +511,7 @@ ExtrusionEntityCollection make_brim(const Print &print, PrintTryCancel try_cance
Points &points = static_cast<ExtrusionPath*>(this_loop_trimmed.entities.back())->polyline.points; Points &points = static_cast<ExtrusionPath*>(this_loop_trimmed.entities.back())->polyline.points;
points.reserve(path.size()); points.reserve(path.size());
for (const ClipperLib_Z::IntPoint &pt : path) for (const ClipperLib_Z::IntPoint &pt : path)
points.emplace_back(coord_t(pt.X), coord_t(pt.Y)); points.emplace_back(coord_t(pt.x()), coord_t(pt.y()));
} }
chain_and_reorder_extrusion_entities(this_loop_trimmed.entities, &last_pt); chain_and_reorder_extrusion_entities(this_loop_trimmed.entities, &last_pt);
brim.entities.reserve(brim.entities.size() + this_loop_trimmed.entities.size()); brim.entities.reserve(brim.entities.size() + this_loop_trimmed.entities.size());

View File

@ -23,6 +23,8 @@ add_library(libslic3r STATIC
BridgeDetector.hpp BridgeDetector.hpp
Brim.cpp Brim.cpp
Brim.hpp Brim.hpp
clipper.cpp
clipper.hpp
ClipperUtils.cpp ClipperUtils.cpp
ClipperUtils.hpp ClipperUtils.hpp
Config.cpp Config.cpp

View File

@ -131,7 +131,7 @@ Slic3r::Polygon ClipperPath_to_Slic3rPolygon(const ClipperLib::Path &input)
{ {
Polygon retval; Polygon retval;
for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit) for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit)
retval.points.emplace_back(pit->X, pit->Y); retval.points.emplace_back(pit->x(), pit->y());
return retval; return retval;
} }
@ -139,7 +139,7 @@ Slic3r::Polyline ClipperPath_to_Slic3rPolyline(const ClipperLib::Path &input)
{ {
Polyline retval; Polyline retval;
for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit) for (ClipperLib::Path::const_iterator pit = input.begin(); pit != input.end(); ++pit)
retval.points.emplace_back(pit->X, pit->Y); retval.points.emplace_back(pit->x(), pit->y());
return retval; return retval;
} }
@ -752,7 +752,7 @@ ClipperLib::PolyNodes order_nodes(const ClipperLib::PolyNodes &nodes)
for (const ClipperLib::PolyNode *node : nodes) for (const ClipperLib::PolyNode *node : nodes)
ordering_points.emplace_back( ordering_points.emplace_back(
Point(node->Contour.front().X, node->Contour.front().Y)); Point(node->Contour.front().x(), node->Contour.front().y()));
// perform the ordering // perform the ordering
ClipperLib::PolyNodes ordered_nodes = ClipperLib::PolyNodes ordered_nodes =
@ -777,7 +777,7 @@ static void traverse_pt_outside_in(const ClipperLib::PolyNodes &nodes, Polygons
Points ordering_points; Points ordering_points;
ordering_points.reserve(nodes.size()); ordering_points.reserve(nodes.size());
for (const ClipperLib::PolyNode *node : nodes) for (const ClipperLib::PolyNode *node : nodes)
ordering_points.emplace_back(node->Contour.front().X, node->Contour.front().Y); ordering_points.emplace_back(node->Contour.front().x(), node->Contour.front().y());
// Perform the ordering, push results recursively. // Perform the ordering, push results recursively.
//FIXME pass the last point to chain_clipper_polynodes? //FIXME pass the last point to chain_clipper_polynodes?

View File

@ -8,9 +8,9 @@
#include "Surface.hpp" #include "Surface.hpp"
// import these wherever we're included // import these wherever we're included
using ClipperLib::jtMiter; using Slic3r::ClipperLib::jtMiter;
using ClipperLib::jtRound; using Slic3r::ClipperLib::jtRound;
using ClipperLib::jtSquare; using Slic3r::ClipperLib::jtSquare;
#define CLIPPERUTILS_UNSAFE_OFFSET #define CLIPPERUTILS_UNSAFE_OFFSET

View File

@ -360,6 +360,8 @@ extern std::vector<BoundingBox> get_extents_vector(const ExPolygons &polygons);
extern bool remove_sticks(ExPolygon &poly); extern bool remove_sticks(ExPolygon &poly);
extern void keep_largest_contour_only(ExPolygons &polygons); extern void keep_largest_contour_only(ExPolygons &polygons);
inline double area(const ExPolygon &poly) { return poly.area(); }
inline double area(const ExPolygons &polys) inline double area(const ExPolygons &polys)
{ {
double s = 0.; double s = 0.;

View File

@ -10,10 +10,6 @@
namespace Slic3r { namespace Slic3r {
// Borrowed from C++20
template<class T>
using remove_cvref_t = std::remove_reference_t<std::remove_cv_t<T>>;
// Override for valid execution policies // Override for valid execution policies
template<class EP> struct IsExecutionPolicy_ : public std::false_type {}; template<class EP> struct IsExecutionPolicy_ : public std::false_type {};

View File

@ -595,7 +595,6 @@ static inline bool line_rounded_thick_segment_collision(
// Very short line vector. Just test whether the center point is inside the offset line. // Very short line vector. Just test whether the center point is inside the offset line.
Vec2d lpt = 0.5 * (line_a + line_b); Vec2d lpt = 0.5 * (line_a + line_b);
if (segment_l > SCALED_EPSILON) { if (segment_l > SCALED_EPSILON) {
struct Linef { Vec2d a, b; };
intersects = line_alg::distance_to_squared(Linef{ segment_a, segment_b }, lpt) < offset2; intersects = line_alg::distance_to_squared(Linef{ segment_a, segment_b }, lpt) < offset2;
} else } else
intersects = (0.5 * (segment_a + segment_b) - lpt).squaredNorm() < offset2; intersects = (0.5 * (segment_a + segment_b) - lpt).squaredNorm() < offset2;
@ -1196,8 +1195,6 @@ static inline void mark_boundary_segments_overlapping_infill(
// Spacing (width) of the infill lines. // Spacing (width) of the infill lines.
const double spacing) const double spacing)
{ {
struct Linef { Vec2d a; Vec2d b; };
for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) { for (ContourIntersectionPoint &cp : graph.map_infill_end_point_to_boundary) {
const Points &contour = graph.boundary[cp.contour_idx]; const Points &contour = graph.boundary[cp.contour_idx];
const std::vector<double> &contour_params = graph.boundary_params[cp.contour_idx]; const std::vector<double> &contour_params = graph.boundary_params[cp.contour_idx];
@ -2003,9 +2000,8 @@ static double evaluate_support_arch_cost(const Polyline &pl)
double dmax = 0; double dmax = 0;
// Maximum distance in Y axis out of the (ymin, ymax) band and from the (front, back) line. // Maximum distance in Y axis out of the (ymin, ymax) band and from the (front, back) line.
struct Linef { Vec2d a, b; };
Linef line { front.cast<double>(), back.cast<double>() }; Linef line { front.cast<double>(), back.cast<double>() };
for (const Point pt : pl.points) for (const Point &pt : pl.points)
dmax = std::max<double>(std::max(dmax, line_alg::distance_to(line, Vec2d(pt.cast<double>()))), std::max(pt.y() - ymax, ymin - pt.y())); dmax = std::max<double>(std::max(dmax, line_alg::distance_to(line, Vec2d(pt.cast<double>()))), std::max(pt.y() - ymax, ymin - pt.y()));
return dmax; return dmax;
} }

View File

@ -22,12 +22,14 @@
#pragma warning(pop) #pragma warning(pop)
#endif // _MSC_VER #endif // _MSC_VER
namespace ClipperLib { namespace Slic3r {
class PolyNode;
using PolyNodes = std::vector<PolyNode*>;
}
namespace Slic3r { namespace Geometry { namespace ClipperLib {
class PolyNode;
using PolyNodes = std::vector<PolyNode*>;
}
namespace Geometry {
// Generic result of an orientation predicate. // Generic result of an orientation predicate.
enum Orientation enum Orientation
@ -530,6 +532,6 @@ inline bool is_rotation_ninety_degrees(const Vec3d &rotation)
return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z()); return is_rotation_ninety_degrees(rotation.x()) && is_rotation_ninety_degrees(rotation.y()) && is_rotation_ninety_degrees(rotation.z());
} }
} } } } // namespace Slicer::Geometry
#endif #endif

View File

@ -4,6 +4,8 @@
#include "libslic3r.h" #include "libslic3r.h"
#include "Point.hpp" #include "Point.hpp"
#include <type_traits>
namespace Slic3r { namespace Slic3r {
class BoundingBox; class BoundingBox;
@ -20,12 +22,28 @@ Linef3 transform(const Linef3& line, const Transform3d& t);
namespace line_alg { namespace line_alg {
template<class L, class En = void> struct Traits {
static constexpr int Dim = L::Dim;
using Scalar = typename L::Scalar;
static Vec<Dim, Scalar>& get_a(L &l) { return l.a; }
static Vec<Dim, Scalar>& get_b(L &l) { return l.b; }
static const Vec<Dim, Scalar>& get_a(const L &l) { return l.a; }
static const Vec<Dim, Scalar>& get_b(const L &l) { return l.b; }
};
template<class L> const constexpr int Dim = Traits<remove_cvref_t<L>>::Dim;
template<class L> using Scalar = typename Traits<remove_cvref_t<L>>::Scalar;
template<class L> auto get_a(L &&l) { return Traits<remove_cvref_t<L>>::get_a(l); }
template<class L> auto get_b(L &&l) { return Traits<remove_cvref_t<L>>::get_b(l); }
// Distance to the closest point of line. // Distance to the closest point of line.
template<class L, class T, int N> template<class L>
double distance_to_squared(const L &line, const Vec<N, T> &point) double distance_to_squared(const L &line, const Vec<Dim<L>, Scalar<L>> &point)
{ {
const Vec<N, double> v = (line.b - line.a).template cast<double>(); const Vec<Dim<L>, double> v = (get_b(line) - get_a(line)).template cast<double>();
const Vec<N, double> va = (point - line.a).template cast<double>(); const Vec<Dim<L>, double> va = (point - get_a(line)).template cast<double>();
const double l2 = v.squaredNorm(); // avoid a sqrt const double l2 = v.squaredNorm(); // avoid a sqrt
if (l2 == 0.0) if (l2 == 0.0)
// a == b case // a == b case
@ -35,12 +53,12 @@ double distance_to_squared(const L &line, const Vec<N, T> &point)
// It falls where t = [(this-a) . (b-a)] / |b-a|^2 // It falls where t = [(this-a) . (b-a)] / |b-a|^2
const double t = va.dot(v) / l2; const double t = va.dot(v) / l2;
if (t < 0.0) return va.squaredNorm(); // beyond the 'a' end of the segment if (t < 0.0) return va.squaredNorm(); // beyond the 'a' end of the segment
else if (t > 1.0) return (point - line.b).template cast<double>().squaredNorm(); // beyond the 'b' end of the segment else if (t > 1.0) return (point - get_b(line)).template cast<double>().squaredNorm(); // beyond the 'b' end of the segment
return (t * v - va).squaredNorm(); return (t * v - va).squaredNorm();
} }
template<class L, class T, int N> template<class L>
double distance_to(const L &line, const Vec<N, T> &point) double distance_to(const L &line, const Vec<Dim<L>, Scalar<L>> &point)
{ {
return std::sqrt(distance_to_squared(line, point)); return std::sqrt(distance_to_squared(line, point));
} }
@ -84,6 +102,9 @@ public:
Point a; Point a;
Point b; Point b;
static const constexpr int Dim = 2;
using Scalar = Point::Scalar;
}; };
class ThickLine : public Line class ThickLine : public Line
@ -107,6 +128,9 @@ public:
Vec3crd a; Vec3crd a;
Vec3crd b; Vec3crd b;
static const constexpr int Dim = 3;
using Scalar = Vec3crd::Scalar;
}; };
class Linef class Linef
@ -117,6 +141,9 @@ public:
Vec2d a; Vec2d a;
Vec2d b; Vec2d b;
static const constexpr int Dim = 2;
using Scalar = Vec2d::Scalar;
}; };
class Linef3 class Linef3
@ -133,6 +160,9 @@ public:
Vec3d a; Vec3d a;
Vec3d b; Vec3d b;
static const constexpr int Dim = 3;
using Scalar = Vec3d::Scalar;
}; };
BoundingBox get_extents(const Lines &lines); BoundingBox get_extents(const Lines &lines);

View File

@ -106,8 +106,8 @@ template<class C> bool all_of(const C &container)
}); });
} }
template<class T> //template<class T>
using remove_cvref_t = std::remove_reference_t<std::remove_cv_t<T>>; //using remove_cvref_t = std::remove_reference_t<std::remove_cv_t<T>>;
/// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html /// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html
template<class T, class I, class = IntegerOnly<I>> template<class T, class I, class = IntegerOnly<I>>

View File

@ -14,55 +14,9 @@
#include <boost/multiprecision/integer.hpp> #include <boost/multiprecision/integer.hpp>
#endif #endif
#include <libnest2d/geometry_traits.hpp> #include <libnest2d/backends/libslic3r/geometries.hpp>
#include <libnest2d/utils/rotcalipers.hpp> #include <libnest2d/utils/rotcalipers.hpp>
namespace libnest2d {
template<> struct PointType<Slic3r::Points> { using Type = Slic3r::Point; };
template<> struct CoordType<Slic3r::Point> { using Type = coord_t; };
template<> struct ShapeTag<Slic3r::ExPolygon> { using Type = PolygonTag; };
template<> struct ShapeTag<Slic3r::Polygon> { using Type = PolygonTag; };
template<> struct ShapeTag<Slic3r::Points> { using Type = PathTag; };
template<> struct ShapeTag<Slic3r::Point> { using Type = PointTag; };
template<> struct ContourType<Slic3r::ExPolygon> { using Type = Slic3r::Points; };
template<> struct ContourType<Slic3r::Polygon> { using Type = Slic3r::Points; };
namespace pointlike {
template<> inline coord_t x(const Slic3r::Point& p) { return p.x(); }
template<> inline coord_t y(const Slic3r::Point& p) { return p.y(); }
template<> inline coord_t& x(Slic3r::Point& p) { return p.x(); }
template<> inline coord_t& y(Slic3r::Point& p) { return p.y(); }
} // pointlike
namespace shapelike {
template<> inline Slic3r::Points& contour(Slic3r::ExPolygon& sh) { return sh.contour.points; }
template<> inline const Slic3r::Points& contour(const Slic3r::ExPolygon& sh) { return sh.contour.points; }
template<> inline Slic3r::Points& contour(Slic3r::Polygon& sh) { return sh.points; }
template<> inline const Slic3r::Points& contour(const Slic3r::Polygon& sh) { return sh.points; }
template<> Slic3r::Points::iterator begin(Slic3r::Points& pts, const PathTag&) { return pts.begin();}
template<> Slic3r::Points::const_iterator cbegin(const Slic3r::Points& pts, const PathTag&) { return pts.cbegin(); }
template<> Slic3r::Points::iterator end(Slic3r::Points& pts, const PathTag&) { return pts.end();}
template<> Slic3r::Points::const_iterator cend(const Slic3r::Points& pts, const PathTag&) { return pts.cend(); }
template<> inline Slic3r::ExPolygon create<Slic3r::ExPolygon>(Slic3r::Points&& contour)
{
Slic3r::ExPolygon expoly; expoly.contour.points.swap(contour);
return expoly;
}
template<> inline Slic3r::Polygon create<Slic3r::Polygon>(Slic3r::Points&& contour)
{
Slic3r::Polygon poly; poly.points.swap(contour);
return poly;
}
} // shapelike
} // libnest2d
namespace Slic3r { namespace Slic3r {
// Used as compute type. // Used as compute type.
@ -74,13 +28,22 @@ using Rational = boost::rational<boost::multiprecision::int128_t>;
using Rational = boost::rational<__int128>; using Rational = boost::rational<__int128>;
#endif #endif
MinAreaBoundigBox::MinAreaBoundigBox(const Polygon &p, PolygonLevel pc) template<class P>
libnest2d::RotatedBox<Point, Unit> minAreaBoundigBox_(
const P &p, MinAreaBoundigBox::PolygonLevel lvl)
{ {
const Polygon &chull = pc == pcConvex ? p : P chull = lvl == MinAreaBoundigBox::pcConvex ?
p :
libnest2d::sl::convexHull(p); libnest2d::sl::convexHull(p);
libnest2d::RotatedBox<Point, Unit> box = libnest2d::removeCollinearPoints(chull);
libnest2d::minAreaBoundingBox<Polygon, Unit, Rational>(chull);
return libnest2d::minAreaBoundingBox<P, Unit, Rational>(chull);
}
MinAreaBoundigBox::MinAreaBoundigBox(const Polygon &p, PolygonLevel pc)
{
libnest2d::RotatedBox<Point, Unit> box = minAreaBoundigBox_(p, pc);
m_right = libnest2d::cast<long double>(box.right_extent()); m_right = libnest2d::cast<long double>(box.right_extent());
m_bottom = libnest2d::cast<long double>(box.bottom_extent()); m_bottom = libnest2d::cast<long double>(box.bottom_extent());
@ -89,11 +52,7 @@ MinAreaBoundigBox::MinAreaBoundigBox(const Polygon &p, PolygonLevel pc)
MinAreaBoundigBox::MinAreaBoundigBox(const ExPolygon &p, PolygonLevel pc) MinAreaBoundigBox::MinAreaBoundigBox(const ExPolygon &p, PolygonLevel pc)
{ {
const ExPolygon &chull = pc == pcConvex ? p : libnest2d::RotatedBox<Point, Unit> box = minAreaBoundigBox_(p, pc);
libnest2d::sl::convexHull(p);
libnest2d::RotatedBox<Point, Unit> box =
libnest2d::minAreaBoundingBox<ExPolygon, Unit, Rational>(chull);
m_right = libnest2d::cast<long double>(box.right_extent()); m_right = libnest2d::cast<long double>(box.right_extent());
m_bottom = libnest2d::cast<long double>(box.bottom_extent()); m_bottom = libnest2d::cast<long double>(box.bottom_extent());
@ -102,11 +61,7 @@ MinAreaBoundigBox::MinAreaBoundigBox(const ExPolygon &p, PolygonLevel pc)
MinAreaBoundigBox::MinAreaBoundigBox(const Points &pts, PolygonLevel pc) MinAreaBoundigBox::MinAreaBoundigBox(const Points &pts, PolygonLevel pc)
{ {
const Points &chull = pc == pcConvex ? pts : libnest2d::RotatedBox<Point, Unit> box = minAreaBoundigBox_(pts, pc);
libnest2d::sl::convexHull(pts);
libnest2d::RotatedBox<Point, Unit> box =
libnest2d::minAreaBoundingBox<Points, Unit, Rational>(chull);
m_right = libnest2d::cast<long double>(box.right_extent()); m_right = libnest2d::cast<long double>(box.right_extent());
m_bottom = libnest2d::cast<long double>(box.bottom_extent()); m_bottom = libnest2d::cast<long double>(box.bottom_extent());

View File

@ -27,11 +27,7 @@ public:
// Constructors with various types of geometry data used in Slic3r. // Constructors with various types of geometry data used in Slic3r.
// If the convexity is known apriory, pcConvex can be used to skip // If the convexity is known apriory, pcConvex can be used to skip
// convex hull calculation. It is very important that the input polygons // convex hull calculation.
// do NOT have any collinear points (except for the first and the last
// vertex being the same -- meaning a closed polygon for boost)
// To make sure this constraint is satisfied, you can call
// remove_collinear_points on the input polygon before handing over here)
explicit MinAreaBoundigBox(const Polygon&, PolygonLevel = pcSimple); explicit MinAreaBoundigBox(const Polygon&, PolygonLevel = pcSimple);
explicit MinAreaBoundigBox(const ExPolygon&, PolygonLevel = pcSimple); explicit MinAreaBoundigBox(const ExPolygon&, PolygonLevel = pcSimple);
explicit MinAreaBoundigBox(const Points&, PolygonLevel = pcSimple); explicit MinAreaBoundigBox(const Points&, PolygonLevel = pcSimple);

View File

@ -84,6 +84,13 @@ public:
static Points _douglas_peucker(const Points &points, const double tolerance); static Points _douglas_peucker(const Points &points, const double tolerance);
static Points visivalingam(const Points& pts, const double& tolerance); static Points visivalingam(const Points& pts, const double& tolerance);
inline auto begin() { return points.begin(); }
inline auto begin() const { return points.begin(); }
inline auto end() { return points.end(); }
inline auto end() const { return points.end(); }
inline auto cbegin() const { return points.begin(); }
inline auto cend() const { return points.end(); }
}; };
class MultiPoint3 class MultiPoint3

View File

@ -17,42 +17,42 @@ class BoundingBox;
class Line; class Line;
class MultiPoint; class MultiPoint;
class Point; class Point;
typedef Point Vector; using Vector = Point;
// Eigen types, to replace the Slic3r's own types in the future. // Eigen types, to replace the Slic3r's own types in the future.
// Vector types with a fixed point coordinate base type. // Vector types with a fixed point coordinate base type.
typedef Eigen::Matrix<coord_t, 2, 1, Eigen::DontAlign> Vec2crd; using Vec2crd = Eigen::Matrix<coord_t, 2, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<coord_t, 3, 1, Eigen::DontAlign> Vec3crd; using Vec3crd = Eigen::Matrix<coord_t, 3, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<int, 2, 1, Eigen::DontAlign> Vec2i; using Vec2i = Eigen::Matrix<int, 2, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<int, 3, 1, Eigen::DontAlign> Vec3i; using Vec3i = Eigen::Matrix<int, 3, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<int32_t, 2, 1, Eigen::DontAlign> Vec2i32; using Vec2i32 = Eigen::Matrix<int32_t, 2, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<int64_t, 2, 1, Eigen::DontAlign> Vec2i64; using Vec2i64 = Eigen::Matrix<int64_t, 2, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<int32_t, 3, 1, Eigen::DontAlign> Vec3i32; using Vec3i32 = Eigen::Matrix<int32_t, 3, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<int64_t, 3, 1, Eigen::DontAlign> Vec3i64; using Vec3i64 = Eigen::Matrix<int64_t, 3, 1, Eigen::DontAlign>;
// Vector types with a double coordinate base type. // Vector types with a double coordinate base type.
typedef Eigen::Matrix<float, 2, 1, Eigen::DontAlign> Vec2f; using Vec2f = Eigen::Matrix<float, 2, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<float, 3, 1, Eigen::DontAlign> Vec3f; using Vec3f = Eigen::Matrix<float, 3, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<double, 2, 1, Eigen::DontAlign> Vec2d; using Vec2d = Eigen::Matrix<double, 2, 1, Eigen::DontAlign>;
typedef Eigen::Matrix<double, 3, 1, Eigen::DontAlign> Vec3d; using Vec3d = Eigen::Matrix<double, 3, 1, Eigen::DontAlign>;
typedef std::vector<Point> Points; using Points = std::vector<Point>;
typedef std::vector<Point*> PointPtrs; using PointPtrs = std::vector<Point*>;
typedef std::vector<const Point*> PointConstPtrs; using PointConstPtrs = std::vector<const Point*>;
typedef std::vector<Vec3crd> Points3; using Points3 = std::vector<Vec3crd>;
typedef std::vector<Vec2d> Pointfs; using Pointfs = std::vector<Vec2d>;
typedef std::vector<Vec2d> Vec2ds; using Vec2ds = std::vector<Vec2d>;
typedef std::vector<Vec3d> Pointf3s; using Pointf3s = std::vector<Vec3d>;
typedef Eigen::Matrix<float, 2, 2, Eigen::DontAlign> Matrix2f; using Matrix2f = Eigen::Matrix<float, 2, 2, Eigen::DontAlign>;
typedef Eigen::Matrix<double, 2, 2, Eigen::DontAlign> Matrix2d; using Matrix2d = Eigen::Matrix<double, 2, 2, Eigen::DontAlign>;
typedef Eigen::Matrix<float, 3, 3, Eigen::DontAlign> Matrix3f; using Matrix3f = Eigen::Matrix<float, 3, 3, Eigen::DontAlign>;
typedef Eigen::Matrix<double, 3, 3, Eigen::DontAlign> Matrix3d; using Matrix3d = Eigen::Matrix<double, 3, 3, Eigen::DontAlign>;
typedef Eigen::Transform<float, 2, Eigen::Affine, Eigen::DontAlign> Transform2f; using Transform2f = Eigen::Transform<float, 2, Eigen::Affine, Eigen::DontAlign>;
typedef Eigen::Transform<double, 2, Eigen::Affine, Eigen::DontAlign> Transform2d; using Transform2d = Eigen::Transform<double, 2, Eigen::Affine, Eigen::DontAlign>;
typedef Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign> Transform3f; using Transform3f = Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign>;
typedef Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign> Transform3d; using Transform3d = Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign>;
inline bool operator<(const Vec2d &lhs, const Vec2d &rhs) { return lhs(0) < rhs(0) || (lhs(0) == rhs(0) && lhs(1) < rhs(1)); } inline bool operator<(const Vec2d &lhs, const Vec2d &rhs) { return lhs(0) < rhs(0) || (lhs(0) == rhs(0) && lhs(1) < rhs(1)); }
@ -101,7 +101,7 @@ template<int N, class T> using Vec = Eigen::Matrix<T, N, 1, Eigen::DontAlign, N
class Point : public Vec2crd class Point : public Vec2crd
{ {
public: public:
typedef coord_t coord_type; using coord_type = coord_t;
Point() : Vec2crd(0, 0) {} Point() : Vec2crd(0, 0) {}
Point(int32_t x, int32_t y) : Vec2crd(coord_t(x), coord_t(y)) {} Point(int32_t x, int32_t y) : Vec2crd(coord_t(x), coord_t(y)) {}
@ -337,7 +337,7 @@ public:
} }
private: private:
typedef typename std::unordered_multimap<Vec2crd, ValueType, PointHash> map_type; using map_type = typename std::unordered_multimap<Vec2crd, ValueType, PointHash>;
PointAccessor m_point_accessor; PointAccessor m_point_accessor;
map_type m_map; map_type m_map;
coord_t m_search_radius; coord_t m_search_radius;
@ -439,11 +439,11 @@ inline Point align_to_grid(Point coord, Point spacing, Point base)
#include <boost/polygon/polygon.hpp> #include <boost/polygon/polygon.hpp>
namespace boost { namespace polygon { namespace boost { namespace polygon {
template <> template <>
struct geometry_concept<Slic3r::Point> { typedef point_concept type; }; struct geometry_concept<Slic3r::Point> { using type = point_concept; };
template <> template <>
struct point_traits<Slic3r::Point> { struct point_traits<Slic3r::Point> {
typedef coord_t coordinate_type; using coordinate_type = coord_t;
static inline coordinate_type get(const Slic3r::Point& point, orientation_2d orient) { static inline coordinate_type get(const Slic3r::Point& point, orientation_2d orient) {
return static_cast<coordinate_type>(point((orient == HORIZONTAL) ? 0 : 1)); return static_cast<coordinate_type>(point((orient == HORIZONTAL) ? 0 : 1));
@ -452,7 +452,7 @@ namespace boost { namespace polygon {
template <> template <>
struct point_mutable_traits<Slic3r::Point> { struct point_mutable_traits<Slic3r::Point> {
typedef coord_t coordinate_type; using coordinate_type = coord_t;
static inline void set(Slic3r::Point& point, orientation_2d orient, coord_t value) { static inline void set(Slic3r::Point& point, orientation_2d orient, coord_t value) {
point((orient == HORIZONTAL) ? 0 : 1) = value; point((orient == HORIZONTAL) ? 0 : 1) = value;
} }

View File

@ -72,6 +72,9 @@ public:
// Projection of a point onto the polygon. // Projection of a point onto the polygon.
Point point_projection(const Point &point) const; Point point_projection(const Point &point) const;
std::vector<float> parameter_by_length() const; std::vector<float> parameter_by_length() const;
using iterator = Points::iterator;
using const_iterator = Points::const_iterator;
}; };
inline bool operator==(const Polygon &lhs, const Polygon &rhs) { return lhs.points == rhs.points; } inline bool operator==(const Polygon &lhs, const Polygon &rhs) { return lhs.points == rhs.points; }
@ -90,6 +93,8 @@ inline double total_length(const Polygons &polylines) {
return total; return total;
} }
inline double area(const Polygon &poly) { return poly.area(); }
inline double area(const Polygons &polys) inline double area(const Polygons &polys)
{ {
double s = 0.; double s = 0.;

View File

@ -4,7 +4,6 @@
#include <libslic3r/SLA/RasterBase.hpp> #include <libslic3r/SLA/RasterBase.hpp>
#include "libslic3r/ExPolygon.hpp" #include "libslic3r/ExPolygon.hpp"
#include "libslic3r/MTUtils.hpp" #include "libslic3r/MTUtils.hpp"
#include <libnest2d/backends/clipper/clipper_polygon.hpp>
// For rasterizing // For rasterizing
#include <agg/agg_basics.h> #include <agg/agg_basics.h>
@ -21,10 +20,7 @@
namespace Slic3r { namespace Slic3r {
inline const Polygon& contour(const ExPolygon& p) { return p.contour; } inline const Polygon& contour(const ExPolygon& p) { return p.contour; }
inline const ClipperLib::Path& contour(const ClipperLib::Polygon& p) { return p.Contour; }
inline const Polygons& holes(const ExPolygon& p) { return p.holes; } inline const Polygons& holes(const ExPolygon& p) { return p.holes; }
inline const ClipperLib::Paths& holes(const ClipperLib::Polygon& p) { return p.Holes; }
namespace sla { namespace sla {
@ -77,8 +73,6 @@ protected:
double getPx(const Point &p) { return p(0) * m_pxdim_scaled.w_mm; } double getPx(const Point &p) { return p(0) * m_pxdim_scaled.w_mm; }
double getPy(const Point &p) { return p(1) * m_pxdim_scaled.h_mm; } double getPy(const Point &p) { return p(1) * m_pxdim_scaled.h_mm; }
agg::path_storage to_path(const Polygon &poly) { return to_path(poly.points); } agg::path_storage to_path(const Polygon &poly) { return to_path(poly.points); }
double getPx(const ClipperLib::IntPoint &p) { return p.X * m_pxdim_scaled.w_mm; }
double getPy(const ClipperLib::IntPoint& p) { return p.Y * m_pxdim_scaled.h_mm; }
template<class PointVec> agg::path_storage _to_path(const PointVec& v) template<class PointVec> agg::path_storage _to_path(const PointVec& v)
{ {
@ -168,7 +162,6 @@ public:
} }
void draw(const ExPolygon &poly) override { _draw(poly); } void draw(const ExPolygon &poly) override { _draw(poly); }
void draw(const ClipperLib::Polygon &poly) override { _draw(poly); }
EncodedRaster encode(RasterEncoder encoder) const override EncodedRaster encode(RasterEncoder encoder) const override
{ {

View File

@ -11,8 +11,6 @@
#include <libslic3r/ExPolygon.hpp> #include <libslic3r/ExPolygon.hpp>
#include <libslic3r/SLA/Concurrency.hpp> #include <libslic3r/SLA/Concurrency.hpp>
namespace ClipperLib { struct Polygon; }
namespace Slic3r { namespace Slic3r {
template<class T> using uqptr = std::unique_ptr<T>; template<class T> using uqptr = std::unique_ptr<T>;
@ -92,7 +90,6 @@ public:
/// Draw a polygon with holes. /// Draw a polygon with holes.
virtual void draw(const ExPolygon& poly) = 0; virtual void draw(const ExPolygon& poly) = 0;
virtual void draw(const ClipperLib::Polygon& poly) = 0;
/// Get the resolution of the raster. /// Get the resolution of the raster.
virtual Resolution resolution() const = 0; virtual Resolution resolution() const = 0;

View File

@ -12,11 +12,9 @@
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "Tesselate.hpp" #include "Tesselate.hpp"
#include "ExPolygonCollection.hpp" #include "ExPolygonCollection.hpp"
#include "MinAreaBoundingBox.hpp"
#include "libslic3r.h" #include "libslic3r.h"
#include "libnest2d/backends/clipper/geometries.hpp"
#include "libnest2d/utils/rotcalipers.hpp"
#include <iostream> #include <iostream>
#include <random> #include <random>
@ -400,7 +398,7 @@ std::vector<Vec2f> sample_expolygon(const ExPolygons &expolys, float samples_per
void sample_expolygon_boundary(const ExPolygon & expoly, void sample_expolygon_boundary(const ExPolygon & expoly,
float samples_per_mm, float samples_per_mm,
std::vector<Vec2f> &out, std::vector<Vec2f> &out,
std::mt19937 & rng) std::mt19937 & /*rng*/)
{ {
double point_stepping_scaled = scale_(1.f) / samples_per_mm; double point_stepping_scaled = scale_(1.f) / samples_per_mm;
for (size_t i_contour = 0; i_contour <= expoly.holes.size(); ++ i_contour) { for (size_t i_contour = 0; i_contour <= expoly.holes.size(); ++ i_contour) {
@ -553,9 +551,8 @@ void SupportPointGenerator::uniformly_cover(const ExPolygons& islands, Structure
// auto bb = get_extents(islands); // auto bb = get_extents(islands);
if (flags & icfIsNew) { if (flags & icfIsNew) {
auto chull_ex = ExPolygonCollection{islands}.convex_hull(); auto chull = ExPolygonCollection{islands}.convex_hull();
auto chull = Slic3rMultiPoint_to_ClipperPath(chull_ex); auto rotbox = MinAreaBoundigBox{chull, MinAreaBoundigBox::pcConvex};
auto rotbox = libnest2d::minAreaBoundingBox(chull);
Vec2d bbdim = {unscaled(rotbox.width()), unscaled(rotbox.height())}; Vec2d bbdim = {unscaled(rotbox.width()), unscaled(rotbox.height())};
if (bbdim.x() > bbdim.y()) std::swap(bbdim.x(), bbdim.y()); if (bbdim.x() > bbdim.y()) std::swap(bbdim.x(), bbdim.y());

View File

@ -9,7 +9,6 @@
#include "Point.hpp" #include "Point.hpp"
#include "MTUtils.hpp" #include "MTUtils.hpp"
#include "Zipper.hpp" #include "Zipper.hpp"
#include <libnest2d/backends/clipper/clipper_polygon.hpp>
namespace Slic3r { namespace Slic3r {
@ -483,7 +482,7 @@ public:
// The collection of slice records for the current level. // The collection of slice records for the current level.
std::vector<std::reference_wrapper<const SliceRecord>> m_slices; std::vector<std::reference_wrapper<const SliceRecord>> m_slices;
std::vector<ClipperLib::Polygon> m_transformed_slices; ExPolygons m_transformed_slices;
template<class Container> void transformed_slices(Container&& c) template<class Container> void transformed_slices(Container&& c)
{ {
@ -507,7 +506,7 @@ public:
auto slices() const -> const decltype (m_slices)& { return m_slices; } auto slices() const -> const decltype (m_slices)& { return m_slices; }
const std::vector<ClipperLib::Polygon> & transformed_slices() const { const ExPolygons & transformed_slices() const {
return m_transformed_slices; return m_transformed_slices;
} }
}; };

View File

@ -16,9 +16,6 @@
#include <libslic3r/ClipperUtils.hpp> #include <libslic3r/ClipperUtils.hpp>
// For geometry algorithms with native Clipper types (no copies and conversions)
#include <libnest2d/backends/clipper/geometries.hpp>
#include <boost/log/trivial.hpp> #include <boost/log/trivial.hpp>
#include "I18N.hpp" #include "I18N.hpp"
@ -717,55 +714,49 @@ void SLAPrint::Steps::slice_supports(SLAPrintObject &po) {
report_status(-2, "", SlicingStatus::RELOAD_SLA_PREVIEW); report_status(-2, "", SlicingStatus::RELOAD_SLA_PREVIEW);
} }
using ClipperPoint = ClipperLib::IntPoint; //static ClipperPolygons polyunion(const ClipperPolygons &subjects)
using ClipperPolygon = ClipperLib::Polygon; // see clipper_polygon.hpp in libnest2d //{
using ClipperPolygons = std::vector<ClipperPolygon>; // ClipperLib::Clipper clipper;
static ClipperPolygons polyunion(const ClipperPolygons &subjects) // bool closed = true;
{
ClipperLib::Clipper clipper;
bool closed = true; // for(auto& path : subjects) {
// clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
// clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed);
// }
for(auto& path : subjects) { // auto mode = ClipperLib::pftPositive;
clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed);
}
auto mode = ClipperLib::pftPositive; // return libnest2d::clipper_execute(clipper, ClipperLib::ctUnion, mode, mode);
//}
return libnest2d::clipper_execute(clipper, ClipperLib::ctUnion, mode, mode); //static ClipperPolygons polydiff(const ClipperPolygons &subjects, const ClipperPolygons& clips)
} //{
// ClipperLib::Clipper clipper;
static ClipperPolygons polydiff(const ClipperPolygons &subjects, const ClipperPolygons& clips) // bool closed = true;
{
ClipperLib::Clipper clipper;
bool closed = true; // for(auto& path : subjects) {
// clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed);
// clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed);
// }
for(auto& path : subjects) { // for(auto& path : clips) {
clipper.AddPath(path.Contour, ClipperLib::ptSubject, closed); // clipper.AddPath(path.Contour, ClipperLib::ptClip, closed);
clipper.AddPaths(path.Holes, ClipperLib::ptSubject, closed); // clipper.AddPaths(path.Holes, ClipperLib::ptClip, closed);
} // }
for(auto& path : clips) { // auto mode = ClipperLib::pftPositive;
clipper.AddPath(path.Contour, ClipperLib::ptClip, closed);
clipper.AddPaths(path.Holes, ClipperLib::ptClip, closed);
}
auto mode = ClipperLib::pftPositive; // return libnest2d::clipper_execute(clipper, ClipperLib::ctDifference, mode, mode);
//}
return libnest2d::clipper_execute(clipper, ClipperLib::ctDifference, mode, mode);
}
// get polygons for all instances in the object // get polygons for all instances in the object
static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o) static ExPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o)
{ {
namespace sl = libnest2d::sl;
if (!record.print_obj()) return {}; if (!record.print_obj()) return {};
ClipperPolygons polygons; ExPolygons polygons;
auto &input_polygons = record.get_slice(o); auto &input_polygons = record.get_slice(o);
auto &instances = record.print_obj()->instances(); auto &instances = record.print_obj()->instances();
bool is_lefthanded = record.print_obj()->is_left_handed(); bool is_lefthanded = record.print_obj()->is_left_handed();
@ -776,43 +767,42 @@ static ClipperPolygons get_all_polygons(const SliceRecord& record, SliceOrigin o
for (size_t i = 0; i < instances.size(); ++i) for (size_t i = 0; i < instances.size(); ++i)
{ {
ClipperPolygon poly; ExPolygon poly;
// We need to reverse if is_lefthanded is true but // We need to reverse if is_lefthanded is true but
bool needreverse = is_lefthanded; bool needreverse = is_lefthanded;
// should be a move // should be a move
poly.Contour.reserve(polygon.contour.size() + 1); poly.contour.points.reserve(polygon.contour.size() + 1);
auto& cntr = polygon.contour.points; auto& cntr = polygon.contour.points;
if(needreverse) if(needreverse)
for(auto it = cntr.rbegin(); it != cntr.rend(); ++it) for(auto it = cntr.rbegin(); it != cntr.rend(); ++it)
poly.Contour.emplace_back(it->x(), it->y()); poly.contour.points.emplace_back(it->x(), it->y());
else else
for(auto& p : cntr) for(auto& p : cntr)
poly.Contour.emplace_back(p.x(), p.y()); poly.contour.points.emplace_back(p.x(), p.y());
for(auto& h : polygon.holes) { for(auto& h : polygon.holes) {
poly.Holes.emplace_back(); poly.holes.emplace_back();
auto& hole = poly.Holes.back(); auto& hole = poly.holes.back();
hole.reserve(h.points.size() + 1); hole.points.reserve(h.points.size() + 1);
if(needreverse) if(needreverse)
for(auto it = h.points.rbegin(); it != h.points.rend(); ++it) for(auto it = h.points.rbegin(); it != h.points.rend(); ++it)
hole.emplace_back(it->x(), it->y()); hole.points.emplace_back(it->x(), it->y());
else else
for(auto& p : h.points) for(auto& p : h.points)
hole.emplace_back(p.x(), p.y()); hole.points.emplace_back(p.x(), p.y());
} }
if(is_lefthanded) { if(is_lefthanded) {
for(auto& p : poly.Contour) p.X = -p.X; for(auto& p : poly.contour) p.x() = -p.x();
for(auto& h : poly.Holes) for(auto& p : h) p.X = -p.X; for(auto& h : poly.holes) for(auto& p : h) p.x() = -p.x();
} }
sl::rotate(poly, double(instances[i].rotation)); poly.rotate(double(instances[i].rotation));
sl::translate(poly, ClipperPoint{instances[i].shift.x(), poly.translate(Point{instances[i].shift.x(), instances[i].shift.y()});
instances[i].shift.y()});
polygons.emplace_back(std::move(poly)); polygons.emplace_back(std::move(poly));
} }
@ -878,9 +868,6 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
print_statistics.clear(); print_statistics.clear();
// libnest calculates positive area for clockwise polygons, Slic3r is in counter-clockwise
auto areafn = [](const ClipperPolygon& poly) { return - libnest2d::sl::area(poly); };
const double area_fill = printer_config.area_fill.getFloat()*0.01;// 0.5 (50%); const double area_fill = printer_config.area_fill.getFloat()*0.01;// 0.5 (50%);
const double fast_tilt = printer_config.fast_tilt_time.getFloat();// 5.0; const double fast_tilt = printer_config.fast_tilt_time.getFloat();// 5.0;
const double slow_tilt = printer_config.slow_tilt_time.getFloat();// 8.0; const double slow_tilt = printer_config.slow_tilt_time.getFloat();// 8.0;
@ -913,7 +900,7 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
// Going to parallel: // Going to parallel:
auto printlayerfn = [this, auto printlayerfn = [this,
// functions and read only vars // functions and read only vars
areafn, area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, delta_fade_time, area_fill, display_area, exp_time, init_exp_time, fast_tilt, slow_tilt, delta_fade_time,
// write vars // write vars
&mutex, &models_volume, &supports_volume, &estim_time, &slow_layers, &mutex, &models_volume, &supports_volume, &estim_time, &slow_layers,
@ -931,8 +918,8 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
// Calculation of the consumed material // Calculation of the consumed material
ClipperPolygons model_polygons; ExPolygons model_polygons;
ClipperPolygons supports_polygons; ExPolygons supports_polygons;
size_t c = std::accumulate(layer.slices().begin(), size_t c = std::accumulate(layer.slices().begin(),
layer.slices().end(), layer.slices().end(),
@ -954,44 +941,44 @@ void SLAPrint::Steps::merge_slices_and_eval_stats() {
for(const SliceRecord& record : layer.slices()) { for(const SliceRecord& record : layer.slices()) {
ClipperPolygons modelslices = get_all_polygons(record, soModel); ExPolygons modelslices = get_all_polygons(record, soModel);
for(ClipperPolygon& p_tmp : modelslices) model_polygons.emplace_back(std::move(p_tmp)); for(ExPolygon& p_tmp : modelslices) model_polygons.emplace_back(std::move(p_tmp));
ClipperPolygons supportslices = get_all_polygons(record, soSupport); ExPolygons supportslices = get_all_polygons(record, soSupport);
for(ClipperPolygon& p_tmp : supportslices) supports_polygons.emplace_back(std::move(p_tmp)); for(ExPolygon& p_tmp : supportslices) supports_polygons.emplace_back(std::move(p_tmp));
} }
model_polygons = polyunion(model_polygons); model_polygons = union_ex(model_polygons);
double layer_model_area = 0; double layer_model_area = 0;
for (const ClipperPolygon& polygon : model_polygons) for (const ExPolygon& polygon : model_polygons)
layer_model_area += areafn(polygon); layer_model_area += area(polygon);
if (layer_model_area < 0 || layer_model_area > 0) { if (layer_model_area < 0 || layer_model_area > 0) {
Lock lck(mutex); models_volume += layer_model_area * l_height; Lock lck(mutex); models_volume += layer_model_area * l_height;
} }
if(!supports_polygons.empty()) { if(!supports_polygons.empty()) {
if(model_polygons.empty()) supports_polygons = polyunion(supports_polygons); if(model_polygons.empty()) supports_polygons = union_ex(supports_polygons);
else supports_polygons = polydiff(supports_polygons, model_polygons); else supports_polygons = diff_ex(supports_polygons, model_polygons);
// allegedly, union of subject is done withing the diff according to the pftPositive polyFillType // allegedly, union of subject is done withing the diff according to the pftPositive polyFillType
} }
double layer_support_area = 0; double layer_support_area = 0;
for (const ClipperPolygon& polygon : supports_polygons) for (const ExPolygon& polygon : supports_polygons)
layer_support_area += areafn(polygon); layer_support_area += area(polygon);
if (layer_support_area < 0 || layer_support_area > 0) { if (layer_support_area < 0 || layer_support_area > 0) {
Lock lck(mutex); supports_volume += layer_support_area * l_height; Lock lck(mutex); supports_volume += layer_support_area * l_height;
} }
// Here we can save the expensively calculated polygons for printing // Here we can save the expensively calculated polygons for printing
ClipperPolygons trslices; ExPolygons trslices;
trslices.reserve(model_polygons.size() + supports_polygons.size()); trslices.reserve(model_polygons.size() + supports_polygons.size());
for(ClipperPolygon& poly : model_polygons) trslices.emplace_back(std::move(poly)); for(ExPolygon& poly : model_polygons) trslices.emplace_back(std::move(poly));
for(ClipperPolygon& poly : supports_polygons) trslices.emplace_back(std::move(poly)); for(ExPolygon& poly : supports_polygons) trslices.emplace_back(std::move(poly));
layer.transformed_slices(polyunion(trslices)); layer.transformed_slices(union_ex(trslices));
// Calculation of the slow and fast layers to the future controlling those values on FW // Calculation of the slow and fast layers to the future controlling those values on FW
@ -1074,7 +1061,7 @@ void SLAPrint::Steps::rasterize()
PrintLayer& printlayer = m_print->m_printer_input[idx]; PrintLayer& printlayer = m_print->m_printer_input[idx];
if(canceled()) return; if(canceled()) return;
for (const ClipperLib::Polygon& poly : printlayer.transformed_slices()) for (const ExPolygon& poly : printlayer.transformed_slices())
raster.draw(poly); raster.draw(poly);
// Status indication guarded with the spinlock // Status indication guarded with the spinlock

View File

@ -273,8 +273,8 @@ std::string SVG::get_path_d(const ClipperLib::Path &path, double scale, bool clo
std::ostringstream d; std::ostringstream d;
d << "M "; d << "M ";
for (ClipperLib::Path::const_iterator p = path.begin(); p != path.end(); ++p) { for (ClipperLib::Path::const_iterator p = path.begin(); p != path.end(); ++p) {
d << to_svg_x(scale * p->X - origin(0)) << " "; d << to_svg_x(scale * p->x() - origin(0)) << " ";
d << to_svg_y(scale * p->Y - origin(1)) << " "; d << to_svg_y(scale * p->y() - origin(1)) << " ";
} }
if (closed) d << "z"; if (closed) d << "z";
return d.str(); return d.str();

13
src/libslic3r/clipper.cpp Normal file
View File

@ -0,0 +1,13 @@
// Hackish wrapper around the ClipperLib library to compile the Clipper library using Slic3r::Point.
#include "clipper.hpp"
// Don't include <clipper/clipper.hpp> for the second time.
#define clipper_hpp
// Override ClipperLib namespace to Slic3r::ClipperLib
#define CLIPPERLIB_NAMESPACE_PREFIX Slic3r
// Override Slic3r::ClipperLib::IntPoint to Slic3r::Point
#define CLIPPERLIB_INTPOINT_TYPE Slic3r::Point
#include <clipper/clipper.cpp>

26
src/libslic3r/clipper.hpp Normal file
View File

@ -0,0 +1,26 @@
// Hackish wrapper around the ClipperLib library to compile the Clipper library using Slic3r's own Point type.
#ifndef slic3r_clipper_hpp
#ifdef clipper_hpp
#error "You should include the libslic3r/clipper.hpp before clipper/clipper.hpp"
#endif
#ifdef CLIPPERLIB_USE_XYZ
#error "Something went wrong. Using clipper.hpp with Slic3r Point type, but CLIPPERLIB_USE_XYZ is defined."
#endif
#define slic3r_clipper_hpp
#include "Point.hpp"
#define CLIPPERLIB_NAMESPACE_PREFIX Slic3r
#define CLIPPERLIB_INTPOINT_TYPE Slic3r::Point
#include <clipper/clipper.hpp>
#undef clipper_hpp
#undef CLIPPERLIB_NAMESPACE_PREFIX
#undef CLIPPERLIB_INTPOINT_TYPE
#endif // slic3r_clipper_hpp

View File

@ -308,6 +308,10 @@ IntegerOnly<I, std::vector<T, Args...>> reserve_vector(I capacity)
return ret; return ret;
} }
// Borrowed from C++20
template<class T>
using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
} // namespace Slic3r } // namespace Slic3r
#endif #endif

View File

@ -114,7 +114,7 @@
#include <cereal/types/base_class.hpp> #include <cereal/types/base_class.hpp>
#include <clipper/clipper_z.hpp> #include <clipper/clipper_z.hpp>
#include <clipper/clipper.hpp> #include "clipper.hpp"
#include "BoundingBox.hpp" #include "BoundingBox.hpp"
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "Config.hpp" #include "Config.hpp"
@ -129,8 +129,6 @@
#include "libslic3r.h" #include "libslic3r.h"
#include "libslic3r_version.h" #include "libslic3r_version.h"
#include "clipper.hpp"
#include <Shiny/Shiny.h> #include <Shiny/Shiny.h>
#include <admesh/stl.h> #include <admesh/stl.h>

View File

@ -4,4 +4,4 @@ target_link_libraries(${_TEST_NAME}_tests test_common libnest2d )
set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests") set_property(TARGET ${_TEST_NAME}_tests PROPERTY FOLDER "tests")
# catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ") # catch_discover_tests(${_TEST_NAME}_tests TEST_PREFIX "${_TEST_NAME}: ")
add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests ${CATCH_EXTRA_ARGS}) add_test(${_TEST_NAME}_tests ${_TEST_NAME}_tests "${CATCH_EXTRA_ARGS} exclude:[NotWorking]")

View File

@ -44,12 +44,74 @@ struct NfpImpl<S, NfpLevel::CONVEX_ONLY>
} }
} }
namespace {
using namespace libnest2d;
template<int64_t SCALE = 1, class It>
void exportSVG(const char *loc, It from, It to) {
static const char* svg_header =
R"raw(<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg height="500" width="500" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
)raw";
// for(auto r : result) {
std::fstream out(loc, std::fstream::out);
if(out.is_open()) {
out << svg_header;
// Item rbin( RectangleItem(bin.width(), bin.height()) );
// for(unsigned j = 0; j < rbin.vertexCount(); j++) {
// auto v = rbin.vertex(j);
// setY(v, -getY(v)/SCALE + 500 );
// setX(v, getX(v)/SCALE);
// rbin.setVertex(j, v);
// }
// out << shapelike::serialize<Formats::SVG>(rbin.rawShape()) << std::endl;
for(auto it = from; it != to; ++it) {
const Item &itm = *it;
Item tsh(itm.transformedShape());
for(unsigned j = 0; j < tsh.vertexCount(); j++) {
auto v = tsh.vertex(j);
setY(v, -getY(v)/SCALE + 500);
setX(v, getX(v)/SCALE);
tsh.setVertex(j, v);
}
out << shapelike::serialize<Formats::SVG>(tsh.rawShape()) << std::endl;
}
out << "\n</svg>" << std::endl;
}
out.close();
// i++;
// }
}
template<int64_t SCALE = 1>
void exportSVG(std::vector<std::reference_wrapper<Item>>& result, int idx = 0) {
exportSVG<SCALE>((std::string("out") + std::to_string(idx) + ".svg").c_str(),
result.begin(), result.end());
}
}
static std::vector<libnest2d::Item>& prusaParts() { static std::vector<libnest2d::Item>& prusaParts() {
static std::vector<libnest2d::Item> ret; using namespace libnest2d;
static std::vector<Item> ret;
if(ret.empty()) { if(ret.empty()) {
ret.reserve(PRINTER_PART_POLYGONS.size()); ret.reserve(PRINTER_PART_POLYGONS.size());
for(auto& inp : PRINTER_PART_POLYGONS) ret.emplace_back(inp); for(auto& inp : PRINTER_PART_POLYGONS) {
auto inp_cpy = inp;
if (ClosureTypeV<PathImpl> == Closure::OPEN)
inp_cpy.points.pop_back();
if constexpr (!libnest2d::is_clockwise<libnest2d::PathImpl>())
std::reverse(inp_cpy.begin(), inp_cpy.end());
ret.emplace_back(inp_cpy);
}
} }
return ret; return ret;
@ -140,15 +202,15 @@ TEST_CASE("boundingCircle", "[Geometry]") {
PolygonImpl p = {{{0, 10}, {10, 0}, {0, -10}, {0, 10}}, {}}; PolygonImpl p = {{{0, 10}, {10, 0}, {0, -10}, {0, 10}}, {}};
Circle c = boundingCircle(p); Circle c = boundingCircle(p);
REQUIRE(c.center().X == 0); REQUIRE(getX(c.center()) == 0);
REQUIRE(c.center().Y == 0); REQUIRE(getY(c.center()) == 0);
REQUIRE(c.radius() == Approx(10)); REQUIRE(c.radius() == Approx(10));
shapelike::translate(p, PointImpl{10, 10}); shapelike::translate(p, PointImpl{10, 10});
c = boundingCircle(p); c = boundingCircle(p);
REQUIRE(c.center().X == 10); REQUIRE(getX(c.center()) == 10);
REQUIRE(c.center().Y == 10); REQUIRE(getY(c.center()) == 10);
REQUIRE(c.radius() == Approx(10)); REQUIRE(c.radius() == Approx(10));
auto parts = prusaParts(); auto parts = prusaParts();
@ -243,7 +305,7 @@ TEST_CASE("Area", "[Geometry]") {
{61, 97} {61, 97}
}; };
REQUIRE(shapelike::area(item.transformedShape()) > 0 ); REQUIRE(std::abs(shapelike::area(item.transformedShape())) > 0 );
} }
TEST_CASE("IsPointInsidePolygon", "[Geometry]") { TEST_CASE("IsPointInsidePolygon", "[Geometry]") {
@ -296,30 +358,36 @@ TEST_CASE("LeftAndDownPolygon", "[Geometry]")
Box bin(100, 100); Box bin(100, 100);
BottomLeftPlacer placer(bin); BottomLeftPlacer placer(bin);
Item item = {{70, 75}, {88, 60}, {65, 50}, {60, 30}, {80, 20}, {42, 20}, PathImpl pitem = {{70, 75}, {88, 60}, {65, 50}, {60, 30}, {80, 20},
{35, 35}, {35, 55}, {40, 75}, {70, 75}}; {42, 20}, {35, 35}, {35, 55}, {40, 75}};
Item leftControl = { {40, 75}, PathImpl pleftControl = {{40, 75}, {35, 55}, {35, 35},
{35, 55}, {42, 20}, {0, 20}, {0, 75}};
{35, 35},
{42, 20},
{0, 20},
{0, 75},
{40, 75}};
Item downControl = {{88, 60}, PathImpl pdownControl = {{88, 60}, {88, 0}, {35, 0}, {35, 35},
{88, 0}, {42, 20}, {80, 20}, {60, 30}, {65, 50}};
{35, 0},
{35, 35},
{42, 20},
{80, 20},
{60, 30},
{65, 50},
{88, 60}};
if constexpr (!is_clockwise<PathImpl>()) {
std::reverse(sl::begin(pitem), sl::end(pitem));
std::reverse(sl::begin(pleftControl), sl::end(pleftControl));
std::reverse(sl::begin(pdownControl), sl::end(pdownControl));
}
if constexpr (ClosureTypeV<PathImpl> == Closure::CLOSED) {
sl::addVertex(pitem, sl::front(pitem));
sl::addVertex(pleftControl, sl::front(pleftControl));
sl::addVertex(pdownControl, sl::front(pdownControl));
}
Item item{pitem}, leftControl{pleftControl}, downControl{pdownControl};
Item leftp(placer.leftPoly(item)); Item leftp(placer.leftPoly(item));
REQUIRE(shapelike::isValid(leftp.rawShape()).first); auto valid = sl::isValid(leftp.rawShape());
std::vector<std::reference_wrapper<Item>> to_export{ leftp, leftControl };
exportSVG<1>("leftp.svg", to_export.begin(), to_export.end());
REQUIRE(valid.first);
REQUIRE(leftp.vertexCount() == leftControl.vertexCount()); REQUIRE(leftp.vertexCount() == leftControl.vertexCount());
for(unsigned long i = 0; i < leftControl.vertexCount(); i++) { for(unsigned long i = 0; i < leftControl.vertexCount(); i++) {
@ -338,7 +406,7 @@ TEST_CASE("LeftAndDownPolygon", "[Geometry]")
} }
} }
TEST_CASE("ArrangeRectanglesTight", "[Nesting]") TEST_CASE("ArrangeRectanglesTight", "[Nesting][NotWorking]")
{ {
using namespace libnest2d; using namespace libnest2d;
@ -390,6 +458,8 @@ TEST_CASE("ArrangeRectanglesTight", "[Nesting]")
// check for no intersections, no containment: // check for no intersections, no containment:
// exportSVG<1>("arrangeRectanglesTight.svg", rects.begin(), rects.end());
bool valid = true; bool valid = true;
for(Item& r1 : rects) { for(Item& r1 : rects) {
for(Item& r2 : rects) { for(Item& r2 : rects) {
@ -470,57 +540,7 @@ TEST_CASE("ArrangeRectanglesLoose", "[Nesting]")
} }
namespace { TEST_CASE("BottomLeftStressTest", "[Geometry][NotWorking]") {
using namespace libnest2d;
template<int64_t SCALE = 1, class It>
void exportSVG(const char *loc, It from, It to) {
static const char* svg_header =
R"raw(<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg height="500" width="500" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
)raw";
// for(auto r : result) {
std::fstream out(loc, std::fstream::out);
if(out.is_open()) {
out << svg_header;
// Item rbin( RectangleItem(bin.width(), bin.height()) );
// for(unsigned j = 0; j < rbin.vertexCount(); j++) {
// auto v = rbin.vertex(j);
// setY(v, -getY(v)/SCALE + 500 );
// setX(v, getX(v)/SCALE);
// rbin.setVertex(j, v);
// }
// out << shapelike::serialize<Formats::SVG>(rbin.rawShape()) << std::endl;
for(auto it = from; it != to; ++it) {
const Item &itm = *it;
Item tsh(itm.transformedShape());
for(unsigned j = 0; j < tsh.vertexCount(); j++) {
auto v = tsh.vertex(j);
setY(v, -getY(v)/SCALE + 500);
setX(v, getX(v)/SCALE);
tsh.setVertex(j, v);
}
out << shapelike::serialize<Formats::SVG>(tsh.rawShape()) << std::endl;
}
out << "\n</svg>" << std::endl;
}
out.close();
// i++;
// }
}
template<int64_t SCALE = 1>
void exportSVG(std::vector<std::reference_wrapper<Item>>& result, int idx = 0) {
exportSVG((std::string("out") + std::to_string(idx) + ".svg").c_str(),
result.begin(), result.end());
}
}
TEST_CASE("BottomLeftStressTest", "[Geometry]") {
using namespace libnest2d; using namespace libnest2d;
const Coord SCALE = 1000000; const Coord SCALE = 1000000;
@ -563,7 +583,7 @@ TEST_CASE("BottomLeftStressTest", "[Geometry]") {
TEST_CASE("convexHull", "[Geometry]") { TEST_CASE("convexHull", "[Geometry]") {
using namespace libnest2d; using namespace libnest2d;
ClipperLib::Path poly = PRINTER_PART_POLYGONS[0]; PathImpl poly = PRINTER_PART_POLYGONS[0];
auto chull = sl::convexHull(poly); auto chull = sl::convexHull(poly);
@ -597,7 +617,7 @@ TEST_CASE("PrusaPartsShouldFitIntoTwoBins", "[Nesting]") {
})); }));
// Gather the items into piles of arranged polygons... // Gather the items into piles of arranged polygons...
using Pile = TMultiShape<ClipperLib::Polygon>; using Pile = TMultiShape<PolygonImpl>;
std::vector<Pile> piles(bins); std::vector<Pile> piles(bins);
for (auto &itm : input) for (auto &itm : input)
@ -609,6 +629,20 @@ TEST_CASE("PrusaPartsShouldFitIntoTwoBins", "[Nesting]") {
auto bb = sl::boundingBox(pile); auto bb = sl::boundingBox(pile);
REQUIRE(sl::isInside(bb, bin)); REQUIRE(sl::isInside(bb, bin));
} }
// Check the area of merged pile vs the sum of area of all the parts
// They should match, otherwise there is an overlap which should not happen.
for (auto &pile : piles) {
double area_sum = 0.;
for (auto &obj : pile)
area_sum += sl::area(obj);
auto pile_m = nfp::merge(pile);
double area_merge = sl::area(pile_m);
REQUIRE(area_sum == Approx(area_merge));
}
} }
TEST_CASE("EmptyItemShouldBeUntouched", "[Nesting]") { TEST_CASE("EmptyItemShouldBeUntouched", "[Nesting]") {
@ -616,7 +650,7 @@ TEST_CASE("EmptyItemShouldBeUntouched", "[Nesting]") {
std::vector<Item> items; std::vector<Item> items;
items.emplace_back(Item{}); // Emplace empty item items.emplace_back(Item{}); // Emplace empty item
items.emplace_back(Item{0, 200, 0}); // Emplace zero area item items.emplace_back(Item{ {0, 200} }); // Emplace zero area item
size_t bins = libnest2d::nest(items, bin); size_t bins = libnest2d::nest(items, bin);
@ -661,12 +695,12 @@ TEST_CASE("Items can be preloaded", "[Nesting]") {
REQUIRE(bins == 1); REQUIRE(bins == 1);
REQUIRE(fixed_rect.binId() == 0); REQUIRE(fixed_rect.binId() == 0);
REQUIRE(fixed_rect.translation().X == bin.center().X); REQUIRE(getX(fixed_rect.translation()) == getX(bin.center()));
REQUIRE(fixed_rect.translation().Y == bin.center().Y); REQUIRE(getY(fixed_rect.translation()) == getY(bin.center()));
REQUIRE(movable_rect.binId() == 0); REQUIRE(movable_rect.binId() == 0);
REQUIRE(movable_rect.translation().X != bin.center().X); REQUIRE(getX(movable_rect.translation()) != getX(bin.center()));
REQUIRE(movable_rect.translation().Y != bin.center().Y); REQUIRE(getY(movable_rect.translation()) != getY(bin.center()));
} }
SECTION("Preloaded Item should not affect free bins") { SECTION("Preloaded Item should not affect free bins") {
@ -677,14 +711,14 @@ TEST_CASE("Items can be preloaded", "[Nesting]") {
REQUIRE(bins == 2); REQUIRE(bins == 2);
REQUIRE(fixed_rect.binId() == 1); REQUIRE(fixed_rect.binId() == 1);
REQUIRE(fixed_rect.translation().X == bin.center().X); REQUIRE(getX(fixed_rect.translation()) == getX(bin.center()));
REQUIRE(fixed_rect.translation().Y == bin.center().Y); REQUIRE(getY(fixed_rect.translation()) == getY(bin.center()));
REQUIRE(movable_rect.binId() == 0); REQUIRE(movable_rect.binId() == 0);
auto bb = movable_rect.boundingBox(); auto bb = movable_rect.boundingBox();
REQUIRE(bb.center().X == bin.center().X); REQUIRE(getX(bb.center()) == getX(bin.center()));
REQUIRE(bb.center().Y == bin.center().Y); REQUIRE(getY(bb.center()) == getY(bin.center()));
} }
} }
@ -700,15 +734,13 @@ std::vector<ItemPair> nfp_testdata = {
{ {
{80, 50}, {80, 50},
{100, 70}, {100, 70},
{120, 50}, {120, 50}
{80, 50}
}, },
{ {
{10, 10}, {10, 10},
{10, 40}, {10, 40},
{40, 40}, {40, 40},
{40, 10}, {40, 10}
{10, 10}
} }
}, },
{ {
@ -718,15 +750,13 @@ std::vector<ItemPair> nfp_testdata = {
{80, 90}, {80, 90},
{120, 90}, {120, 90},
{140, 70}, {140, 70},
{120, 50}, {120, 50}
{80, 50}
}, },
{ {
{10, 10}, {10, 10},
{10, 40}, {10, 40},
{40, 40}, {40, 40},
{40, 10}, {40, 10}
{10, 10}
} }
}, },
{ {
@ -738,15 +768,13 @@ std::vector<ItemPair> nfp_testdata = {
{30, 40}, {30, 40},
{40, 40}, {40, 40},
{50, 30}, {50, 30},
{50, 20}, {50, 20}
{40, 10}
}, },
{ {
{80, 0}, {80, 0},
{80, 30}, {80, 30},
{110, 30}, {110, 30},
{110, 0}, {110, 0}
{80, 0}
} }
}, },
{ {
@ -766,8 +794,7 @@ std::vector<ItemPair> nfp_testdata = {
{122, 97}, {122, 97},
{120, 98}, {120, 98},
{118, 101}, {118, 101},
{117, 103}, {117, 103}
{117, 107}
}, },
{ {
{102, 116}, {102, 116},
@ -777,8 +804,7 @@ std::vector<ItemPair> nfp_testdata = {
{148, 100}, {148, 100},
{148, 85}, {148, 85},
{147, 84}, {147, 84},
{102, 84}, {102, 84}
{102, 116},
} }
}, },
{ {
@ -793,8 +819,7 @@ std::vector<ItemPair> nfp_testdata = {
{139, 68}, {139, 68},
{111, 68}, {111, 68},
{108, 70}, {108, 70},
{99, 102}, {99, 102}
{99, 122},
}, },
{ {
{107, 124}, {107, 124},
@ -810,8 +835,7 @@ std::vector<ItemPair> nfp_testdata = {
{136, 86}, {136, 86},
{134, 85}, {134, 85},
{108, 85}, {108, 85},
{107, 86}, {107, 86}
{107, 124},
} }
}, },
{ {
@ -825,8 +849,7 @@ std::vector<ItemPair> nfp_testdata = {
{156, 66}, {156, 66},
{133, 57}, {133, 57},
{132, 57}, {132, 57},
{91, 98}, {91, 98}
{91, 100},
}, },
{ {
{101, 90}, {101, 90},
@ -843,8 +866,7 @@ std::vector<ItemPair> nfp_testdata = {
{145, 84}, {145, 84},
{105, 84}, {105, 84},
{102, 87}, {102, 87},
{101, 89}, {101, 89}
{101, 90},
} }
} }
}; };
@ -860,8 +882,7 @@ std::vector<ItemPair> nfp_testdata = {
{533659, 157607}, {533659, 157607},
{538669, 160091}, {538669, 160091},
{537178, 142155}, {537178, 142155},
{534959, 143386}, {534959, 143386}
{533726, 142141},
} }
}, },
{ {
@ -884,8 +905,7 @@ std::vector<ItemPair> nfp_testdata = {
{209315, 17080}, {209315, 17080},
{205326, 17080}, {205326, 17080},
{203334, 13629}, {203334, 13629},
{204493, 11616}, {204493, 11616}
{118305, 11603},
} }
}, },
} }
@ -957,6 +977,14 @@ void testNfp(const std::vector<ItemPair>& testdata) {
for(auto& td : testdata) { for(auto& td : testdata) {
auto orbiter = td.orbiter; auto orbiter = td.orbiter;
auto stationary = td.stationary; auto stationary = td.stationary;
if (!libnest2d::is_clockwise<PolygonImpl>()) {
auto porb = orbiter.rawShape();
auto pstat = stationary.rawShape();
std::reverse(sl::begin(porb), sl::end(porb));
std::reverse(sl::begin(pstat), sl::end(pstat));
orbiter = Item{porb};
stationary = Item{pstat};
}
onetest(orbiter, stationary, tidx++); onetest(orbiter, stationary, tidx++);
} }
@ -964,6 +992,14 @@ void testNfp(const std::vector<ItemPair>& testdata) {
for(auto& td : testdata) { for(auto& td : testdata) {
auto orbiter = td.stationary; auto orbiter = td.stationary;
auto stationary = td.orbiter; auto stationary = td.orbiter;
if (!libnest2d::is_clockwise<PolygonImpl>()) {
auto porb = orbiter.rawShape();
auto pstat = stationary.rawShape();
std::reverse(sl::begin(porb), sl::end(porb));
std::reverse(sl::begin(pstat), sl::end(pstat));
orbiter = Item{porb};
stationary = Item{pstat};
}
onetest(orbiter, stationary, tidx++); onetest(orbiter, stationary, tidx++);
} }
} }
@ -1073,7 +1109,7 @@ using Ratio = boost::rational<boost::multiprecision::int128_t>;
TEST_CASE("MinAreaBBWithRotatingCalipers", "[Geometry]") { TEST_CASE("MinAreaBBWithRotatingCalipers", "[Geometry]") {
long double err_epsilon = 500e6l; long double err_epsilon = 500e6l;
for(ClipperLib::Path rinput : PRINTER_PART_POLYGONS) { for(PathImpl rinput : PRINTER_PART_POLYGONS) {
PolygonImpl poly(rinput); PolygonImpl poly(rinput);
long double arearef = refMinAreaBox(poly); long double arearef = refMinAreaBox(poly);
@ -1085,8 +1121,8 @@ TEST_CASE("MinAreaBBWithRotatingCalipers", "[Geometry]") {
REQUIRE(succ); REQUIRE(succ);
} }
for(ClipperLib::Path rinput : STEGOSAUR_POLYGONS) { for(PathImpl rinput : STEGOSAUR_POLYGONS) {
rinput.pop_back(); // rinput.pop_back();
std::reverse(rinput.begin(), rinput.end()); std::reverse(rinput.begin(), rinput.end());
PolygonImpl poly(removeCollinearPoints<PathImpl, PointImpl, Unit>(rinput, 1000000)); PolygonImpl poly(removeCollinearPoints<PathImpl, PointImpl, Unit>(rinput, 1000000));
@ -1116,7 +1152,7 @@ template<class It> MultiPolygon merged_pile(It from, It to, int bin_id)
TEST_CASE("Test for bed center distance optimization", "[Nesting], [NestKernels]") TEST_CASE("Test for bed center distance optimization", "[Nesting], [NestKernels]")
{ {
static const constexpr ClipperLib::cInt W = 10000000; static const constexpr Slic3r::ClipperLib::cInt W = 10000000;
// Get the input items and define the bin. // Get the input items and define the bin.
std::vector<RectangleItem> input(9, {W, W}); std::vector<RectangleItem> input(9, {W, W});
@ -1151,7 +1187,7 @@ TEST_CASE("Test for bed center distance optimization", "[Nesting], [NestKernels]
TEST_CASE("Test for biggest bounding box area", "[Nesting], [NestKernels]") TEST_CASE("Test for biggest bounding box area", "[Nesting], [NestKernels]")
{ {
static const constexpr ClipperLib::cInt W = 10000000; static const constexpr Slic3r::ClipperLib::cInt W = 10000000;
static const constexpr size_t N = 100; static const constexpr size_t N = 100;
// Get the input items and define the bin. // Get the input items and define the bin.

View File

@ -26,7 +26,7 @@ namespace Slic3r {
pt.Y += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA; pt.Y += CLIPPER_OFFSET_SCALE_ROUNDING_DELTA;
pt.X >>= CLIPPER_OFFSET_POWER_OF_2; pt.X >>= CLIPPER_OFFSET_POWER_OF_2;
pt.Y >>= CLIPPER_OFFSET_POWER_OF_2; pt.Y >>= CLIPPER_OFFSET_POWER_OF_2;
out.emplace_back(coord_t(pt.X), coord_t(pt.Y)); out.emplace_back(coord_t(pt.x()), coord_t(pt.y()));
} }
return out; return out;
} }

View File

@ -2,7 +2,6 @@
%{ %{
#include <xsinit.h> #include <xsinit.h>
#include "clipper.hpp"
#include "libslic3r/ClipperUtils.hpp" #include "libslic3r/ClipperUtils.hpp"
%} %}
@ -21,10 +20,10 @@ _constant()
OUTPUT: RETVAL OUTPUT: RETVAL
Polygons Polygons
offset(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3) offset(polygons, delta, joinType = Slic3r::ClipperLib::jtMiter, miterLimit = 3)
Polygons polygons Polygons polygons
const float delta const float delta
ClipperLib::JoinType joinType Slic3r::ClipperLib::JoinType joinType
double miterLimit double miterLimit
CODE: CODE:
RETVAL = offset(polygons, delta, joinType, miterLimit); RETVAL = offset(polygons, delta, joinType, miterLimit);
@ -32,10 +31,10 @@ offset(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3)
RETVAL RETVAL
ExPolygons ExPolygons
offset_ex(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3) offset_ex(polygons, delta, joinType = Slic3r::ClipperLib::jtMiter, miterLimit = 3)
Polygons polygons Polygons polygons
const float delta const float delta
ClipperLib::JoinType joinType Slic3r::ClipperLib::JoinType joinType
double miterLimit double miterLimit
CODE: CODE:
RETVAL = offset_ex(polygons, delta, joinType, miterLimit); RETVAL = offset_ex(polygons, delta, joinType, miterLimit);
@ -43,11 +42,11 @@ offset_ex(polygons, delta, joinType = ClipperLib::jtMiter, miterLimit = 3)
RETVAL RETVAL
Polygons Polygons
offset2(polygons, delta1, delta2, joinType = ClipperLib::jtMiter, miterLimit = 3) offset2(polygons, delta1, delta2, joinType = Slic3r::ClipperLib::jtMiter, miterLimit = 3)
Polygons polygons Polygons polygons
const float delta1 const float delta1
const float delta2 const float delta2
ClipperLib::JoinType joinType Slic3r::ClipperLib::JoinType joinType
double miterLimit double miterLimit
CODE: CODE:
RETVAL = offset2(polygons, delta1, delta2, joinType, miterLimit); RETVAL = offset2(polygons, delta1, delta2, joinType, miterLimit);
@ -55,11 +54,11 @@ offset2(polygons, delta1, delta2, joinType = ClipperLib::jtMiter, miterLimit = 3
RETVAL RETVAL
ExPolygons ExPolygons
offset2_ex(polygons, delta1, delta2, joinType = ClipperLib::jtMiter, miterLimit = 3) offset2_ex(polygons, delta1, delta2, joinType = Slic3r::ClipperLib::jtMiter, miterLimit = 3)
Polygons polygons Polygons polygons
const float delta1 const float delta1
const float delta2 const float delta2
ClipperLib::JoinType joinType Slic3r::ClipperLib::JoinType joinType
double miterLimit double miterLimit
CODE: CODE:
RETVAL = offset2_ex(polygons, delta1, delta2, joinType, miterLimit); RETVAL = offset2_ex(polygons, delta1, delta2, joinType, miterLimit);

View File

@ -79,9 +79,9 @@ Polyline::rotate(angle, center_sv)
THIS->rotate(angle, center); THIS->rotate(angle, center);
Polygons Polygons
Polyline::grow(delta, joinType = ClipperLib::jtSquare, miterLimit = 3) Polyline::grow(delta, joinType = Slic3r::ClipperLib::jtSquare, miterLimit = 3)
const float delta const float delta
ClipperLib::JoinType joinType Slic3r::ClipperLib::JoinType joinType
double miterLimit double miterLimit
CODE: CODE:
RETVAL = offset(*THIS, delta, joinType, miterLimit); RETVAL = offset(*THIS, delta, joinType, miterLimit);

View File

@ -85,7 +85,7 @@ Surface::polygons()
Surfaces Surfaces
Surface::offset(delta, joinType = ClipperLib::jtMiter, miterLimit = 3) Surface::offset(delta, joinType = ClipperLib::jtMiter, miterLimit = 3)
const float delta const float delta
ClipperLib::JoinType joinType Slic3r::ClipperLib::JoinType joinType
double miterLimit double miterLimit
CODE: CODE:
surfaces_append(RETVAL, offset_ex(THIS->expolygon, delta, joinType, miterLimit), *THIS); surfaces_append(RETVAL, offset_ex(THIS->expolygon, delta, joinType, miterLimit), *THIS);

View File

@ -211,8 +211,8 @@ FlowRole T_UV
PrintStep T_UV PrintStep T_UV
PrintObjectStep T_UV PrintObjectStep T_UV
SurfaceType T_UV SurfaceType T_UV
ClipperLib::JoinType T_UV Slic3r::ClipperLib::JoinType T_UV
ClipperLib::PolyFillType T_UV Slic3r::ClipperLib::PolyFillType T_UV
# we return these types whenever we want the items to be cloned # we return these types whenever we want the items to be cloned
Points T_ARRAYREF Points T_ARRAYREF