mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-11 16:57:53 -06:00
Fixes for degenerate cases.
This commit is contained in:
parent
c82fd692c3
commit
3b0e0aaed4
4 changed files with 80 additions and 63 deletions
|
@ -239,48 +239,49 @@ void offset(ExPolygon& sh, coord_t distance, bool edgerounding = true) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void offset(Polygon& sh, coord_t distance, bool edgerounding = true) {
|
void offset(Polygon &sh, coord_t distance, bool edgerounding = true)
|
||||||
using ClipperLib::ClipperOffset;
|
{
|
||||||
using ClipperLib::jtRound;
|
using ClipperLib::ClipperOffset;
|
||||||
using ClipperLib::jtMiter;
|
using ClipperLib::jtRound;
|
||||||
using ClipperLib::etClosedPolygon;
|
using ClipperLib::jtMiter;
|
||||||
using ClipperLib::Paths;
|
using ClipperLib::etClosedPolygon;
|
||||||
using ClipperLib::Path;
|
using ClipperLib::Paths;
|
||||||
|
using ClipperLib::Path;
|
||||||
|
|
||||||
auto&& ctour = Slic3rMultiPoint_to_ClipperPath(sh);
|
auto &&ctour = Slic3rMultiPoint_to_ClipperPath(sh);
|
||||||
|
|
||||||
// If the input is not at least a triangle, we can not do this algorithm
|
// If the input is not at least a triangle, we can not do this algorithm
|
||||||
if(ctour.size() < 3) {
|
if (ctour.size() < 3) {
|
||||||
BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!";
|
BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClipperOffset offs;
|
ClipperOffset offs;
|
||||||
offs.ArcTolerance = 0.01*scaled(1.);
|
offs.ArcTolerance = 0.01 * scaled(1.);
|
||||||
Paths result;
|
Paths result;
|
||||||
offs.AddPath(ctour, edgerounding ? jtRound : jtMiter, etClosedPolygon);
|
offs.AddPath(ctour, edgerounding ? jtRound : jtMiter, etClosedPolygon);
|
||||||
offs.Execute(result, static_cast<double>(distance));
|
offs.Execute(result, static_cast<double>(distance));
|
||||||
|
|
||||||
// Offsetting reverts the orientation and also removes the last vertex
|
// Offsetting reverts the orientation and also removes the last vertex
|
||||||
// so boost will not have a closed polygon.
|
// so boost will not have a closed polygon.
|
||||||
|
|
||||||
bool found_the_contour = false;
|
bool found_the_contour = false;
|
||||||
for(auto& r : result) {
|
for (auto &r : result) {
|
||||||
if(ClipperLib::Orientation(r)) {
|
if (ClipperLib::Orientation(r)) {
|
||||||
// We don't like if the offsetting generates more than one contour
|
// We don't like if the offsetting generates more than one contour
|
||||||
// but throwing would be an overkill. Instead, we should warn the
|
// but throwing would be an overkill. Instead, we should warn the
|
||||||
// caller about the inability to create correct geometries
|
// caller about the inability to create correct geometries
|
||||||
if(!found_the_contour) {
|
if (!found_the_contour) {
|
||||||
auto rr = ClipperPath_to_Slic3rPolygon(r);
|
auto rr = ClipperPath_to_Slic3rPolygon(r);
|
||||||
sh.points.swap(rr.points);
|
sh.points.swap(rr.points);
|
||||||
found_the_contour = true;
|
found_the_contour = true;
|
||||||
} else {
|
} else {
|
||||||
BOOST_LOG_TRIVIAL(warning)
|
BOOST_LOG_TRIVIAL(warning)
|
||||||
<< "Warning: offsetting result is invalid!";
|
<< "Warning: offsetting result is invalid!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unification of polygons (with clipper) preserving holes as well.
|
/// Unification of polygons (with clipper) preserving holes as well.
|
||||||
ExPolygons unify(const ExPolygons& shapes) {
|
ExPolygons unify(const ExPolygons& shapes) {
|
||||||
|
|
|
@ -73,6 +73,7 @@ public:
|
||||||
|
|
||||||
inline double ground_level() const { return m_ground_level + m_gnd_offset; }
|
inline double ground_level() const { return m_ground_level + m_gnd_offset; }
|
||||||
inline void ground_level_offset(double o) { m_gnd_offset = o; }
|
inline void ground_level_offset(double o) { m_gnd_offset = o; }
|
||||||
|
inline double ground_level_offset() const { return m_gnd_offset; }
|
||||||
|
|
||||||
inline const Eigen::MatrixXd& V() const { return m_V; }
|
inline const Eigen::MatrixXd& V() const { return m_V; }
|
||||||
inline const Eigen::MatrixXi& F() const { return m_F; }
|
inline const Eigen::MatrixXi& F() const { return m_F; }
|
||||||
|
|
|
@ -530,6 +530,7 @@ struct CompactBridge {
|
||||||
const Vec3d& ep,
|
const Vec3d& ep,
|
||||||
const Vec3d& n,
|
const Vec3d& n,
|
||||||
double r,
|
double r,
|
||||||
|
bool endball = true,
|
||||||
size_t steps = 45)
|
size_t steps = 45)
|
||||||
{
|
{
|
||||||
Vec3d startp = sp + r * n;
|
Vec3d startp = sp + r * n;
|
||||||
|
@ -543,12 +544,14 @@ struct CompactBridge {
|
||||||
double fa = 2*PI/steps;
|
double fa = 2*PI/steps;
|
||||||
auto upperball = sphere(r, Portion{PI / 2 - fa, PI}, fa);
|
auto upperball = sphere(r, Portion{PI / 2 - fa, PI}, fa);
|
||||||
for(auto& p : upperball.points) p += startp;
|
for(auto& p : upperball.points) p += startp;
|
||||||
|
|
||||||
auto lowerball = sphere(r, Portion{0, PI/2 + 2*fa}, fa);
|
if(endball) {
|
||||||
for(auto& p : lowerball.points) p += endp;
|
auto lowerball = sphere(r, Portion{0, PI/2 + 2*fa}, fa);
|
||||||
|
for(auto& p : lowerball.points) p += endp;
|
||||||
|
mesh.merge(lowerball);
|
||||||
|
}
|
||||||
|
|
||||||
mesh.merge(upperball);
|
mesh.merge(upperball);
|
||||||
mesh.merge(lowerball);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -594,13 +597,17 @@ struct Pad {
|
||||||
// base silhouette. Create the offsetted version and punch the
|
// base silhouette. Create the offsetted version and punch the
|
||||||
// breaksticks across its perimeter.
|
// breaksticks across its perimeter.
|
||||||
|
|
||||||
ExPolygons modelbase_sticks = modelbase;
|
ExPolygons modelbase_offs = modelbase;
|
||||||
|
|
||||||
if (pcfg.embed_object.object_gap_mm > 0.0)
|
if (pcfg.embed_object.object_gap_mm > 0.0)
|
||||||
modelbase_sticks
|
modelbase_offs
|
||||||
= offset_ex(modelbase_sticks,
|
= offset_ex(modelbase_offs,
|
||||||
float(scaled(pcfg.embed_object.object_gap_mm)));
|
float(scaled(pcfg.embed_object.object_gap_mm)));
|
||||||
|
|
||||||
|
// Create a spatial index of the support silhouette polygons.
|
||||||
|
// This will be used to check for intersections with the model
|
||||||
|
// silhouette polygons. If there is no intersection, then a certain
|
||||||
|
// part of the pad is redundant as it does not host any supports.
|
||||||
BoxIndex bindex;
|
BoxIndex bindex;
|
||||||
{
|
{
|
||||||
unsigned idx = 0;
|
unsigned idx = 0;
|
||||||
|
@ -611,14 +618,27 @@ struct Pad {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Punching the breaksticks across the offsetted polygon perimeters
|
||||||
ExPolygons pad_stickholes; pad_stickholes.reserve(modelbase.size());
|
ExPolygons pad_stickholes; pad_stickholes.reserve(modelbase.size());
|
||||||
for(auto& poly : modelbase_sticks) {
|
for(auto& poly : modelbase_offs) {
|
||||||
|
|
||||||
if (!bindex.query(poly.contour.bounding_box(),
|
std::vector<BoxIndexEl> qres =
|
||||||
BoxIndex::qtIntersects).empty()) {
|
bindex.query(poly.contour.bounding_box(),
|
||||||
|
BoxIndex::qtIntersects);
|
||||||
|
|
||||||
|
if (!qres.empty()) {
|
||||||
|
|
||||||
|
// The model silhouette polygon 'poly' HAS an intersection
|
||||||
|
// with the support silhouettes. Include this polygon
|
||||||
|
// in the pad holes with the breaksticks and merge the
|
||||||
|
// original (offsetted) version with the rest of the pad
|
||||||
|
// base plate.
|
||||||
|
|
||||||
basep.emplace_back(poly.contour);
|
basep.emplace_back(poly.contour);
|
||||||
|
|
||||||
|
// The holes of 'poly' will become positive parts of the
|
||||||
|
// pad, so they has to be checked for intersections as well
|
||||||
|
// and erased if there is no intersection with the supports
|
||||||
auto it = poly.holes.begin();
|
auto it = poly.holes.begin();
|
||||||
while(it != poly.holes.end()) {
|
while(it != poly.holes.end()) {
|
||||||
if (bindex.query(it->bounding_box(),
|
if (bindex.query(it->bounding_box(),
|
||||||
|
@ -627,7 +647,8 @@ struct Pad {
|
||||||
else
|
else
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Punch the breaksticks
|
||||||
sla::breakstick_holes(
|
sla::breakstick_holes(
|
||||||
poly,
|
poly,
|
||||||
pcfg.embed_object.object_gap_mm, // padding
|
pcfg.embed_object.object_gap_mm, // padding
|
||||||
|
@ -638,7 +659,7 @@ struct Pad {
|
||||||
pad_stickholes.emplace_back(poly);
|
pad_stickholes.emplace_back(poly);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
create_base_pool(basep, tmesh, pad_stickholes, cfg);
|
create_base_pool(basep, tmesh, pad_stickholes, cfg);
|
||||||
} else {
|
} else {
|
||||||
for (const ExPolygon &bp : modelbase) basep.emplace_back(bp.contour);
|
for (const ExPolygon &bp : modelbase) basep.emplace_back(bp.contour);
|
||||||
|
@ -1495,12 +1516,13 @@ class SLASupportTree::Algorithm {
|
||||||
Vec3d pgnd = {endp(X), endp(Y), gndlvl};
|
Vec3d pgnd = {endp(X), endp(Y), gndlvl};
|
||||||
can_add_base = result.score > min_dist;
|
can_add_base = result.score > min_dist;
|
||||||
|
|
||||||
|
double gnd_offs = m_mesh.ground_level_offset();
|
||||||
auto abort_in_shame =
|
auto abort_in_shame =
|
||||||
[&normal_mode, &can_add_base, &endp, jp, gndlvl]()
|
[gnd_offs, &normal_mode, &can_add_base, &endp, jp, gndlvl]()
|
||||||
{
|
{
|
||||||
normal_mode = true;
|
normal_mode = true;
|
||||||
can_add_base = false; // Nothing left to do, hope for the best
|
can_add_base = false; // Nothing left to do, hope for the best
|
||||||
endp = {jp(X), jp(Y), gndlvl};
|
endp = {jp(X), jp(Y), gndlvl - gnd_offs };
|
||||||
};
|
};
|
||||||
|
|
||||||
// We have to check if the bridge is feasible.
|
// We have to check if the bridge is feasible.
|
||||||
|
@ -2317,7 +2339,8 @@ public:
|
||||||
double idist = bridge_mesh_intersect(sph, dir, R, true);
|
double idist = bridge_mesh_intersect(sph, dir, R, true);
|
||||||
double dist = ray_mesh_intersect(sj, dir);
|
double dist = ray_mesh_intersect(sj, dir);
|
||||||
if (std::isinf(dist))
|
if (std::isinf(dist))
|
||||||
dist = sph(Z) - m_result.ground_level - HWIDTH_MM;
|
dist = sph(Z) - m_mesh.ground_level()
|
||||||
|
+ m_mesh.ground_level_offset();
|
||||||
|
|
||||||
if(std::isnan(idist) || idist < 2*R ||
|
if(std::isnan(idist) || idist < 2*R ||
|
||||||
std::isnan(dist) || dist < 2*R)
|
std::isnan(dist) || dist < 2*R)
|
||||||
|
@ -2329,7 +2352,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec3d ej = sj + (dist + HWIDTH_MM)* dir;
|
Vec3d ej = sj + (dist + HWIDTH_MM)* dir;
|
||||||
m_result.add_compact_bridge(sp, ej, n, R);
|
m_result.add_compact_bridge(sp, ej, n, R, !std::isinf(dist));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -1084,9 +1084,6 @@ void SLAPrint::process()
|
||||||
using ClipperPolygons = std::vector<ClipperPolygon>;
|
using ClipperPolygons = std::vector<ClipperPolygon>;
|
||||||
namespace sl = libnest2d::shapelike; // For algorithms
|
namespace sl = libnest2d::shapelike; // For algorithms
|
||||||
|
|
||||||
// If the raster has vertical orientation, we will flip the coordinates
|
|
||||||
// bool flpXY = m_printer_config.display_orientation.getInt() == SLADisplayOrientation::sladoPortrait;
|
|
||||||
|
|
||||||
// Set up custom union and diff functions for clipper polygons
|
// Set up custom union and diff functions for clipper polygons
|
||||||
auto polyunion = [] (const ClipperPolygons& subjects)
|
auto polyunion = [] (const ClipperPolygons& subjects)
|
||||||
{
|
{
|
||||||
|
@ -1194,11 +1191,6 @@ void SLAPrint::process()
|
||||||
sl::translate(poly, ClipperPoint{instances[i].shift(X),
|
sl::translate(poly, ClipperPoint{instances[i].shift(X),
|
||||||
instances[i].shift(Y)});
|
instances[i].shift(Y)});
|
||||||
|
|
||||||
// if (flpXY) {
|
|
||||||
// for(auto& p : poly.Contour) std::swap(p.X, p.Y);
|
|
||||||
// for(auto& h : poly.Holes) for(auto& p : h) std::swap(p.X, p.Y);
|
|
||||||
// }
|
|
||||||
|
|
||||||
polygons.emplace_back(std::move(poly));
|
polygons.emplace_back(std::move(poly));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue