Introduction of ClipperLib_Z: The Clipper library compiled with support

of the Z coordinate, compiled in the ClipperLib_Z namespace.

Update of Lukas's new brim clipping:
All the brim contours are now clipped by the ClipperLib_Z library
in one shot.
This commit is contained in:
bubnikv 2019-09-13 16:16:37 +02:00
parent bb896b4c13
commit ea8b6262cf
7 changed files with 148 additions and 46 deletions

View file

@ -4,4 +4,6 @@ cmake_minimum_required(VERSION 2.6)
add_library(clipper STATIC add_library(clipper STATIC
clipper.cpp clipper.cpp
clipper.hpp clipper.hpp
clipper_z.cpp
clipper_z.hpp
) )

View file

@ -51,7 +51,11 @@
#include <Shiny/Shiny.h> #include <Shiny/Shiny.h>
#include <libslic3r/Int128.hpp> #include <libslic3r/Int128.hpp>
#ifdef use_xyz
namespace ClipperLib_Z {
#else /* use_xyz */
namespace ClipperLib { namespace ClipperLib {
#endif /* use_xyz */
static double const pi = 3.141592653589793238; static double const pi = 3.141592653589793238;
static double const two_pi = pi *2; static double const two_pi = pi *2;
@ -1616,7 +1620,7 @@ void Clipper::SetZ(IntPoint& pt, TEdge& e1, TEdge& e2)
else if (pt == e1.Top) pt.Z = e1.Top.Z; else if (pt == e1.Top) pt.Z = e1.Top.Z;
else if (pt == e2.Bot) pt.Z = e2.Bot.Z; else if (pt == e2.Bot) pt.Z = e2.Bot.Z;
else if (pt == e2.Top) pt.Z = e2.Top.Z; else if (pt == e2.Top) pt.Z = e2.Top.Z;
else (*m_ZFill)(e1.Bot, e1.Top, e2.Bot, e2.Top, pt); else m_ZFill(e1.Bot, e1.Top, e2.Bot, e2.Top, pt);
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#endif #endif

View file

@ -35,6 +35,7 @@
#define clipper_hpp #define clipper_hpp
#include <inttypes.h> #include <inttypes.h>
#include <functional>
#define CLIPPER_VERSION "6.2.6" #define CLIPPER_VERSION "6.2.6"
@ -56,7 +57,11 @@
#include <functional> #include <functional>
#include <queue> #include <queue>
#ifdef use_xyz
namespace ClipperLib_Z {
#else /* use_xyz */
namespace ClipperLib { namespace ClipperLib {
#endif /* use_xyz */
enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
enum PolyType { ptSubject, ptClip }; enum PolyType { ptSubject, ptClip };
@ -114,7 +119,7 @@ struct DoublePoint
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#ifdef use_xyz #ifdef use_xyz
typedef void (*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); typedef std::function<void(const IntPoint& e1bot, const IntPoint& e1top, const IntPoint& e2bot, const IntPoint& e2top, IntPoint& pt)> ZFillCallback;
#endif #endif
enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4}; enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};

View file

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

18
src/clipper/clipper_z.hpp Normal file
View file

@ -0,0 +1,18 @@
// Hackish wrapper around the ClipperLib library to compile the Clipper library with the Z support.
#ifndef clipper_z_hpp
#ifdef clipper_hpp
#error "You should include the clipper_z.hpp first"
#endif
#define clipper_z_hpp
// Enable the Z coordinate support.
#define use_xyz
#include "clipper.hpp"
#undef clipper_hpp
#undef use_xyz
#endif clipper_z_hpp

View file

@ -1,3 +1,5 @@
#include "clipper/clipper_z.hpp"
#include "Print.hpp" #include "Print.hpp"
#include "BoundingBox.hpp" #include "BoundingBox.hpp"
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
@ -1729,53 +1731,115 @@ void Print::_make_brim()
ClipperLib::jtRound, ClipperLib::jtRound,
float(scale_(0.1))); float(scale_(0.1)));
const Polygon& skirt_inner = skirt_inners.front(); // First calculate the trimming region.
const Polygon& skirt_outer = skirt_outers.front(); ClipperLib_Z::Paths trimming;
{
ClipperLib_Z::Paths input_subject;
ClipperLib_Z::Paths input_clip;
for (const Polygon &poly : skirt_outers) {
input_subject.emplace_back();
ClipperLib_Z::Path &out = input_subject.back();
out.reserve(poly.points.size());
for (const Point &pt : poly.points)
out.emplace_back(pt.x(), pt.y(), 0);
}
for (const Polygon &poly : skirt_inners) {
input_clip.emplace_back();
ClipperLib_Z::Path &out = input_clip.back();
out.reserve(poly.points.size());
for (const Point &pt : poly.points)
out.emplace_back(pt.x(), pt.y(), 0);
}
// init Clipper
ClipperLib_Z::Clipper clipper;
// add polygons
clipper.AddPaths(input_subject, ClipperLib_Z::ptSubject, true);
clipper.AddPaths(input_clip, ClipperLib_Z::ptClip, true);
// perform operation
clipper.Execute(ClipperLib_Z::ctDifference, trimming, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
}
for (size_t i=0; i<loops.size(); ++i) { // Second, trim the extrusion loops with the trimming regions.
Polylines lines; ClipperLib_Z::Paths loops_trimmed;
lines.push_back(Polyline()); {
bool del = false; // Produce a closed polyline (repeat the first point at the end).
const Polygon& poly = loops[i]; ClipperLib_Z::Paths input_clip;
// Check all points of the polygon, consider the first to also be at the end so the loop is closed for (const Polygon &loop : loops) {
for (int pt_idx=0; pt_idx <= (int)poly.points.size(); ++pt_idx) { input_clip.emplace_back();
const Point* pt = (pt_idx == (int)poly.points.size() ? &poly.points[0] : &poly.points[pt_idx]); ClipperLib_Z::Path& out = input_clip.back();
const Point* last_pt = (pt_idx == 0 ? nullptr : &poly.points[pt_idx-1]); out.reserve(loop.points.size());
bool valid_point = skirt_inner.contains(*pt) || ! skirt_outer.contains(*pt); int64_t loop_idx = &loop - &loops.front();
Points intersections(2); // inner and outer intersection for (const Point& pt : loop.points)
// The Z coordinate carries index of the source loop.
out.emplace_back(pt.x(), pt.y(), loop_idx + 1);
out.emplace_back(out.front());
}
// init Clipper
ClipperLib_Z::Clipper clipper;
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
// 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));
});
// add polygons
clipper.AddPaths(input_clip, ClipperLib_Z::ptSubject, false);
clipper.AddPaths(trimming, ClipperLib_Z::ptClip, true);
// perform operation
ClipperLib_Z::PolyTree loops_trimmed_tree;
clipper.Execute(ClipperLib_Z::ctDifference, loops_trimmed_tree, ClipperLib_Z::pftEvenOdd, ClipperLib_Z::pftEvenOdd);
ClipperLib_Z::PolyTreeToPaths(loops_trimmed_tree, loops_trimmed);
}
if (pt_idx > 0) { // Third, produce the extrusions, sorted by the source loop indices.
Line line(*last_pt, *pt); {
bool inner = skirt_inner.first_intersection(line, &intersections[0]); std::vector<std::pair<const ClipperLib_Z::Path*, size_t>> loops_trimmed_order;
bool outer = skirt_outer.first_intersection(line, &intersections[1]); loops_trimmed_order.reserve(loops_trimmed.size());
del = del || inner || outer; for (const ClipperLib_Z::Path &path : loops_trimmed) {
if (inner != outer) {// there is exactly one intersection size_t input_idx = 0;
lines.back().append(inner ? intersections[0] : intersections[1]); for (const ClipperLib_Z::IntPoint &pt : path)
} if (pt.Z > 0) {
if (inner && outer) { input_idx = (size_t)pt.Z;
int nearest_idx = pt->nearest_point_index(intersections); break;
lines.back().append(intersections[! nearest_idx]); }
lines.push_back(Polyline()); assert(input_idx != 0);
lines.back().append(intersections[nearest_idx]); loops_trimmed_order.emplace_back(&path, input_idx);
} }
} std::stable_sort(loops_trimmed_order.begin(), loops_trimmed_order.end(),
if (valid_point) [](const std::pair<const ClipperLib_Z::Path*, size_t> &l, const std::pair<const ClipperLib_Z::Path*, size_t> &r) {
lines.back().append(*pt); return l.second < r.second;
else { });
del = true; Vec3f last_pt(0.f, 0.f, 0.f);
lines.push_back(Polyline());
} for (size_t i = 0; i < loops_trimmed_order.size();) {
} // Find all pieces that the initial loop was split into.
// If we found a single intersection, we should erase the respective ExtrusionLoop size_t j = i + 1;
// and append the polylines that we created. for (; j < loops_trimmed_order.size() && loops_trimmed_order[i].first == loops_trimmed_order[j].first; ++ j) ;
if (del) { const ClipperLib_Z::Path &first_path = *loops_trimmed_order[i].first;
loops.erase(loops.begin() + (i--)); if (i + 1 == j && first_path.size() > 3 && first_path.front().X == first_path.back().X && first_path.front().Y == first_path.back().Y) {
extrusion_entities_append_paths(m_brim.entities, std::move(lines), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height())); auto *loop = new ExtrusionLoop();
} m_brim.entities.emplace_back(loop);
} loop->paths.emplace_back(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height()));
Points &points = loop->paths.front().polyline.points;
points.reserve(first_path.size());
for (const ClipperLib_Z::IntPoint &pt : first_path)
points.emplace_back(coord_t(pt.X), coord_t(pt.Y));
i = j;
} else {
//FIXME this is not optimal as the G-code generator will follow the sequence of paths verbatim without respect to minimum travel distance.
for (; i < j; ++ i) {
m_brim.entities.emplace_back(new ExtrusionPath(erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height())));
const ClipperLib_Z::Path &path = *loops_trimmed_order[i].first;
Points &points = static_cast<ExtrusionPath*>(m_brim.entities.back())->polyline.points;
points.reserve(path.size());
for (const ClipperLib_Z::IntPoint &pt : path)
points.emplace_back(coord_t(pt.X), coord_t(pt.Y));
}
}
}
}
} else {
extrusion_entities_append_loops(m_brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height()));
} }
extrusion_entities_append_loops(m_brim.entities, std::move(loops), erSkirt, float(flow.mm3_per_mm()), float(flow.width), float(this->skirt_first_layer_height()));
} }
// Wipe tower support. // Wipe tower support.

View file

@ -105,6 +105,8 @@
#include <cereal/access.hpp> #include <cereal/access.hpp>
#include <cereal/types/base_class.hpp> #include <cereal/types/base_class.hpp>
#include <clipper/clipper_z.hpp>
#include <clipper/clipper.hpp>
#include "BoundingBox.hpp" #include "BoundingBox.hpp"
#include "ClipperUtils.hpp" #include "ClipperUtils.hpp"
#include "Config.hpp" #include "Config.hpp"