Fixed conflicts after merge with master

This commit is contained in:
enricoturri1966 2021-04-30 14:54:58 +02:00
commit 395164c7a1
96 changed files with 3797 additions and 2294 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)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 221 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 31 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 31 KiB

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();
@ -288,12 +296,18 @@ inline NfpResult<RawShape> nfpConvexOnly(const RawShape& sh,
// If Ratio is an actual rational type, there is no precision loss // If Ratio is an actual rational type, there is no precision loss
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];
return q[0] < 2 ? pcos1 < pcos2 : pcos1 > pcos2; if constexpr (is_clockwise<RawShape>())
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.
return q[0] > q[1]; if constexpr (is_clockwise<RawShape>())
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,44 +365,50 @@ 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
sl::addVertex(rsh, topleft_vertex); if constexpr (ClosureTypeV<RawShape> == Closure::CLOSED)
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

@ -471,8 +471,8 @@ bool ConfigBase::set_deserialize_nothrow(const t_config_option_key &opt_key_src,
{ {
t_config_option_key opt_key = opt_key_src; t_config_option_key opt_key = opt_key_src;
std::string value = value_src; std::string value = value_src;
// Both opt_key and value may be modified by _handle_legacy(). // Both opt_key and value may be modified by handle_legacy().
// If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by _handle_legacy(). // If the opt_key is no more valid in this version of Slic3r, opt_key is cleared by handle_legacy().
this->handle_legacy(opt_key, value); this->handle_legacy(opt_key, value);
if (opt_key.empty()) if (opt_key.empty())
// Ignore the option. // Ignore the option.

View File

@ -18,11 +18,53 @@
#include <boost/algorithm/string/trim.hpp> #include <boost/algorithm/string/trim.hpp>
#include <boost/format/format_fwd.hpp> #include <boost/format/format_fwd.hpp>
#include <boost/functional/hash.hpp>
#include <boost/property_tree/ptree_fwd.hpp> #include <boost/property_tree/ptree_fwd.hpp>
#include <cereal/access.hpp> #include <cereal/access.hpp>
#include <cereal/types/base_class.hpp> #include <cereal/types/base_class.hpp>
namespace Slic3r {
struct FloatOrPercent
{
double value;
bool percent;
private:
friend class cereal::access;
template<class Archive> void serialize(Archive& ar) { ar(this->value); ar(this->percent); }
};
inline bool operator==(const FloatOrPercent& l, const FloatOrPercent& r) throw() { return l.value == r.value && l.percent == r.percent; }
inline bool operator!=(const FloatOrPercent& l, const FloatOrPercent& r) throw() { return !(l == r); }
}
namespace std {
template<> struct hash<Slic3r::FloatOrPercent> {
std::size_t operator()(const Slic3r::FloatOrPercent& v) const noexcept {
std::size_t seed = std::hash<double>{}(v.value);
return v.percent ? seed ^ 0x9e3779b9 : seed;
}
};
template<> struct hash<Slic3r::Vec2d> {
std::size_t operator()(const Slic3r::Vec2d& v) const noexcept {
std::size_t seed = std::hash<double>{}(v.x());
boost::hash_combine(seed, std::hash<double>{}(v.y()));
return seed;
}
};
template<> struct hash<Slic3r::Vec3d> {
std::size_t operator()(const Slic3r::Vec3d& v) const noexcept {
std::size_t seed = std::hash<double>{}(v.x());
boost::hash_combine(seed, std::hash<double>{}(v.y()));
boost::hash_combine(seed, std::hash<double>{}(v.z()));
return seed;
}
};
}
namespace Slic3r { namespace Slic3r {
// Name of the configuration option. // Name of the configuration option.
@ -137,6 +179,7 @@ public:
virtual void setInt(int /* val */) { throw BadOptionTypeException("Calling ConfigOption::setInt on a non-int ConfigOption"); } virtual void setInt(int /* val */) { throw BadOptionTypeException("Calling ConfigOption::setInt on a non-int ConfigOption"); }
virtual bool operator==(const ConfigOption &rhs) const = 0; virtual bool operator==(const ConfigOption &rhs) const = 0;
bool operator!=(const ConfigOption &rhs) const { return ! (*this == rhs); } bool operator!=(const ConfigOption &rhs) const { return ! (*this == rhs); }
virtual size_t hash() const throw() = 0;
bool is_scalar() const { return (int(this->type()) & int(coVectorType)) == 0; } bool is_scalar() const { return (int(this->type()) & int(coVectorType)) == 0; }
bool is_vector() const { return ! this->is_scalar(); } bool is_vector() const { return ! this->is_scalar(); }
// If this option is nullable, then it may have its value or values set to nil. // If this option is nullable, then it may have its value or values set to nil.
@ -185,8 +228,10 @@ public:
return this->value == static_cast<const ConfigOptionSingle<T>*>(&rhs)->value; return this->value == static_cast<const ConfigOptionSingle<T>*>(&rhs)->value;
} }
bool operator==(const T &rhs) const { return this->value == rhs; } bool operator==(const T &rhs) const throw() { return this->value == rhs; }
bool operator!=(const T &rhs) const { return this->value != rhs; } bool operator!=(const T &rhs) const throw() { return this->value != rhs; }
size_t hash() const throw() override { return std::hash<T>{}(this->value); }
private: private:
friend class cereal::access; friend class cereal::access;
@ -339,8 +384,16 @@ public:
return this->values == static_cast<const ConfigOptionVector<T>*>(&rhs)->values; return this->values == static_cast<const ConfigOptionVector<T>*>(&rhs)->values;
} }
bool operator==(const std::vector<T> &rhs) const { return this->values == rhs; } bool operator==(const std::vector<T> &rhs) const throw() { return this->values == rhs; }
bool operator!=(const std::vector<T> &rhs) const { return this->values != rhs; } bool operator!=(const std::vector<T> &rhs) const throw() { return this->values != rhs; }
size_t hash() const throw() override {
std::hash<T> hasher;
size_t seed = 0;
for (const auto &v : this->values)
boost::hash_combine(seed, hasher(v));
return seed;
}
// Is this option overridden by another option? // Is this option overridden by another option?
// An option overrides another option if it is not nil and not equal. // An option overrides another option if it is not nil and not equal.
@ -413,7 +466,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
double getFloat() const override { return this->value; } double getFloat() const override { return this->value; }
ConfigOption* clone() const override { return new ConfigOptionFloat(*this); } ConfigOption* clone() const override { return new ConfigOptionFloat(*this); }
bool operator==(const ConfigOptionFloat &rhs) const { return this->value == rhs.value; } bool operator==(const ConfigOptionFloat &rhs) const throw() { return this->value == rhs.value; }
std::string serialize() const override std::string serialize() const override
{ {
@ -454,7 +507,7 @@ public:
static ConfigOptionType static_type() { return coFloats; } static ConfigOptionType static_type() { return coFloats; }
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionFloatsTempl(*this); } ConfigOption* clone() const override { return new ConfigOptionFloatsTempl(*this); }
bool operator==(const ConfigOptionFloatsTempl &rhs) const { return vectors_equal(this->values, rhs.values); } bool operator==(const ConfigOptionFloatsTempl &rhs) const throw() { return vectors_equal(this->values, rhs.values); }
bool operator==(const ConfigOption &rhs) const override { bool operator==(const ConfigOption &rhs) const override {
if (rhs.type() != this->type()) if (rhs.type() != this->type())
throw Slic3r::RuntimeError("ConfigOptionFloatsTempl: Comparing incompatible types"); throw Slic3r::RuntimeError("ConfigOptionFloatsTempl: Comparing incompatible types");
@ -566,7 +619,7 @@ public:
int getInt() const override { return this->value; } int getInt() const override { return this->value; }
void setInt(int val) override { this->value = val; } void setInt(int val) override { this->value = val; }
ConfigOption* clone() const override { return new ConfigOptionInt(*this); } ConfigOption* clone() const override { return new ConfigOptionInt(*this); }
bool operator==(const ConfigOptionInt &rhs) const { return this->value == rhs.value; } bool operator==(const ConfigOptionInt &rhs) const throw() { return this->value == rhs.value; }
std::string serialize() const override std::string serialize() const override
{ {
@ -606,7 +659,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionIntsTempl(*this); } ConfigOption* clone() const override { return new ConfigOptionIntsTempl(*this); }
ConfigOptionIntsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionIntsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionIntsTempl &rhs) const { return this->values == rhs.values; } bool operator==(const ConfigOptionIntsTempl &rhs) const throw() { return this->values == rhs.values; }
// Could a special "nil" value be stored inside the vector, indicating undefined value? // Could a special "nil" value be stored inside the vector, indicating undefined value?
bool nullable() const override { return NULLABLE; } bool nullable() const override { return NULLABLE; }
// Special "nil" value to be stored into the vector if this->supports_nil(). // Special "nil" value to be stored into the vector if this->supports_nil().
@ -689,7 +742,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionString(*this); } ConfigOption* clone() const override { return new ConfigOptionString(*this); }
ConfigOptionString& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionString& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionString &rhs) const { return this->value == rhs.value; } bool operator==(const ConfigOptionString &rhs) const throw() { return this->value == rhs.value; }
bool empty() const { return this->value.empty(); } bool empty() const { return this->value.empty(); }
std::string serialize() const override std::string serialize() const override
@ -722,7 +775,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionStrings(*this); } ConfigOption* clone() const override { return new ConfigOptionStrings(*this); }
ConfigOptionStrings& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionStrings& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionStrings &rhs) const { return this->values == rhs.values; } bool operator==(const ConfigOptionStrings &rhs) const throw() { return this->values == rhs.values; }
bool is_nil(size_t) const override { return false; } bool is_nil(size_t) const override { return false; }
std::string serialize() const override std::string serialize() const override
@ -757,7 +810,8 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionPercent(*this); } ConfigOption* clone() const override { return new ConfigOptionPercent(*this); }
ConfigOptionPercent& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionPercent& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPercent &rhs) const { return this->value == rhs.value; } bool operator==(const ConfigOptionPercent &rhs) const throw() { return this->value == rhs.value; }
double get_abs_value(double ratio_over) const { return ratio_over * this->value / 100; } double get_abs_value(double ratio_over) const { return ratio_over * this->value / 100; }
std::string serialize() const override std::string serialize() const override
@ -796,8 +850,8 @@ public:
static ConfigOptionType static_type() { return coPercents; } static ConfigOptionType static_type() { return coPercents; }
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionPercentsTempl(*this); } ConfigOption* clone() const override { return new ConfigOptionPercentsTempl(*this); }
ConfigOptionPercentsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionPercentsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPercentsTempl &rhs) const { return this->values == rhs.values; } bool operator==(const ConfigOptionPercentsTempl &rhs) const throw() { return this->values == rhs.values; }
std::string serialize() const override std::string serialize() const override
{ {
@ -856,8 +910,12 @@ public:
assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(&rhs)); assert(dynamic_cast<const ConfigOptionFloatOrPercent*>(&rhs));
return *this == *static_cast<const ConfigOptionFloatOrPercent*>(&rhs); return *this == *static_cast<const ConfigOptionFloatOrPercent*>(&rhs);
} }
bool operator==(const ConfigOptionFloatOrPercent &rhs) const bool operator==(const ConfigOptionFloatOrPercent &rhs) const throw()
{ return this->value == rhs.value && this->percent == rhs.percent; } { return this->value == rhs.value && this->percent == rhs.percent; }
size_t hash() const throw() override {
size_t seed = std::hash<double>{}(this->value);
return this->percent ? seed ^ 0x9e3779b9 : seed;
}
double get_abs_value(double ratio_over) const double get_abs_value(double ratio_over) const
{ return this->percent ? (ratio_over * this->value / 100) : this->value; } { return this->percent ? (ratio_over * this->value / 100) : this->value; }
@ -891,27 +949,6 @@ private:
template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionPercent>(this), percent); } template<class Archive> void serialize(Archive &ar) { ar(cereal::base_class<ConfigOptionPercent>(this), percent); }
}; };
struct FloatOrPercent
{
double value;
bool percent;
private:
friend class cereal::access;
template<class Archive> void serialize(Archive & ar) { ar(this->value); ar(this->percent); }
};
inline bool operator==(const FloatOrPercent &l, const FloatOrPercent &r)
{
return l.value == r.value && l.percent == r.percent;
}
inline bool operator!=(const FloatOrPercent& l, const FloatOrPercent& r)
{
return !(l == r);
}
template<bool NULLABLE> template<bool NULLABLE>
class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVector<FloatOrPercent> class ConfigOptionFloatsOrPercentsTempl : public ConfigOptionVector<FloatOrPercent>
{ {
@ -925,13 +962,14 @@ public:
static ConfigOptionType static_type() { return coFloatsOrPercents; } static ConfigOptionType static_type() { return coFloatsOrPercents; }
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionFloatsOrPercentsTempl(*this); } ConfigOption* clone() const override { return new ConfigOptionFloatsOrPercentsTempl(*this); }
bool operator==(const ConfigOptionFloatsOrPercentsTempl &rhs) const { return vectors_equal(this->values, rhs.values); } bool operator==(const ConfigOptionFloatsOrPercentsTempl &rhs) const throw() { return vectors_equal(this->values, rhs.values); }
bool operator==(const ConfigOption &rhs) const override { bool operator==(const ConfigOption &rhs) const override {
if (rhs.type() != this->type()) if (rhs.type() != this->type())
throw Slic3r::RuntimeError("ConfigOptionFloatsOrPercentsTempl: Comparing incompatible types"); throw Slic3r::RuntimeError("ConfigOptionFloatsOrPercentsTempl: Comparing incompatible types");
assert(dynamic_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs)); assert(dynamic_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs));
return vectors_equal(this->values, static_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs)->values); return vectors_equal(this->values, static_cast<const ConfigOptionVector<FloatOrPercent>*>(&rhs)->values);
} }
// Could a special "nil" value be stored inside the vector, indicating undefined value? // Could a special "nil" value be stored inside the vector, indicating undefined value?
bool nullable() const override { return NULLABLE; } bool nullable() const override { return NULLABLE; }
// Special "nil" value to be stored into the vector if this->supports_nil(). // Special "nil" value to be stored into the vector if this->supports_nil().
@ -1038,7 +1076,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionPoint(*this); } ConfigOption* clone() const override { return new ConfigOptionPoint(*this); }
ConfigOptionPoint& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionPoint& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPoint &rhs) const { return this->value == rhs.value; } bool operator==(const ConfigOptionPoint &rhs) const throw() { return this->value == rhs.value; }
std::string serialize() const override std::string serialize() const override
{ {
@ -1074,7 +1112,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionPoints(*this); } ConfigOption* clone() const override { return new ConfigOptionPoints(*this); }
ConfigOptionPoints& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionPoints& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPoints &rhs) const { return this->values == rhs.values; } bool operator==(const ConfigOptionPoints &rhs) const throw() { return this->values == rhs.values; }
bool is_nil(size_t) const override { return false; } bool is_nil(size_t) const override { return false; }
std::string serialize() const override std::string serialize() const override
@ -1146,7 +1184,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionPoint3(*this); } ConfigOption* clone() const override { return new ConfigOptionPoint3(*this); }
ConfigOptionPoint3& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionPoint3& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionPoint3 &rhs) const { return this->value == rhs.value; } bool operator==(const ConfigOptionPoint3 &rhs) const throw() { return this->value == rhs.value; }
std::string serialize() const override std::string serialize() const override
{ {
@ -1183,7 +1221,7 @@ public:
bool getBool() const override { return this->value; } bool getBool() const override { return this->value; }
ConfigOption* clone() const override { return new ConfigOptionBool(*this); } ConfigOption* clone() const override { return new ConfigOptionBool(*this); }
ConfigOptionBool& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionBool& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionBool &rhs) const { return this->value == rhs.value; } bool operator==(const ConfigOptionBool &rhs) const throw() { return this->value == rhs.value; }
std::string serialize() const override std::string serialize() const override
{ {
@ -1217,7 +1255,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionBoolsTempl(*this); } ConfigOption* clone() const override { return new ConfigOptionBoolsTempl(*this); }
ConfigOptionBoolsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionBoolsTempl& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionBoolsTempl &rhs) const { return this->values == rhs.values; } bool operator==(const ConfigOptionBoolsTempl &rhs) const throw() { return this->values == rhs.values; }
// Could a special "nil" value be stored inside the vector, indicating undefined value? // Could a special "nil" value be stored inside the vector, indicating undefined value?
bool nullable() const override { return NULLABLE; } bool nullable() const override { return NULLABLE; }
// Special "nil" value to be stored into the vector if this->supports_nil(). // Special "nil" value to be stored into the vector if this->supports_nil().
@ -1311,7 +1349,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionEnum<T>(*this); } ConfigOption* clone() const override { return new ConfigOptionEnum<T>(*this); }
ConfigOptionEnum<T>& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionEnum<T>& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionEnum<T> &rhs) const { return this->value == rhs.value; } bool operator==(const ConfigOptionEnum<T> &rhs) const throw() { return this->value == rhs.value; }
int getInt() const override { return (int)this->value; } int getInt() const override { return (int)this->value; }
void setInt(int val) override { this->value = T(val); } void setInt(int val) override { this->value = T(val); }
@ -1397,7 +1435,7 @@ public:
ConfigOptionType type() const override { return static_type(); } ConfigOptionType type() const override { return static_type(); }
ConfigOption* clone() const override { return new ConfigOptionEnumGeneric(*this); } ConfigOption* clone() const override { return new ConfigOptionEnumGeneric(*this); }
ConfigOptionEnumGeneric& operator=(const ConfigOption *opt) { this->set(opt); return *this; } ConfigOptionEnumGeneric& operator=(const ConfigOption *opt) { this->set(opt); return *this; }
bool operator==(const ConfigOptionEnumGeneric &rhs) const { return this->value == rhs.value; } bool operator==(const ConfigOptionEnumGeneric &rhs) const throw() { return this->value == rhs.value; }
bool operator==(const ConfigOption &rhs) const override bool operator==(const ConfigOption &rhs) const override
{ {

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

@ -318,7 +318,7 @@ std::string ExtrusionEntity::role_to_string(ExtrusionRole role)
case erIroning : return L("Ironing"); case erIroning : return L("Ironing");
case erBridgeInfill : return L("Bridge infill"); case erBridgeInfill : return L("Bridge infill");
case erGapFill : return L("Gap fill"); case erGapFill : return L("Gap fill");
case erSkirt : return L("Skirt"); case erSkirt : return L("Skirt/Brim");
case erSupportMaterial : return L("Support material"); case erSupportMaterial : return L("Support material");
case erSupportMaterialInterface : return L("Support material interface"); case erSupportMaterialInterface : return L("Support material interface");
case erWipeTower : return L("Wipe tower"); case erWipeTower : return L("Wipe tower");
@ -349,7 +349,7 @@ ExtrusionRole ExtrusionEntity::string_to_role(const std::string_view role)
return erBridgeInfill; return erBridgeInfill;
else if (role == L("Gap fill")) else if (role == L("Gap fill"))
return erGapFill; return erGapFill;
else if (role == L("Skirt")) else if (role == L("Skirt") || role == L("Skirt/Brim")) // "Skirt" is for backward compatibility with 2.3.1 and earlier
return erSkirt; return erSkirt;
else if (role == L("Support material")) else if (role == L("Support material"))
return erSupportMaterial; return erSupportMaterial;

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

@ -89,18 +89,11 @@ double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionFloat
if (opt->percent) { if (opt->percent) {
auto opt_key_layer_height = first_layer ? "first_layer_height" : "layer_height"; auto opt_key_layer_height = first_layer ? "first_layer_height" : "layer_height";
auto opt_layer_height = config.option(opt_key_layer_height); auto opt_layer_height = config.option(opt_key_layer_height);
if (opt_layer_height == nullptr) if (opt_layer_height == nullptr)
throw_on_missing_variable(opt_key, opt_key_layer_height); throw_on_missing_variable(opt_key, opt_key_layer_height);
double layer_height = opt_layer_height->getFloat(); assert(! first_layer || ! static_cast<const ConfigOptionFloatOrPercent*>(opt_layer_height)->percent);
if (first_layer && static_cast<const ConfigOptionFloatOrPercent*>(opt_layer_height)->percent) { return opt->get_abs_value(opt_layer_height->getFloat());
// first_layer_height depends on layer_height.
opt_layer_height = config.option("layer_height");
if (opt_layer_height == nullptr)
throw_on_missing_variable(opt_key, "layer_height");
layer_height *= 0.01 * opt_layer_height->getFloat();
}
return opt->get_abs_value(layer_height);
} }
if (opt->value == 0.) { if (opt->value == 0.) {
@ -238,13 +231,14 @@ Flow support_material_flow(const PrintObject *object, float layer_height)
Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height) Flow support_material_1st_layer_flow(const PrintObject *object, float layer_height)
{ {
const auto &width = (object->print()->config().first_layer_extrusion_width.value > 0) ? object->print()->config().first_layer_extrusion_width : object->config().support_material_extrusion_width; const PrintConfig &print_config = object->print()->config();
const auto &width = (print_config.first_layer_extrusion_width.value > 0) ? print_config.first_layer_extrusion_width : object->config().support_material_extrusion_width;
return Flow::new_from_config_width( return Flow::new_from_config_width(
frSupportMaterial, frSupportMaterial,
// The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution. // The width parameter accepted by new_from_config_width is of type ConfigOptionFloatOrPercent, the Flow class takes care of the percent to value substitution.
(width.value > 0) ? width : object->config().extrusion_width, (width.value > 0) ? width : object->config().extrusion_width,
float(object->print()->config().nozzle_diameter.get_at(object->config().support_material_extruder-1)), float(print_config.nozzle_diameter.get_at(object->config().support_material_extruder-1)),
(layer_height > 0.f) ? layer_height : float(object->config().first_layer_height.get_abs_value(object->config().layer_height.value))); (layer_height > 0.f) ? layer_height : float(print_config.first_layer_height.get_abs_value(object->config().layer_height.value)));
} }
Flow support_material_interface_flow(const PrintObject *object, float layer_height) Flow support_material_interface_flow(const PrintObject *object, float layer_height)

View File

@ -248,7 +248,7 @@ std::vector<ExPolygons> extract_slices_from_sla_archive(
{ {
double incr, val, prev; double incr, val, prev;
bool stop = false; bool stop = false;
tbb::spin_mutex mutex; tbb::spin_mutex mutex = {};
} st {100. / slices.size(), 0., 0.}; } st {100. / slices.size(), 0., 0.};
tbb::parallel_for(size_t(0), arch.images.size(), tbb::parallel_for(size_t(0), arch.images.size(),
@ -371,6 +371,13 @@ void fill_iniconf(ConfMap &m, const SLAPrint &print)
m["numSlow"] = std::to_string(stats.slow_layers_count); m["numSlow"] = std::to_string(stats.slow_layers_count);
m["numFast"] = std::to_string(stats.fast_layers_count); m["numFast"] = std::to_string(stats.fast_layers_count);
m["printTime"] = std::to_string(stats.estimated_print_time); m["printTime"] = std::to_string(stats.estimated_print_time);
bool hollow_en = false;
auto it = print.objects().begin();
while (!hollow_en && it != print.objects().end())
hollow_en = (*it++)->config().hollowing_enable;
m["hollow"] = hollow_en ? "1" : "0";
m["action"] = "print"; m["action"] = "print";
} }

View File

@ -1111,7 +1111,8 @@ void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thu
// Write some terse information on the slicing parameters. // Write some terse information on the slicing parameters.
const PrintObject *first_object = print.objects().front(); const PrintObject *first_object = print.objects().front();
const double layer_height = first_object->config().layer_height.value; const double layer_height = first_object->config().layer_height.value;
const double first_layer_height = first_object->config().first_layer_height.get_abs_value(layer_height); assert(! print.config().first_layer_height.percent);
const double first_layer_height = print.config().first_layer_height.value;
for (const PrintRegion* region : print.regions()) { for (const PrintRegion* region : print.regions()) {
_write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frExternalPerimeter, layer_height).width()); _write_format(file, "; external perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frExternalPerimeter, layer_height).width());
_write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frPerimeter, layer_height).width()); _write_format(file, "; perimeters extrusion width = %.2fmm\n", region->flow(*first_object, frPerimeter, layer_height).width());

View File

@ -326,7 +326,7 @@ std::vector<PerExtruderAdjustments> CoolingBuffer::parse_layer_gcode(const std::
PerExtruderAdjustments *adjustment = &per_extruder_adjustments[map_extruder_to_per_extruder_adjustment[current_extruder]]; PerExtruderAdjustments *adjustment = &per_extruder_adjustments[map_extruder_to_per_extruder_adjustment[current_extruder]];
const char *line_start = gcode.c_str(); const char *line_start = gcode.c_str();
const char *line_end = line_start; const char *line_end = line_start;
const char extrusion_axis = config.get_extrusion_axis()[0]; const char extrusion_axis = get_extrusion_axis(config)[0];
// Index of an existing CoolingLine of the current adjustment, which holds the feedrate setting command // Index of an existing CoolingLine of the current adjustment, which holds the feedrate setting command
// for a sequence of extrusion moves. // for a sequence of extrusion moves.
size_t active_speed_modifier = size_t(-1); size_t active_speed_modifier = size_t(-1);

View File

@ -857,6 +857,12 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_time_processor.export_remaining_time_enabled = config.remaining_times.value; m_time_processor.export_remaining_time_enabled = config.remaining_times.value;
m_use_volumetric_e = config.use_volumetric_e; m_use_volumetric_e = config.use_volumetric_e;
#if ENABLE_START_GCODE_VISUALIZATION
const ConfigOptionFloatOrPercent* first_layer_height = config.option<ConfigOptionFloatOrPercent>("first_layer_height");
if (first_layer_height != nullptr)
m_first_layer_height = std::abs(first_layer_height->value);
#endif // ENABLE_START_GCODE_VISUALIZATION
} }
void GCodeProcessor::apply_config(const DynamicPrintConfig& config) void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
@ -1035,6 +1041,12 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
const ConfigOptionBool* use_volumetric_e = config.option<ConfigOptionBool>("use_volumetric_e"); const ConfigOptionBool* use_volumetric_e = config.option<ConfigOptionBool>("use_volumetric_e");
if (use_volumetric_e != nullptr) if (use_volumetric_e != nullptr)
m_use_volumetric_e = use_volumetric_e->value; m_use_volumetric_e = use_volumetric_e->value;
#if ENABLE_START_GCODE_VISUALIZATION
const ConfigOptionFloatOrPercent* first_layer_height = config.option<ConfigOptionFloatOrPercent>("first_layer_height");
if (first_layer_height != nullptr)
m_first_layer_height = std::abs(first_layer_height->value);
#endif // ENABLE_START_GCODE_VISUALIZATION
} }
void GCodeProcessor::enable_stealth_time_estimator(bool enabled) void GCodeProcessor::enable_stealth_time_estimator(bool enabled)
@ -1085,6 +1097,10 @@ void GCodeProcessor::reset()
m_filament_diameters = std::vector<float>(Min_Extruder_Count, 1.75f); m_filament_diameters = std::vector<float>(Min_Extruder_Count, 1.75f);
m_extruded_last_z = 0.0f; m_extruded_last_z = 0.0f;
#if ENABLE_START_GCODE_VISUALIZATION
m_first_layer_height = 0.0f;
m_processing_start_custom_gcode = false;
#endif // ENABLE_START_GCODE_VISUALIZATION
m_g1_line_id = 0; m_g1_line_id = 0;
m_layer_id = 0; m_layer_id = 0;
m_cp_color.reset(); m_cp_color.reset();
@ -1450,6 +1466,9 @@ void GCodeProcessor::process_tags(const std::string_view comment)
if (m_extrusion_role == erExternalPerimeter) if (m_extrusion_role == erExternalPerimeter)
m_seams_detector.activate(true); m_seams_detector.activate(true);
#endif // ENABLE_SEAMS_VISUALIZATION #endif // ENABLE_SEAMS_VISUALIZATION
#if ENABLE_START_GCODE_VISUALIZATION
m_processing_start_custom_gcode = (m_extrusion_role == erCustom && m_g1_line_id == 0);
#endif // ENABLE_START_GCODE_VISUALIZATION
return; return;
} }
@ -2194,7 +2213,11 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
#endif // ENABLE_GCODE_VIEWER_DATA_CHECKING #endif // ENABLE_GCODE_VIEWER_DATA_CHECKING
} }
#if ENABLE_START_GCODE_VISUALIZATION
if (type == EMoveType::Extrude && (m_width == 0.0f || m_height == 0.0f))
#else
if (type == EMoveType::Extrude && (m_extrusion_role == erCustom || m_width == 0.0f || m_height == 0.0f)) if (type == EMoveType::Extrude && (m_extrusion_role == erCustom || m_width == 0.0f || m_height == 0.0f))
#endif // ENABLE_START_GCODE_VISUALIZATION
type = EMoveType::Travel; type = EMoveType::Travel;
// time estimate section // time estimate section
@ -2310,13 +2333,13 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
// Calculate the jerk depending on whether the axis is coasting in the same direction or reversing a direction. // Calculate the jerk depending on whether the axis is coasting in the same direction or reversing a direction.
float jerk = float jerk =
(v_exit > v_entry) ? (v_exit > v_entry) ?
(((v_entry > 0.0f) || (v_exit < 0.0f)) ? ((v_entry > 0.0f || v_exit < 0.0f) ?
// coasting // coasting
(v_exit - v_entry) : (v_exit - v_entry) :
// axis reversal // axis reversal
std::max(v_exit, -v_entry)) : std::max(v_exit, -v_entry)) :
// v_exit <= v_entry // v_exit <= v_entry
(((v_entry < 0.0f) || (v_exit > 0.0f)) ? ((v_entry < 0.0f || v_exit > 0.0f) ?
// coasting // coasting
(v_entry - v_exit) : (v_entry - v_exit) :
// axis reversal // axis reversal
@ -2337,7 +2360,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
float vmax_junction_threshold = vmax_junction * 0.99f; float vmax_junction_threshold = vmax_junction * 0.99f;
// Not coasting. The machine will stop and start the movements anyway, better to start the segment from start. // Not coasting. The machine will stop and start the movements anyway, better to start the segment from start.
if ((prev.safe_feedrate > vmax_junction_threshold) && (curr.safe_feedrate > vmax_junction_threshold)) if (prev.safe_feedrate > vmax_junction_threshold && curr.safe_feedrate > vmax_junction_threshold)
vmax_junction = curr.safe_feedrate; vmax_junction = curr.safe_feedrate;
} }
@ -2857,7 +2880,11 @@ void GCodeProcessor::store_move_vertex(EMoveType type)
m_extrusion_role, m_extrusion_role,
m_extruder_id, m_extruder_id,
m_cp_color.current, m_cp_color.current,
#if ENABLE_START_GCODE_VISUALIZATION
Vec3f(m_end_position[X], m_end_position[Y], m_processing_start_custom_gcode ? m_first_layer_height : m_end_position[Z]) + m_extruder_offsets[m_extruder_id],
#else
Vec3f(m_end_position[X], m_end_position[Y], m_end_position[Z]) + m_extruder_offsets[m_extruder_id], Vec3f(m_end_position[X], m_end_position[Y], m_end_position[Z]) + m_extruder_offsets[m_extruder_id],
#endif // ENABLE_START_GCODE_VISUALIZATION
m_end_position[E] - m_start_position[E], m_end_position[E] - m_start_position[E],
m_feedrate, m_feedrate,
m_width, m_width,

