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:
parent
bb896b4c13
commit
ea8b6262cf
7 changed files with 148 additions and 46 deletions
|
@ -4,4 +4,6 @@ cmake_minimum_required(VERSION 2.6)
|
|||
add_library(clipper STATIC
|
||||
clipper.cpp
|
||||
clipper.hpp
|
||||
clipper_z.cpp
|
||||
clipper_z.hpp
|
||||
)
|
||||
|
|
|
@ -51,7 +51,11 @@
|
|||
#include <Shiny/Shiny.h>
|
||||
#include <libslic3r/Int128.hpp>
|
||||
|
||||
#ifdef use_xyz
|
||||
namespace ClipperLib_Z {
|
||||
#else /* use_xyz */
|
||||
namespace ClipperLib {
|
||||
#endif /* use_xyz */
|
||||
|
||||
static double const pi = 3.141592653589793238;
|
||||
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 == e2.Bot) pt.Z = e2.Bot.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
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#define clipper_hpp
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <functional>
|
||||
|
||||
#define CLIPPER_VERSION "6.2.6"
|
||||
|
||||
|
@ -56,7 +57,11 @@
|
|||
#include <functional>
|
||||
#include <queue>
|
||||
|
||||
#ifdef use_xyz
|
||||
namespace ClipperLib_Z {
|
||||
#else /* use_xyz */
|
||||
namespace ClipperLib {
|
||||
#endif /* use_xyz */
|
||||
|
||||
enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
|
||||
enum PolyType { ptSubject, ptClip };
|
||||
|
@ -114,7 +119,7 @@ struct DoublePoint
|
|||
//------------------------------------------------------------------------------
|
||||
|
||||
#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
|
||||
|
||||
enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};
|
||||
|
|
7
src/clipper/clipper_z.cpp
Normal file
7
src/clipper/clipper_z.cpp
Normal 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
18
src/clipper/clipper_z.hpp
Normal 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
|
|
@ -1,3 +1,5 @@
|
|||
#include "clipper/clipper_z.hpp"
|
||||
|
||||
#include "Print.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
|
@ -1729,53 +1731,115 @@ void Print::_make_brim()
|
|||
ClipperLib::jtRound,
|
||||
float(scale_(0.1)));
|
||||
|
||||
const Polygon& skirt_inner = skirt_inners.front();
|
||||
const Polygon& skirt_outer = skirt_outers.front();
|
||||
// First calculate the trimming region.
|
||||
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) {
|
||||
Polylines lines;
|
||||
lines.push_back(Polyline());
|
||||
bool del = false;
|
||||
const Polygon& poly = loops[i];
|
||||
// Check all points of the polygon, consider the first to also be at the end so the loop is closed
|
||||
for (int pt_idx=0; pt_idx <= (int)poly.points.size(); ++pt_idx) {
|
||||
const Point* pt = (pt_idx == (int)poly.points.size() ? &poly.points[0] : &poly.points[pt_idx]);
|
||||
const Point* last_pt = (pt_idx == 0 ? nullptr : &poly.points[pt_idx-1]);
|
||||
bool valid_point = skirt_inner.contains(*pt) || ! skirt_outer.contains(*pt);
|
||||
Points intersections(2); // inner and outer intersection
|
||||
// Second, trim the extrusion loops with the trimming regions.
|
||||
ClipperLib_Z::Paths loops_trimmed;
|
||||
{
|
||||
// Produce a closed polyline (repeat the first point at the end).
|
||||
ClipperLib_Z::Paths input_clip;
|
||||
for (const Polygon &loop : loops) {
|
||||
input_clip.emplace_back();
|
||||
ClipperLib_Z::Path& out = input_clip.back();
|
||||
out.reserve(loop.points.size());
|
||||
int64_t loop_idx = &loop - &loops.front();
|
||||
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) {
|
||||
Line line(*last_pt, *pt);
|
||||
bool inner = skirt_inner.first_intersection(line, &intersections[0]);
|
||||
bool outer = skirt_outer.first_intersection(line, &intersections[1]);
|
||||
del = del || inner || outer;
|
||||
if (inner != outer) {// there is exactly one intersection
|
||||
lines.back().append(inner ? intersections[0] : intersections[1]);
|
||||
}
|
||||
if (inner && outer) {
|
||||
int nearest_idx = pt->nearest_point_index(intersections);
|
||||
lines.back().append(intersections[! nearest_idx]);
|
||||
lines.push_back(Polyline());
|
||||
lines.back().append(intersections[nearest_idx]);
|
||||
}
|
||||
}
|
||||
if (valid_point)
|
||||
lines.back().append(*pt);
|
||||
else {
|
||||
del = true;
|
||||
lines.push_back(Polyline());
|
||||
}
|
||||
}
|
||||
// If we found a single intersection, we should erase the respective ExtrusionLoop
|
||||
// and append the polylines that we created.
|
||||
if (del) {
|
||||
loops.erase(loops.begin() + (i--));
|
||||
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()));
|
||||
}
|
||||
}
|
||||
// Third, produce the extrusions, sorted by the source loop indices.
|
||||
{
|
||||
std::vector<std::pair<const ClipperLib_Z::Path*, size_t>> loops_trimmed_order;
|
||||
loops_trimmed_order.reserve(loops_trimmed.size());
|
||||
for (const ClipperLib_Z::Path &path : loops_trimmed) {
|
||||
size_t input_idx = 0;
|
||||
for (const ClipperLib_Z::IntPoint &pt : path)
|
||||
if (pt.Z > 0) {
|
||||
input_idx = (size_t)pt.Z;
|
||||
break;
|
||||
}
|
||||
assert(input_idx != 0);
|
||||
loops_trimmed_order.emplace_back(&path, input_idx);
|
||||
}
|
||||
std::stable_sort(loops_trimmed_order.begin(), loops_trimmed_order.end(),
|
||||
[](const std::pair<const ClipperLib_Z::Path*, size_t> &l, const std::pair<const ClipperLib_Z::Path*, size_t> &r) {
|
||||
return l.second < r.second;
|
||||
});
|
||||
Vec3f last_pt(0.f, 0.f, 0.f);
|
||||
|
||||
for (size_t i = 0; i < loops_trimmed_order.size();) {
|
||||
// Find all pieces that the initial loop was split into.
|
||||
size_t j = i + 1;
|
||||
for (; j < loops_trimmed_order.size() && loops_trimmed_order[i].first == loops_trimmed_order[j].first; ++ j) ;
|
||||
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) {
|
||||
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.
|
||||
|
|
|
@ -105,6 +105,8 @@
|
|||
#include <cereal/access.hpp>
|
||||
#include <cereal/types/base_class.hpp>
|
||||
|
||||
#include <clipper/clipper_z.hpp>
|
||||
#include <clipper/clipper.hpp>
|
||||
#include "BoundingBox.hpp"
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Config.hpp"
|
||||
|
|
Loading…
Reference in a new issue