View File

@ -520,6 +520,10 @@ namespace Slic3r {
ExtruderTemps m_extruder_temps; ExtruderTemps m_extruder_temps;
std::vector<float> m_filament_diameters; std::vector<float> m_filament_diameters;
float m_extruded_last_z; float m_extruded_last_z;
#if ENABLE_START_GCODE_VISUALIZATION
float m_first_layer_height; // mm
bool m_processing_start_custom_gcode;
#endif // ENABLE_START_GCODE_VISUALIZATION
unsigned int m_g1_line_id; unsigned int m_g1_line_id;
unsigned int m_layer_id; unsigned int m_layer_id;
CpColor m_cp_color; CpColor m_cp_color;

View File

@ -546,10 +546,24 @@ WipeTower::WipeTower(const PrintConfig& config, const std::vector<std::vector<fl
m_extra_loading_move = float(config.extra_loading_move); m_extra_loading_move = float(config.extra_loading_move);
m_set_extruder_trimpot = config.high_current_on_filament_swap; m_set_extruder_trimpot = config.high_current_on_filament_swap;
} }
// Calculate where the priming lines should be - very naive test not detecting parallelograms or custom shapes // Calculate where the priming lines should be - very naive test not detecting parallelograms etc.
const std::vector<Vec2d>& bed_points = config.bed_shape.values; const std::vector<Vec2d>& bed_points = config.bed_shape.values;
BoundingBoxf bb(bed_points);
m_bed_width = float(bb.size().x());
m_bed_shape = (bed_points.size() == 4 ? RectangularBed : CircularBed); m_bed_shape = (bed_points.size() == 4 ? RectangularBed : CircularBed);
m_bed_width = float(BoundingBoxf(bed_points).size().x());
if (m_bed_shape == CircularBed) {
// this may still be a custom bed, check that the points are roughly on a circle
double r2 = std::pow(m_bed_width/2., 2.);
double lim2 = std::pow(m_bed_width/10., 2.);
Vec2d center = bb.center();
for (const Vec2d& pt : bed_points)
if (std::abs(std::pow(pt.x()-center.x(), 2.) + std::pow(pt.y()-center.y(), 2.) - r2) > lim2) {
m_bed_shape = CustomBed;
break;
}
}
m_bed_bottom_left = m_bed_shape == RectangularBed m_bed_bottom_left = m_bed_shape == RectangularBed
? Vec2f(bed_points.front().x(), bed_points.front().y()) ? Vec2f(bed_points.front().x(), bed_points.front().y())
: Vec2f::Zero(); : Vec2f::Zero();
@ -625,10 +639,12 @@ std::vector<WipeTower::ToolChangeResult> WipeTower::prime(
float prime_section_width = std::min(0.9f * m_bed_width / tools.size(), 60.f); float prime_section_width = std::min(0.9f * m_bed_width / tools.size(), 60.f);
box_coordinates cleaning_box(Vec2f(0.02f * m_bed_width, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f); box_coordinates cleaning_box(Vec2f(0.02f * m_bed_width, 0.01f + m_perimeter_width/2.f), prime_section_width, 100.f);
// In case of a circular bed, place it so it goes across the diameter and hope it will fit if (m_bed_shape == CircularBed) {
if (m_bed_shape == CircularBed) cleaning_box = box_coordinates(Vec2f(0.f, 0.f), prime_section_width, 100.f);
cleaning_box.translate(-m_bed_width/2 + m_bed_width * 0.03f, -m_bed_width * 0.12f); float total_width_half = tools.size() * prime_section_width / 2.f;
if (m_bed_shape == RectangularBed) cleaning_box.translate(-total_width_half, -std::sqrt(std::max(0.f, std::pow(m_bed_width/2, 2.f) - std::pow(1.05f * total_width_half, 2.f))));
}
else
cleaning_box.translate(m_bed_bottom_left); cleaning_box.translate(m_bed_bottom_left);
std::vector<ToolChangeResult> results; std::vector<ToolChangeResult> results;

View File

@ -277,7 +277,8 @@ private:
// Bed properties // Bed properties
enum { enum {
RectangularBed, RectangularBed,
CircularBed CircularBed,
CustomBed
} m_bed_shape; } m_bed_shape;
float m_bed_width; // width of the bed bounding box float m_bed_width; // width of the bed bounding box
Vec2f m_bed_bottom_left; // bottom-left corner coordinates (for rectangular beds) Vec2f m_bed_bottom_left; // bottom-left corner coordinates (for rectangular beds)

View File

@ -13,13 +13,13 @@ namespace Slic3r {
void GCodeReader::apply_config(const GCodeConfig &config) void GCodeReader::apply_config(const GCodeConfig &config)
{ {
m_config = config; m_config = config;
m_extrusion_axis = m_config.get_extrusion_axis()[0]; m_extrusion_axis = get_extrusion_axis(m_config)[0];
} }
void GCodeReader::apply_config(const DynamicPrintConfig &config) void GCodeReader::apply_config(const DynamicPrintConfig &config)
{ {
m_config.apply(config, true); m_config.apply(config, true);
m_extrusion_axis = m_config.get_extrusion_axis()[0]; m_extrusion_axis = get_extrusion_axis(m_config)[0];
} }
const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline, std::pair<const char*, const char*> &command) const char* GCodeReader::parse_line_internal(const char *ptr, GCodeLine &gline, std::pair<const char*, const char*> &command)

View File

@ -18,7 +18,7 @@ namespace Slic3r {
void GCodeWriter::apply_print_config(const PrintConfig &print_config) void GCodeWriter::apply_print_config(const PrintConfig &print_config)
{ {
this->config.apply(print_config, true); this->config.apply(print_config, true);
m_extrusion_axis = this->config.get_extrusion_axis(); m_extrusion_axis = get_extrusion_axis(this->config);
m_single_extruder_multi_material = print_config.single_extruder_multi_material.value; m_single_extruder_multi_material = print_config.single_extruder_multi_material.value;
bool is_marlin = print_config.gcode_flavor.value == gcfMarlinLegacy || print_config.gcode_flavor.value == gcfMarlinFirmware; bool is_marlin = print_config.gcode_flavor.value == gcfMarlinLegacy || print_config.gcode_flavor.value == gcfMarlinFirmware;
m_max_acceleration = std::lrint((is_marlin && print_config.machine_limits_usage.value == MachineLimitsUsage::EmitToGCode) ? m_max_acceleration = std::lrint((is_marlin && print_config.machine_limits_usage.value == MachineLimitsUsage::EmitToGCode) ?

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
template<class P>
libnest2d::RotatedBox<Point, Unit> minAreaBoundigBox_(
const P &p, MinAreaBoundigBox::PolygonLevel lvl)
{
P chull = lvl == MinAreaBoundigBox::pcConvex ?
p :
libnest2d::sl::convexHull(p);
libnest2d::removeCollinearPoints(chull);
return libnest2d::minAreaBoundingBox<P, Unit, Rational>(chull);
}
MinAreaBoundigBox::MinAreaBoundigBox(const Polygon &p, PolygonLevel pc) MinAreaBoundigBox::MinAreaBoundigBox(const Polygon &p, PolygonLevel pc)
{ {
const Polygon &chull = pc == pcConvex ? p : libnest2d::RotatedBox<Point, Unit> box = minAreaBoundigBox_(p, pc);
libnest2d::sl::convexHull(p);
libnest2d::RotatedBox<Point, Unit> box =
libnest2d::minAreaBoundingBox<Polygon, 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());
@ -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

@ -26,12 +26,8 @@ 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

@ -833,18 +833,6 @@ indexed_triangle_set ModelObject::raw_indexed_triangle_set() const
return out; return out;
} }
// Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes.
TriangleMesh ModelObject::full_raw_mesh() const
{
TriangleMesh mesh;
for (const ModelVolume *v : this->volumes)
{
TriangleMesh vol_mesh(v->mesh());
vol_mesh.transform(v->get_matrix());
mesh.merge(vol_mesh);
}
return mesh;
}
const BoundingBoxf3& ModelObject::raw_mesh_bounding_box() const const BoundingBoxf3& ModelObject::raw_mesh_bounding_box() const
{ {

View File

@ -289,8 +289,6 @@ public:
TriangleMesh raw_mesh() const; TriangleMesh raw_mesh() const;
// The same as above, but producing a lightweight indexed_triangle_set. // The same as above, but producing a lightweight indexed_triangle_set.
indexed_triangle_set raw_indexed_triangle_set() const; indexed_triangle_set raw_indexed_triangle_set() const;
// Non-transformed (non-rotated, non-scaled, non-translated) sum of all object volumes.
TriangleMesh full_raw_mesh() const;
// A transformed snug bounding box around the non-modifier object volumes, without the translation applied. // A transformed snug bounding box around the non-modifier object volumes, without the translation applied.
// This bounding box is only used for the actual slicing. // This bounding box is only used for the actual slicing.
const BoundingBoxf3& raw_bounding_box() const; const BoundingBoxf3& raw_bounding_box() const;

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

@ -296,6 +296,13 @@ void Preset::normalize(DynamicPrintConfig &config)
if (auto *gap_fill_enabled = config.option<ConfigOptionBool>("gap_fill_enabled", false); gap_fill_enabled) if (auto *gap_fill_enabled = config.option<ConfigOptionBool>("gap_fill_enabled", false); gap_fill_enabled)
gap_fill_enabled->value = false; gap_fill_enabled->value = false;
} }
if (auto *first_layer_height = config.option<ConfigOptionFloatOrPercent>("first_layer_height", false); first_layer_height && first_layer_height->percent)
if (const auto *layer_height = config.option<ConfigOptionFloat>("layer_height", false); layer_height) {
// Legacy conversion - first_layer_height moved from PrintObject setting to a Print setting, thus we are getting rid of the dependency
// of first_layer_height on PrintObject specific layer_height. Covert the first layer heigth to an absolute value.
first_layer_height->value = first_layer_height->get_abs_value(layer_height->value);
first_layer_height->percent = false;
}
} }
std::string Preset::remove_invalid_keys(DynamicPrintConfig &config, const DynamicPrintConfig &default_config) std::string Preset::remove_invalid_keys(DynamicPrintConfig &config, const DynamicPrintConfig &default_config)

View File

@ -1464,7 +1464,8 @@ std::string Print::validate(std::string* warning) const
} }
// validate first_layer_height // validate first_layer_height
double first_layer_height = object->config().get_abs_value("first_layer_height"); assert(! m_config.first_layer_height.percent);
double first_layer_height = m_config.first_layer_height.value;
double first_layer_min_nozzle_diameter; double first_layer_min_nozzle_diameter;
if (object->has_raft()) { if (object->has_raft()) {
// if we have raft layers, only support material extruder is used on first layer // if we have raft layers, only support material extruder is used on first layer
@ -1561,9 +1562,8 @@ BoundingBox Print::total_bounding_box() const
double Print::skirt_first_layer_height() const double Print::skirt_first_layer_height() const
{ {
if (m_objects.empty()) assert(! m_config.first_layer_height.percent);
throw Slic3r::InvalidArgument("skirt_first_layer_height() can't be called without PrintObjects"); return m_config.first_layer_height.value;
return m_objects.front()->config().get_abs_value("first_layer_height");
} }
Flow Print::brim_flow() const Flow Print::brim_flow() const

View File

@ -995,10 +995,8 @@ void PrintConfigDef::init_fff_params()
def->label = L("First layer height"); def->label = L("First layer height");
def->category = L("Layers and Perimeters"); def->category = L("Layers and Perimeters");
def->tooltip = L("When printing with very low layer heights, you might still want to print a thicker " def->tooltip = L("When printing with very low layer heights, you might still want to print a thicker "
"bottom layer to improve adhesion and tolerance for non perfect build plates. " "bottom layer to improve adhesion and tolerance for non perfect build plates.");
"This can be expressed as an absolute value or as a percentage (for example: 150%) " def->sidetext = L("mm");
"over the default layer height.");
def->sidetext = L("mm or %");
def->ratio_over = "layer_height"; def->ratio_over = "layer_height";
def->set_default_value(new ConfigOptionFloatOrPercent(0.35, false)); def->set_default_value(new ConfigOptionFloatOrPercent(0.35, false));
@ -3610,7 +3608,7 @@ std::string DynamicPrintConfig::validate()
FullPrintConfig fpc; FullPrintConfig fpc;
fpc.apply(*this, true); fpc.apply(*this, true);
// Verify this print options through the FullPrintConfig. // Verify this print options through the FullPrintConfig.
return fpc.validate(); return Slic3r::validate(fpc);
} }
default: default:
//FIXME no validation on SLA data? //FIXME no validation on SLA data?
@ -3619,135 +3617,135 @@ std::string DynamicPrintConfig::validate()
} }
//FIXME localize this function. //FIXME localize this function.
std::string FullPrintConfig::validate() std::string validate(const FullPrintConfig &cfg)
{ {
// --layer-height // --layer-height
if (this->get_abs_value("layer_height") <= 0) if (cfg.get_abs_value("layer_height") <= 0)
return "Invalid value for --layer-height"; return "Invalid value for --layer-height";
if (fabs(fmod(this->get_abs_value("layer_height"), SCALING_FACTOR)) > 1e-4) if (fabs(fmod(cfg.get_abs_value("layer_height"), SCALING_FACTOR)) > 1e-4)
return "--layer-height must be a multiple of print resolution"; return "--layer-height must be a multiple of print resolution";
// --first-layer-height // --first-layer-height
if (this->get_abs_value("first_layer_height") <= 0) if (cfg.first_layer_height.value <= 0)
return "Invalid value for --first-layer-height"; return "Invalid value for --first-layer-height";
// --filament-diameter // --filament-diameter
for (double fd : this->filament_diameter.values) for (double fd : cfg.filament_diameter.values)
if (fd < 1) if (fd < 1)
return "Invalid value for --filament-diameter"; return "Invalid value for --filament-diameter";
// --nozzle-diameter // --nozzle-diameter
for (double nd : this->nozzle_diameter.values) for (double nd : cfg.nozzle_diameter.values)
if (nd < 0.005) if (nd < 0.005)
return "Invalid value for --nozzle-diameter"; return "Invalid value for --nozzle-diameter";
// --perimeters // --perimeters
if (this->perimeters.value < 0) if (cfg.perimeters.value < 0)
return "Invalid value for --perimeters"; return "Invalid value for --perimeters";
// --solid-layers // --solid-layers
if (this->top_solid_layers < 0) if (cfg.top_solid_layers < 0)
return "Invalid value for --top-solid-layers"; return "Invalid value for --top-solid-layers";
if (this->bottom_solid_layers < 0) if (cfg.bottom_solid_layers < 0)
return "Invalid value for --bottom-solid-layers"; return "Invalid value for --bottom-solid-layers";
if (this->use_firmware_retraction.value && if (cfg.use_firmware_retraction.value &&
this->gcode_flavor.value != gcfSmoothie && cfg.gcode_flavor.value != gcfSmoothie &&
this->gcode_flavor.value != gcfRepRapSprinter && cfg.gcode_flavor.value != gcfRepRapSprinter &&
this->gcode_flavor.value != gcfRepRapFirmware && cfg.gcode_flavor.value != gcfRepRapFirmware &&
this->gcode_flavor.value != gcfMarlinLegacy && cfg.gcode_flavor.value != gcfMarlinLegacy &&
this->gcode_flavor.value != gcfMarlinFirmware && cfg.gcode_flavor.value != gcfMarlinFirmware &&
this->gcode_flavor.value != gcfMachinekit && cfg.gcode_flavor.value != gcfMachinekit &&
this->gcode_flavor.value != gcfRepetier) cfg.gcode_flavor.value != gcfRepetier)
return "--use-firmware-retraction is only supported by Marlin, Smoothie, RepRapFirmware, Repetier and Machinekit firmware"; return "--use-firmware-retraction is only supported by Marlin, Smoothie, RepRapFirmware, Repetier and Machinekit firmware";
if (this->use_firmware_retraction.value) if (cfg.use_firmware_retraction.value)
for (unsigned char wipe : this->wipe.values) for (unsigned char wipe : cfg.wipe.values)
if (wipe) if (wipe)
return "--use-firmware-retraction is not compatible with --wipe"; return "--use-firmware-retraction is not compatible with --wipe";
// --gcode-flavor // --gcode-flavor
if (! print_config_def.get("gcode_flavor")->has_enum_value(this->gcode_flavor.serialize())) if (! print_config_def.get("gcode_flavor")->has_enum_value(cfg.gcode_flavor.serialize()))
return "Invalid value for --gcode-flavor"; return "Invalid value for --gcode-flavor";
// --fill-pattern // --fill-pattern
if (! print_config_def.get("fill_pattern")->has_enum_value(this->fill_pattern.serialize())) if (! print_config_def.get("fill_pattern")->has_enum_value(cfg.fill_pattern.serialize()))
return "Invalid value for --fill-pattern"; return "Invalid value for --fill-pattern";
// --top-fill-pattern // --top-fill-pattern
if (! print_config_def.get("top_fill_pattern")->has_enum_value(this->top_fill_pattern.serialize())) if (! print_config_def.get("top_fill_pattern")->has_enum_value(cfg.top_fill_pattern.serialize()))
return "Invalid value for --top-fill-pattern"; return "Invalid value for --top-fill-pattern";
// --bottom-fill-pattern // --bottom-fill-pattern
if (! print_config_def.get("bottom_fill_pattern")->has_enum_value(this->bottom_fill_pattern.serialize())) if (! print_config_def.get("bottom_fill_pattern")->has_enum_value(cfg.bottom_fill_pattern.serialize()))
return "Invalid value for --bottom-fill-pattern"; return "Invalid value for --bottom-fill-pattern";
// --fill-density // --fill-density
if (fabs(this->fill_density.value - 100.) < EPSILON && if (fabs(cfg.fill_density.value - 100.) < EPSILON &&
! print_config_def.get("top_fill_pattern")->has_enum_value(this->fill_pattern.serialize())) ! print_config_def.get("top_fill_pattern")->has_enum_value(cfg.fill_pattern.serialize()))
return "The selected fill pattern is not supposed to work at 100% density"; return "The selected fill pattern is not supposed to work at 100% density";
// --infill-every-layers // --infill-every-layers
if (this->infill_every_layers < 1) if (cfg.infill_every_layers < 1)
return "Invalid value for --infill-every-layers"; return "Invalid value for --infill-every-layers";
// --skirt-height // --skirt-height
if (this->skirt_height < 0) if (cfg.skirt_height < 0)
return "Invalid value for --skirt-height"; return "Invalid value for --skirt-height";
// --bridge-flow-ratio // --bridge-flow-ratio
if (this->bridge_flow_ratio <= 0) if (cfg.bridge_flow_ratio <= 0)
return "Invalid value for --bridge-flow-ratio"; return "Invalid value for --bridge-flow-ratio";
// extruder clearance // extruder clearance
if (this->extruder_clearance_radius <= 0) if (cfg.extruder_clearance_radius <= 0)
return "Invalid value for --extruder-clearance-radius"; return "Invalid value for --extruder-clearance-radius";
if (this->extruder_clearance_height <= 0) if (cfg.extruder_clearance_height <= 0)
return "Invalid value for --extruder-clearance-height"; return "Invalid value for --extruder-clearance-height";
// --extrusion-multiplier // --extrusion-multiplier
for (double em : this->extrusion_multiplier.values) for (double em : cfg.extrusion_multiplier.values)
if (em <= 0) if (em <= 0)
return "Invalid value for --extrusion-multiplier"; return "Invalid value for --extrusion-multiplier";
// --default-acceleration // --default-acceleration
if ((this->perimeter_acceleration != 0. || this->infill_acceleration != 0. || this->bridge_acceleration != 0. || this->first_layer_acceleration != 0.) && if ((cfg.perimeter_acceleration != 0. || cfg.infill_acceleration != 0. || cfg.bridge_acceleration != 0. || cfg.first_layer_acceleration != 0.) &&
this->default_acceleration == 0.) cfg.default_acceleration == 0.)
return "Invalid zero value for --default-acceleration when using other acceleration settings"; return "Invalid zero value for --default-acceleration when using other acceleration settings";
// --spiral-vase // --spiral-vase
if (this->spiral_vase) { if (cfg.spiral_vase) {
// Note that we might want to have more than one perimeter on the bottom // Note that we might want to have more than one perimeter on the bottom
// solid layers. // solid layers.
if (this->perimeters > 1) if (cfg.perimeters > 1)
return "Can't make more than one perimeter when spiral vase mode is enabled"; return "Can't make more than one perimeter when spiral vase mode is enabled";
else if (this->perimeters < 1) else if (cfg.perimeters < 1)
return "Can't make less than one perimeter when spiral vase mode is enabled"; return "Can't make less than one perimeter when spiral vase mode is enabled";
if (this->fill_density > 0) if (cfg.fill_density > 0)
return "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0"; return "Spiral vase mode can only print hollow objects, so you need to set Fill density to 0";
if (this->top_solid_layers > 0) if (cfg.top_solid_layers > 0)
return "Spiral vase mode is not compatible with top solid layers"; return "Spiral vase mode is not compatible with top solid layers";
if (this->support_material || this->support_material_enforce_layers > 0) if (cfg.support_material || cfg.support_material_enforce_layers > 0)
return "Spiral vase mode is not compatible with support material"; return "Spiral vase mode is not compatible with support material";
} }
// extrusion widths // extrusion widths
{ {
double max_nozzle_diameter = 0.; double max_nozzle_diameter = 0.;
for (double dmr : this->nozzle_diameter.values) for (double dmr : cfg.nozzle_diameter.values)
max_nozzle_diameter = std::max(max_nozzle_diameter, dmr); max_nozzle_diameter = std::max(max_nozzle_diameter, dmr);
const char *widths[] = { "external_perimeter", "perimeter", "infill", "solid_infill", "top_infill", "support_material", "first_layer" }; const char *widths[] = { "external_perimeter", "perimeter", "infill", "solid_infill", "top_infill", "support_material", "first_layer" };
for (size_t i = 0; i < sizeof(widths) / sizeof(widths[i]); ++ i) { for (size_t i = 0; i < sizeof(widths) / sizeof(widths[i]); ++ i) {
std::string key(widths[i]); std::string key(widths[i]);
key += "_extrusion_width"; key += "_extrusion_width";
if (this->get_abs_value(key, max_nozzle_diameter) > 10. * max_nozzle_diameter) if (cfg.get_abs_value(key, max_nozzle_diameter) > 10. * max_nozzle_diameter)
return std::string("Invalid extrusion width (too large): ") + key; return std::string("Invalid extrusion width (too large): ") + key;
} }
} }
// Out of range validation of numeric values. // Out of range validation of numeric values.
for (const std::string &opt_key : this->keys()) { for (const std::string &opt_key : cfg.keys()) {
const ConfigOption *opt = this->optptr(opt_key); const ConfigOption *opt = cfg.optptr(opt_key);
assert(opt != nullptr); assert(opt != nullptr);
const ConfigOptionDef *optdef = print_config_def.get(opt_key); const ConfigOptionDef *optdef = print_config_def.get(opt_key);
assert(optdef != nullptr); assert(optdef != nullptr);

File diff suppressed because it is too large Load Diff

View File

@ -2207,6 +2207,7 @@ std::vector<ExPolygons> PrintObject::slice_volumes(
TriangleMesh vol_mesh(model_volume.mesh()); TriangleMesh vol_mesh(model_volume.mesh());
vol_mesh.transform(model_volume.get_matrix(), true); vol_mesh.transform(model_volume.get_matrix(), true);
mesh.merge(vol_mesh); mesh.merge(vol_mesh);
mesh.repair(false);
} }
if (mesh.stl.stats.number_of_facets > 0) { if (mesh.stl.stats.number_of_facets > 0) {
mesh.transform(m_trafo, true); mesh.transform(m_trafo, true);

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();

View File

@ -64,9 +64,9 @@ SlicingParameters SlicingParameters::create_from_config(
coordf_t object_height, coordf_t object_height,
const std::vector<unsigned int> &object_extruders) const std::vector<unsigned int> &object_extruders)
{ {
coordf_t first_layer_height = (object_config.first_layer_height.value <= 0) ? assert(! print_config.first_layer_height.percent);
object_config.layer_height.value : coordf_t first_layer_height = (print_config.first_layer_height.value <= 0) ?
object_config.first_layer_height.get_abs_value(object_config.layer_height.value); object_config.layer_height.value : print_config.first_layer_height.value;
// If object_config.support_material_extruder == 0 resp. object_config.support_material_interface_extruder == 0, // If object_config.support_material_extruder == 0 resp. object_config.support_material_interface_extruder == 0,
// print_config.nozzle_diameter.get_at(size_t(-1)) returns the 0th nozzle diameter, // print_config.nozzle_diameter.get_at(size_t(-1)) returns the 0th nozzle diameter,
// which is consistent with the requirement that if support_material_extruder == 0 resp. support_material_interface_extruder == 0, // which is consistent with the requirement that if support_material_extruder == 0 resp. support_material_interface_extruder == 0,

View File

@ -59,6 +59,8 @@
#define ENABLE_EXTENDED_M73_LINES (1 && ENABLE_VALIDATE_CUSTOM_GCODE) #define ENABLE_EXTENDED_M73_LINES (1 && ENABLE_VALIDATE_CUSTOM_GCODE)
// Enable a modified version of automatic downscale on load of objects too big // Enable a modified version of automatic downscale on load of objects too big
#define ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG (1 && ENABLE_2_4_0_ALPHA0) #define ENABLE_MODIFIED_DOWNSCALE_ON_LOAD_OBJECTS_TOO_BIG (1 && ENABLE_2_4_0_ALPHA0)
// Enable visualization of start gcode as regular toolpaths
#define ENABLE_START_GCODE_VISUALIZATION (1 && ENABLE_2_4_0_ALPHA0)
// Enable visualization of seams in preview // Enable visualization of seams in preview
#define ENABLE_SEAMS_VISUALIZATION (1 && ENABLE_2_4_0_ALPHA0) #define ENABLE_SEAMS_VISUALIZATION (1 && ENABLE_2_4_0_ALPHA0)

View File

@ -357,10 +357,14 @@ void TriangleMesh::transform(const Transform3d& t, bool fix_left_handed)
its_transform(its, t); its_transform(its, t);
if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.) { if (fix_left_handed && t.matrix().block(0, 0, 3, 3).determinant() < 0.) {
// Left handed transformation is being applied. It is a good idea to flip the faces and their normals. // Left handed transformation is being applied. It is a good idea to flip the faces and their normals.
this->repair(false); // As for the assert: the repair function would fix the normals, reversing would
stl_reverse_all_facets(&stl); // break them again. The caller should provide a mesh that does not need repair.
this->its.clear(); // The repair call is left here so things don't break more than they were.
this->require_shared_vertices(); assert(this->repaired);
this->repair(false);
stl_reverse_all_facets(&stl);
this->its.clear();
this->require_shared_vertices();
} }
} }
@ -369,11 +373,12 @@ void TriangleMesh::transform(const Matrix3d& m, bool fix_left_handed)
stl_transform(&stl, m); stl_transform(&stl, m);
its_transform(its, m); its_transform(its, m);
if (fix_left_handed && m.determinant() < 0.) { if (fix_left_handed && m.determinant() < 0.) {
// Left handed transformation is being applied. It is a good idea to flip the faces and their normals. // See comments in function above.
assert(this->repaired);
this->repair(false); this->repair(false);
stl_reverse_all_facets(&stl); stl_reverse_all_facets(&stl);
this->its.clear(); this->its.clear();
this->require_shared_vertices(); this->require_shared_vertices();
} }
} }

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

@ -45,7 +45,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
// layer_height shouldn't be equal to zero // layer_height shouldn't be equal to zero
if (config->opt_float("layer_height") < EPSILON) if (config->opt_float("layer_height") < EPSILON)
{ {
const wxString msg_text = _(L("Zero layer height is not valid.\n\nThe layer height will be reset to 0.01.")); const wxString msg_text = _(L("Layer height is not valid.\n\nThe layer height will be reset to 0.01."));
wxMessageDialog dialog(nullptr, msg_text, _(L("Layer height")), wxICON_WARNING | wxOK); wxMessageDialog dialog(nullptr, msg_text, _(L("Layer height")), wxICON_WARNING | wxOK);
DynamicPrintConfig new_conf = *config; DynamicPrintConfig new_conf = *config;
is_msg_dlg_already_exist = true; is_msg_dlg_already_exist = true;
@ -55,9 +55,9 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
is_msg_dlg_already_exist = false; is_msg_dlg_already_exist = false;
} }
if (fabs(config->option<ConfigOptionFloatOrPercent>("first_layer_height")->value - 0) < EPSILON) if (config->option<ConfigOptionFloatOrPercent>("first_layer_height")->value < EPSILON)
{ {
const wxString msg_text = _(L("Zero first layer height is not valid.\n\nThe first layer height will be reset to 0.01.")); const wxString msg_text = _(L("First layer height is not valid.\n\nThe first layer height will be reset to 0.01."));
wxMessageDialog dialog(nullptr, msg_text, _(L("First layer height")), wxICON_WARNING | wxOK); wxMessageDialog dialog(nullptr, msg_text, _(L("First layer height")), wxICON_WARNING | wxOK);
DynamicPrintConfig new_conf = *config; DynamicPrintConfig new_conf = *config;
is_msg_dlg_already_exist = true; is_msg_dlg_already_exist = true;

View File

@ -1624,13 +1624,15 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS #if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
const std::array<IBufferType, 8> first_seg_v_offsets = convert_vertices_offset(vbuffer_size, { 0, 1, 2, 3, 4, 5, 6, 7 }); const std::array<IBufferType, 8> first_seg_v_offsets = convert_vertices_offset(vbuffer_size, { 0, 1, 2, 3, 4, 5, 6, 7 });
const std::array<IBufferType, 8> non_first_seg_v_offsets = convert_vertices_offset(vbuffer_size, { -4, 0, -2, 1, 2, 3, 4, 5 }); const std::array<IBufferType, 8> non_first_seg_v_offsets = convert_vertices_offset(vbuffer_size, { -4, 0, -2, 1, 2, 3, 4, 5 });
#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS bool is_first_segment = (last_path.vertices_count() == 1);
if (is_first_segment || vbuffer_size == 0) {
#else
if (last_path.vertices_count() == 1 || vbuffer_size == 0) { if (last_path.vertices_count() == 1 || vbuffer_size == 0) {
#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
// 1st segment or restart into a new vertex buffer // 1st segment or restart into a new vertex buffer
// =============================================== // ===============================================
#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS #if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
if (last_path.vertices_count() == 1) if (is_first_segment)
// starting cap triangles // starting cap triangles
append_starting_cap_triangles(indices, first_seg_v_offsets); append_starting_cap_triangles(indices, first_seg_v_offsets);
#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS #endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
@ -1708,7 +1710,7 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
#if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS #if ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
if (next != nullptr && (curr.type != next->type || !last_path.matches(*next))) if (next != nullptr && (curr.type != next->type || !last_path.matches(*next)))
// ending cap triangles // ending cap triangles
append_ending_cap_triangles(indices, non_first_seg_v_offsets); append_ending_cap_triangles(indices, is_first_segment ? first_seg_v_offsets : non_first_seg_v_offsets);
#endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS #endif // ENABLE_REDUCED_TOOLPATHS_SEGMENT_CAPS
last_path.sub_paths.back().last = { ibuffer_id, indices.size() - 1, move_id, curr.position }; last_path.sub_paths.back().last = { ibuffer_id, indices.size() - 1, move_id, curr.position };
@ -1743,7 +1745,11 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
// for the gcode viewer we need to take in account all moves to correctly size the printbed // for the gcode viewer we need to take in account all moves to correctly size the printbed
m_paths_bounding_box.merge(move.position.cast<double>()); m_paths_bounding_box.merge(move.position.cast<double>());
else { else {
#if ENABLE_START_GCODE_VISUALIZATION
if (move.type == EMoveType::Extrude && move.extrusion_role != erCustom && move.width != 0.0f && move.height != 0.0f)
#else
if (move.type == EMoveType::Extrude && move.width != 0.0f && move.height != 0.0f) if (move.type == EMoveType::Extrude && move.width != 0.0f && move.height != 0.0f)
#endif // ENABLE_START_GCODE_VISUALIZATION
m_paths_bounding_box.merge(move.position.cast<double>()); m_paths_bounding_box.merge(move.position.cast<double>());
} }
} }

View File

@ -1,7 +1,6 @@
#include "libslic3r/libslic3r.h" #include "libslic3r/libslic3r.h"
#include "GLCanvas3D.hpp" #include "GLCanvas3D.hpp"
#include "admesh/stl.h"
#include "libslic3r/ClipperUtils.hpp" #include "libslic3r/ClipperUtils.hpp"
#include "libslic3r/PrintConfig.hpp" #include "libslic3r/PrintConfig.hpp"
#include "libslic3r/GCode/ThumbnailData.hpp" #include "libslic3r/GCode/ThumbnailData.hpp"
@ -1682,8 +1681,10 @@ void GLCanvas3D::render()
if (m_picking_enabled) if (m_picking_enabled)
m_mouse.scene_position = _mouse_to_3d(m_mouse.position.cast<coord_t>()); m_mouse.scene_position = _mouse_to_3d(m_mouse.position.cast<coord_t>());
_render_current_gizmo(); // sidebar hints need to be rendered before the gizmos because the depth buffer
// could be invalidated by the following gizmo render methods
_render_selection_sidebar_hints(); _render_selection_sidebar_hints();
_render_current_gizmo();
#if ENABLE_RENDER_PICKING_PASS #if ENABLE_RENDER_PICKING_PASS
} }
#endif // ENABLE_RENDER_PICKING_PASS #endif // ENABLE_RENDER_PICKING_PASS
@ -4998,8 +4999,9 @@ void GLCanvas3D::_render_background() const
if (!m_volumes.empty()) if (!m_volumes.empty())
use_error_color &= _is_any_volume_outside(); use_error_color &= _is_any_volume_outside();
else { else {
BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3(); const BoundingBoxf3 test_volume = (m_config != nullptr) ? print_volume(*m_config) : BoundingBoxf3();
use_error_color &= (test_volume.radius() > 0.0) ? !test_volume.contains(m_gcode_viewer.get_paths_bounding_box()) : false; const BoundingBoxf3& paths_volume = m_gcode_viewer.get_paths_bounding_box();
use_error_color &= (test_volume.radius() > 0.0 && paths_volume.radius() > 0.0) ? !test_volume.contains(paths_volume) : false;
} }
} }

View File

@ -244,10 +244,11 @@ private:
// credits infornation // credits infornation
credits = title + " " + credits = title + " " +
_L("is based on Slic3r by Alessandro Ranellucci and the RepRap community.") + "\n\n" + _L("is based on Slic3r by Alessandro Ranellucci and the RepRap community.") + "\n" +
_L("Developed by Prusa Research.")+ "\n\n" +
title + " " + _L("is licensed under the") + " " + _L("GNU Affero General Public License, version 3") + "\n\n" + title + " " + _L("is licensed under the") + " " + _L("GNU Affero General Public License, version 3") + "\n\n" +
_L("Contributions by Vojtech Bubnik, Enrico Turri, Oleksandra Iushchenko, Tamas Meszaros, Lukas Matena, Vojtech Kral, David Kocik and numerous others.") + "\n\n" + _L("Contributions by Vojtech Bubnik, Enrico Turri, Oleksandra Iushchenko, Tamas Meszaros, Lukas Matena, Vojtech Kral, David Kocik and numerous others.") + "\n\n" +
_L("Artwork model by Nora Al-Badri and Jan Nikolai Nelles"); _L("Artwork model by M Boyer");
title_font = version_font = credits_font = init_font; title_font = version_font = credits_font = init_font;
} }

View File

@ -235,7 +235,7 @@ bool Preview::init(wxWindow* parent, Model* model)
_L("Ironing") + "|1|" + _L("Ironing") + "|1|" +
_L("Bridge infill") + "|1|" + _L("Bridge infill") + "|1|" +
_L("Gap fill") + "|1|" + _L("Gap fill") + "|1|" +
_L("Skirt") + "|1|" + _L("Skirt/Brim") + "|1|" +
_L("Support material") + "|1|" + _L("Support material") + "|1|" +
_L("Support material interface") + "|1|" + _L("Support material interface") + "|1|" +
_L("Wipe tower") + "|1|" + _L("Wipe tower") + "|1|" +

View File

@ -1483,10 +1483,10 @@ void MainFrame::repair_stl()
output_file = dlg.GetPath(); output_file = dlg.GetPath();
} }
auto tmesh = new Slic3r::TriangleMesh(); Slic3r::TriangleMesh tmesh;
tmesh->ReadSTLFile(input_file.ToUTF8().data()); tmesh.ReadSTLFile(input_file.ToUTF8().data());
tmesh->repair(); tmesh.repair();
tmesh->WriteOBJFile(output_file.ToUTF8().data()); tmesh.WriteOBJFile(output_file.ToUTF8().data());
Slic3r::GUI::show_info(this, L("Your file was repaired."), L("Repair")); Slic3r::GUI::show_info(this, L("Your file was repaired."), L("Repair"));
} }

View File

@ -5085,6 +5085,30 @@ void Plater::export_stl(bool extended, bool selection_only)
if (selection_only && (obj_idx == -1 || selection.is_wipe_tower())) if (selection_only && (obj_idx == -1 || selection.is_wipe_tower()))
return; return;
// Following lambda generates a combined mesh for export with normals pointing outwards.
auto mesh_to_export = [](const ModelObject* mo, bool instances) -> TriangleMesh {
TriangleMesh mesh;
for (const ModelVolume *v : mo->volumes)
if (v->is_model_part()) {
TriangleMesh vol_mesh(v->mesh());
vol_mesh.repair();
vol_mesh.transform(v->get_matrix(), true);
mesh.merge(vol_mesh);
}
mesh.repair();
if (instances) {
TriangleMesh vols_mesh(mesh);
mesh = TriangleMesh();
for (const ModelInstance *i : mo->instances) {
TriangleMesh m = vols_mesh;
m.transform(i->get_matrix(), true);
mesh.merge(m);
}
}
mesh.repair();
return mesh;
};
TriangleMesh mesh; TriangleMesh mesh;
if (p->printer_technology == ptFFF) { if (p->printer_technology == ptFFF) {
if (selection_only) { if (selection_only) {
@ -5092,20 +5116,21 @@ void Plater::export_stl(bool extended, bool selection_only)
if (selection.get_mode() == Selection::Instance) if (selection.get_mode() == Selection::Instance)
{ {
if (selection.is_single_full_object()) if (selection.is_single_full_object())
mesh = model_object->mesh(); mesh = mesh_to_export(model_object, true);
else else
mesh = model_object->full_raw_mesh(); mesh = mesh_to_export(model_object, false);
} }
else else
{ {
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
mesh = model_object->volumes[volume->volume_idx()]->mesh(); mesh = model_object->volumes[volume->volume_idx()]->mesh();
mesh.transform(volume->get_volume_transformation().get_matrix()); mesh.transform(volume->get_volume_transformation().get_matrix(), true);
mesh.translate(-model_object->origin_translation.cast<float>()); mesh.translate(-model_object->origin_translation.cast<float>());
} }
} }
else { else {
mesh = p->model.mesh(); for (const ModelObject *o : p->model.objects)
mesh.merge(mesh_to_export(o, true));
} }
} }
else else

View File

@ -86,7 +86,8 @@ std::string PresetHints::maximum_volumetric_flow_description(const PresetBundle
// Print config values // Print config values
double layer_height = print_config.opt_float("layer_height"); double layer_height = print_config.opt_float("layer_height");
double first_layer_height = print_config.get_abs_value("first_layer_height", layer_height); assert(! print_config.option<ConfigOptionFloatOrPercent>("first_layer_height")->percent);
double first_layer_height = print_config.opt_float("first_layer_height");
double support_material_speed = print_config.opt_float("support_material_speed"); double support_material_speed = print_config.opt_float("support_material_speed");
double support_material_interface_speed = print_config.get_abs_value("support_material_interface_speed", support_material_speed); double support_material_interface_speed = print_config.get_abs_value("support_material_interface_speed", support_material_speed);
double bridge_speed = print_config.opt_float("bridge_speed"); double bridge_speed = print_config.opt_float("bridge_speed");

View File

@ -1217,7 +1217,7 @@ void Selection::render_center(bool gizmo_is_dragging) const
if (!m_valid || is_empty() || m_quadric == nullptr) if (!m_valid || is_empty() || m_quadric == nullptr)
return; return;
Vec3d center = gizmo_is_dragging ? m_cache.dragging_center : get_bounding_box().center(); const Vec3d center = gizmo_is_dragging ? m_cache.dragging_center : get_bounding_box().center();
glsafe(::glDisable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_DEPTH_TEST));
@ -1286,7 +1286,7 @@ void Selection::render_sidebar_hints(const std::string& sidebar_field) const
} else { } else {
glsafe(::glTranslated(center(0), center(1), center(2))); glsafe(::glTranslated(center(0), center(1), center(2)));
if (requires_local_axes()) { if (requires_local_axes()) {
Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true); const Transform3d orient_matrix = (*m_volumes)[*m_list.begin()]->get_instance_transformation().get_matrix(true, false, true, true);
glsafe(::glMultMatrixd(orient_matrix.data())); glsafe(::glMultMatrixd(orient_matrix.data()));
} }
} }
@ -1976,7 +1976,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co
if (pos == std::string::npos) if (pos == std::string::npos)
return; return;
double min_z = std::stod(field.substr(pos + 1)); const double min_z = std::stod(field.substr(pos + 1));
// extract type // extract type
field = field.substr(0, pos); field = field.substr(0, pos);
@ -1984,7 +1984,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co
if (pos == std::string::npos) if (pos == std::string::npos)
return; return;
int type = std::stoi(field.substr(pos + 1)); const int type = std::stoi(field.substr(pos + 1));
const BoundingBoxf3& box = get_bounding_box(); const BoundingBoxf3& box = get_bounding_box();
@ -1995,8 +1995,8 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co
// view dependend order of rendering to keep correct transparency // view dependend order of rendering to keep correct transparency
bool camera_on_top = wxGetApp().plater()->get_camera().is_looking_downward(); bool camera_on_top = wxGetApp().plater()->get_camera().is_looking_downward();
float z1 = camera_on_top ? min_z : max_z; const float z1 = camera_on_top ? min_z : max_z;
float z2 = camera_on_top ? max_z : min_z; const float z2 = camera_on_top ? max_z : min_z;
glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glEnable(GL_DEPTH_TEST));
glsafe(::glDisable(GL_CULL_FACE)); glsafe(::glDisable(GL_CULL_FACE));
@ -2004,7 +2004,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co
glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); glsafe(::glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA));
::glBegin(GL_QUADS); ::glBegin(GL_QUADS);
if ((camera_on_top && (type == 1)) || (!camera_on_top && (type == 2))) if ((camera_on_top && type == 1) || (!camera_on_top && type == 2))
::glColor4f(1.0f, 0.38f, 0.0f, 1.0f); ::glColor4f(1.0f, 0.38f, 0.0f, 1.0f);
else else
::glColor4f(0.8f, 0.8f, 0.8f, 0.5f); ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f);
@ -2015,7 +2015,7 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co
glsafe(::glEnd()); glsafe(::glEnd());
::glBegin(GL_QUADS); ::glBegin(GL_QUADS);
if ((camera_on_top && (type == 2)) || (!camera_on_top && (type == 1))) if ((camera_on_top && type == 2) || (!camera_on_top && type == 1))
::glColor4f(1.0f, 0.38f, 0.0f, 1.0f); ::glColor4f(1.0f, 0.38f, 0.0f, 1.0f);
else else
::glColor4f(0.8f, 0.8f, 0.8f, 0.5f); ::glColor4f(0.8f, 0.8f, 0.8f, 0.5f);

View File

@ -21,7 +21,7 @@ use Slic3r::Test;
$config->set('fill_density', 0.4); $config->set('fill_density', 0.4);
$config->set('bottom_solid_layers', 1); $config->set('bottom_solid_layers', 1);
$config->set('first_layer_extrusion_width', 2); $config->set('first_layer_extrusion_width', 2);
$config->set('first_layer_height', '100%'); $config->set('first_layer_height', $config->layer_height);
$config->set('filament_diameter', [ 3.0 ]); $config->set('filament_diameter', [ 3.0 ]);
$config->set('nozzle_diameter', [ 0.5 ]); $config->set('nozzle_diameter', [ 0.5 ]);

View File

@ -49,7 +49,7 @@ use Slic3r::Test qw(_eq);
$config->set('first_layer_height', 0.2); $config->set('first_layer_height', 0.2);
ok $test->(), "absolute first layer height"; ok $test->(), "absolute first layer height";
$config->set('first_layer_height', '60%'); $config->set('first_layer_height', 0.6 * $config->layer_height);
ok $test->(), "relative first layer height"; ok $test->(), "relative first layer height";
$config->set('z_offset', 0.9); $config->set('z_offset', 0.9);

View File

@ -181,7 +181,7 @@ use Slic3r::Test;
my $config = Slic3r::Config::new_from_defaults; my $config = Slic3r::Config::new_from_defaults;
$config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]); $config->set('nozzle_diameter', [0.6,0.6,0.6,0.6]);
$config->set('layer_height', 0.4); $config->set('layer_height', 0.4);
$config->set('first_layer_height', '100%'); $config->set('first_layer_height', $config->layer_height);
$config->set('skirts', 0); $config->set('skirts', 0);
my $print = Slic3r::Test::init_print($model, config => $config); my $print = Slic3r::Test::init_print($model, config => $config);

View File

@ -84,7 +84,7 @@ use Slic3r::Test;
{ {
my $config = Slic3r::Config::new_from_defaults; my $config = Slic3r::Config::new_from_defaults;
$config->set('layer_height', 0.3); $config->set('layer_height', 0.3);
$config->set('first_layer_height', '100%'); $config->set('first_layer_height', $config->layer_height);
$config->set('bottom_solid_layers', 0); $config->set('bottom_solid_layers', 0);
$config->set('top_solid_layers', 3); $config->set('top_solid_layers', 3);
$config->set('cooling', [ 0 ]); $config->set('cooling', [ 0 ]);
@ -119,7 +119,7 @@ use Slic3r::Test;
$config->set('cooling', [ 0 ]); # prevent speed alteration $config->set('cooling', [ 0 ]); # prevent speed alteration
$config->set('first_layer_speed', '100%'); # prevent speed alteration $config->set('first_layer_speed', '100%'); # prevent speed alteration
$config->set('layer_height', 0.4); $config->set('layer_height', 0.4);
$config->set('first_layer_height', '100%'); $config->set('first_layer_height', $config->layer_height);
$config->set('extrusion_width', 0.55); $config->set('extrusion_width', 0.55);
$config->set('bottom_solid_layers', 3); $config->set('bottom_solid_layers', 3);
$config->set('top_solid_layers', 0); $config->set('top_solid_layers', 0);
@ -142,7 +142,7 @@ use Slic3r::Test;
$config->set('cooling', [ 0 ]); # prevent speed alteration $config->set('cooling', [ 0 ]); # prevent speed alteration
$config->set('first_layer_speed', '100%'); # prevent speed alteration $config->set('first_layer_speed', '100%'); # prevent speed alteration
$config->set('layer_height', 0.4); $config->set('layer_height', 0.4);
$config->set('first_layer_height', '100%'); $config->set('first_layer_height', $config->layer_height);
$config->set('bottom_solid_layers', 3); $config->set('bottom_solid_layers', 3);
$config->set('top_solid_layers', 3); $config->set('top_solid_layers', 3);
$config->set('solid_infill_speed', 99); $config->set('solid_infill_speed', 99);
@ -170,7 +170,7 @@ use Slic3r::Test;
$config->set('spiral_vase', 1); $config->set('spiral_vase', 1);
$config->set('bottom_solid_layers', 0); $config->set('bottom_solid_layers', 0);
$config->set('skirts', 0); $config->set('skirts', 0);
$config->set('first_layer_height', '100%'); $config->set('first_layer_height', $config->layer_height);
$config->set('start_gcode', ''); $config->set('start_gcode', '');
$config->set('temperature', [200]); $config->set('temperature', [200]);
$config->set('first_layer_temperature', [205]); $config->set('first_layer_temperature', [205]);
@ -231,8 +231,8 @@ use Slic3r::Test;
$config->set('bottom_solid_layers', 0); $config->set('bottom_solid_layers', 0);
$config->set('retract_layer_change', [0]); $config->set('retract_layer_change', [0]);
$config->set('skirts', 0); $config->set('skirts', 0);
$config->set('first_layer_height', '100%');
$config->set('layer_height', 0.4); $config->set('layer_height', 0.4);
$config->set('first_layer_height', $config->layer_height);
$config->set('start_gcode', ''); $config->set('start_gcode', '');
# $config->set('use_relative_e_distances', 1); # $config->set('use_relative_e_distances', 1);
$config->validate; $config->validate;
@ -310,7 +310,7 @@ use Slic3r::Test;
# $config->set('spiral_vase', 1); # $config->set('spiral_vase', 1);
# $config->set('bottom_solid_layers', 0); # $config->set('bottom_solid_layers', 0);
# $config->set('skirts', 0); # $config->set('skirts', 0);
# $config->set('first_layer_height', '100%'); # $config->set('first_layer_height', $config->layer_height);
# $config->set('start_gcode', ''); # $config->set('start_gcode', '');
# #
# my $print = Slic3r::Test::init_print('two_hollow_squares', config => $config); # my $print = Slic3r::Test::init_print('two_hollow_squares', config => $config);

View File

@ -18,7 +18,7 @@ use Slic3r::Test;
if (0) { if (0) {
my $config = Slic3r::Config::new_from_defaults; my $config = Slic3r::Config::new_from_defaults;
$config->set('layer_height', 0.2); $config->set('layer_height', 0.2);
$config->set('first_layer_height', '100%'); $config->set('first_layer_height', $config->layer_height);
$config->set('extrusion_width', 0.5); $config->set('extrusion_width', 0.5);
$config->set('first_layer_extrusion_width', '200%'); # check this one too $config->set('first_layer_extrusion_width', '200%'); # check this one too
$config->set('skirts', 0); $config->set('skirts', 0);

View File

@ -24,7 +24,7 @@ SCENARIO("Extrusion width specifics", "[Flow]") {
{ "skirts", 1 }, { "skirts", 1 },
{ "perimeters", 3 }, { "perimeters", 3 },
{ "fill_density", "40%" }, { "fill_density", "40%" },
{ "first_layer_height", "100%" } { "first_layer_height", 0.3 }
}); });
WHEN("first layer width set to 2mm") { WHEN("first layer width set to 2mm") {

View File

@ -29,7 +29,7 @@ SCENARIO("SupportMaterial: support_layers_z and contact_distance", "[SupportMate
{ {
ConstSupportLayerPtrsAdaptor support_layers = print.objects().front()->support_layers(); ConstSupportLayerPtrsAdaptor support_layers = print.objects().front()->support_layers();
first_support_layer_height_ok = support_layers.front()->print_z == print.default_object_config().first_layer_height.value; first_support_layer_height_ok = support_layers.front()->print_z == print.config().first_layer_height.value;
layer_height_minimum_ok = true; layer_height_minimum_ok = true;
layer_height_maximum_ok = true; layer_height_maximum_ok = true;

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,9 +794,8 @@ 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},
{111, 126}, {111, 126},
@ -777,9 +804,8 @@ std::vector<ItemPair> nfp_testdata = {
{148, 100}, {148, 100},
{148, 85}, {148, 85},
{147, 84}, {147, 84},
{102, 84}, {102, 84}
{102, 116}, }
}
}, },
{ {
{ {
@ -793,9 +819,8 @@ 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},
{128, 125}, {128, 125},
@ -810,9 +835,8 @@ std::vector<ItemPair> nfp_testdata = {
{136, 86}, {136, 86},
{134, 85}, {134, 85},
{108, 85}, {108, 85},
{107, 86}, {107, 86}
{107, 124}, }
}
}, },
{ {
{ {
@ -825,9 +849,8 @@ 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},
{103, 98}, {103, 98},
@ -843,9 +866,8 @@ std::vector<ItemPair> nfp_testdata = {
{145, 84}, {145, 84},
{105, 84}, {105, 84},
{102, 87}, {102, 87},
{101, 89}, {101, 89}
{101, 90}, }
}
} }
}; };
@ -860,10 +882,9 @@ std::vector<ItemPair> nfp_testdata = {
{533659, 157607}, {533659, 157607},
{538669, 160091}, {538669, 160091},
{537178, 142155}, {537178, 142155},
{534959, 143386}, {534959, 143386}
{533726, 142141}, }
} },
},
{ {
{ {
{118305, 11603}, {118305, 11603},
@ -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

@ -14,9 +14,12 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") {
{ "nozzle_diameter", "0.6;0.6;0.6;0.6" }, { "nozzle_diameter", "0.6;0.6;0.6;0.6" },
{ "temperature", "357;359;363;378" } { "temperature", "357;359;363;378" }
}); });
// To test the "first_layer_extrusion_width" over "first_layer_heigth" over "layer_height" chain. // To test the "first_layer_extrusion_width" over "first_layer_heigth".
config.option<ConfigOptionFloatOrPercent>("first_layer_height")->value = 150.; // "first_layer_heigth" over "layer_height" is no more supported after first_layer_height was moved from PrintObjectConfig to PrintConfig.
config.option<ConfigOptionFloatOrPercent>("first_layer_height")->percent = true; // config.option<ConfigOptionFloatOrPercent>("first_layer_height")->value = 150.;
// config.option<ConfigOptionFloatOrPercent>("first_layer_height")->percent = true;
config.option<ConfigOptionFloatOrPercent>("first_layer_height")->value = 1.5 * config.opt_float("layer_height");
config.option<ConfigOptionFloatOrPercent>("first_layer_height")->percent = false;
// To let the PlaceholderParser throw when referencing first_layer_speed if it is set to percent, as the PlaceholderParser does not know // To let the PlaceholderParser throw when referencing first_layer_speed if it is set to percent, as the PlaceholderParser does not know
// a percent to what. // a percent to what.
config.option<ConfigOptionFloatOrPercent>("first_layer_speed")->value = 50.; config.option<ConfigOptionFloatOrPercent>("first_layer_speed")->value = 50.;
@ -50,7 +53,7 @@ SCENARIO("Placeholder parser scripting", "[PlaceholderParser]") {
SECTION("math: int(-13.4)") { REQUIRE(parser.process("{int(-13.4)}") == "-13"); } SECTION("math: int(-13.4)") { REQUIRE(parser.process("{int(-13.4)}") == "-13"); }
// Test the "coFloatOrPercent" and "xxx_extrusion_width" substitutions. // Test the "coFloatOrPercent" and "xxx_extrusion_width" substitutions.
// first_layer_extrusion_width ratio_over first_layer_heigth ratio_over layer_height // first_layer_extrusion_width ratio_over first_layer_heigth.
SECTION("perimeter_extrusion_width") { REQUIRE(std::stod(parser.process("{perimeter_extrusion_width}")) == Approx(0.67500001192092896)); } SECTION("perimeter_extrusion_width") { REQUIRE(std::stod(parser.process("{perimeter_extrusion_width}")) == Approx(0.67500001192092896)); }
SECTION("first_layer_extrusion_width") { REQUIRE(std::stod(parser.process("{first_layer_extrusion_width}")) == Approx(0.9)); } SECTION("first_layer_extrusion_width") { REQUIRE(std::stod(parser.process("{first_layer_extrusion_width}")) == Approx(0.9)); }
SECTION("support_material_xy_spacing") { REQUIRE(std::stod(parser.process("{support_material_xy_spacing}")) == Approx(0.3375)); } SECTION("support_material_xy_spacing") { REQUIRE(std::stod(parser.process("{support_material_xy_spacing}")) == Approx(0.3375)); }

View File

@ -4,7 +4,7 @@ use strict;
use warnings; use warnings;
use Slic3r::XS; use Slic3r::XS;
use Test::More tests => 147; use Test::More tests => 143;
foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintConfig) { foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintConfig) {
$config->set('layer_height', 0.3); $config->set('layer_height', 0.3);
@ -70,10 +70,11 @@ foreach my $config (Slic3r::Config->new, Slic3r::Config::Static::new_FullPrintCo
ok abs($config->get('first_layer_height') - 0.3) < 1e-4, 'set/get absolute floatOrPercent'; ok abs($config->get('first_layer_height') - 0.3) < 1e-4, 'set/get absolute floatOrPercent';
is $config->opt_serialize('first_layer_height'), '0.3', 'serialize absolute floatOrPercent'; is $config->opt_serialize('first_layer_height'), '0.3', 'serialize absolute floatOrPercent';
$config->set('first_layer_height', '50%'); # This is no more supported after first_layer_height was moved from PrintObjectConfig to PrintConfig.
$config->get_abs_value('first_layer_height'); # $config->set('first_layer_height', $config->get('layer_height'));
ok abs($config->get_abs_value('first_layer_height') - 0.15) < 1e-4, 'set/get relative floatOrPercent'; # $config->get_abs_value('first_layer_height');
is $config->opt_serialize('first_layer_height'), '50%', 'serialize relative floatOrPercent'; # ok abs($config->get_abs_value('first_layer_height') - 0.15) < 1e-4, 'set/get relative floatOrPercent';
# is $config->opt_serialize('first_layer_height'), '50%', 'serialize relative floatOrPercent';
# Uh-oh, we have no point option to test at the moment # Uh-oh, we have no point option to test at the moment
#ok $config->set('print_center', [50,80]), 'valid point coordinates'; #ok $config->set('print_center', [50,80]), 'valid point coordinates';

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

@ -108,7 +108,7 @@
std::string get_extrusion_axis() std::string get_extrusion_axis()
%code{% %code{%
if (GCodeConfig* config = dynamic_cast<GCodeConfig*>(THIS)) { if (GCodeConfig* config = dynamic_cast<GCodeConfig*>(THIS)) {
RETVAL = config->get_extrusion_axis(); RETVAL = get_extrusion_axis(*config);
} else { } else {
CONFESS("This StaticConfig object does not provide get_extrusion_axis()"); CONFESS("This StaticConfig object does not provide get_extrusion_axis()");
} }

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