Fix concave hull having holes.

This commit is contained in:
tamasmeszaros 2019-10-09 14:59:09 +02:00
parent a6a7303e25
commit 175b6fd9f5
6 changed files with 292 additions and 201 deletions

View file

@ -0,0 +1,171 @@
#include "ConcaveHull.hpp"
#include <libslic3r/MTUtils.hpp>
#include <libslic3r/ClipperUtils.hpp>
#include "SLASpatIndex.hpp"
#include <boost/log/trivial.hpp>
namespace Slic3r {
namespace sla {
inline Vec3d to_vec3(const Vec2crd &v2) { return {double(v2(X)), double(v2(Y)), 0.}; }
inline Vec3d to_vec3(const Vec2d &v2) { return {v2(X), v2(Y), 0.}; }
inline Vec2crd to_vec2(const Vec3d &v3) { return {coord_t(v3(X)), coord_t(v3(Y))}; }
Point ConcaveHull::centroid(const Points &pp)
{
Point c;
switch(pp.size()) {
case 0: break;
case 1: c = pp.front(); break;
case 2: c = (pp[0] + pp[1]) / 2; break;
default: {
auto MAX = std::numeric_limits<Point::coord_type>::max();
auto MIN = std::numeric_limits<Point::coord_type>::min();
Point min = {MAX, MAX}, max = {MIN, MIN};
for(auto& p : pp) {
if(p(0) < min(0)) min(0) = p(0);
if(p(1) < min(1)) min(1) = p(1);
if(p(0) > max(0)) max(0) = p(0);
if(p(1) > max(1)) max(1) = p(1);
}
c(0) = min(0) + (max(0) - min(0)) / 2;
c(1) = min(1) + (max(1) - min(1)) / 2;
break;
}
}
return c;
}
// As it shows, the current offset_ex in ClipperUtils hangs if used in jtRound
// mode
ClipperLib::Paths fast_offset(const ClipperLib::Paths &paths,
coord_t delta,
ClipperLib::JoinType jointype)
{
using ClipperLib::ClipperOffset;
using ClipperLib::etClosedPolygon;
using ClipperLib::Paths;
using ClipperLib::Path;
ClipperOffset offs;
offs.ArcTolerance = scaled<double>(0.01);
for (auto &p : paths)
// If the input is not at least a triangle, we can not do this algorithm
if(p.size() < 3) {
BOOST_LOG_TRIVIAL(error) << "Invalid geometry for offsetting!";
return {};
}
offs.AddPaths(paths, jointype, etClosedPolygon);
Paths result;
offs.Execute(result, static_cast<double>(delta));
return result;
}
Points ConcaveHull::calculate_centroids() const
{
// We get the centroids of all the islands in the 2D slice
Points centroids = reserve_vector<Point>(m_polys.size());
std::transform(m_polys.begin(), m_polys.end(),
std::back_inserter(centroids),
[this](const Polygon &poly) { return centroid(poly); });
return centroids;
}
void ConcaveHull::merge_polygons() { m_polys = get_contours(union_ex(m_polys)); }
void ConcaveHull::add_connector_rectangles(const Points &centroids,
coord_t max_dist,
ThrowOnCancel thr)
{
// Centroid of the centroids of islands. This is where the additional
// connector sticks are routed.
Point cc = centroid(centroids);
PointIndex ctrindex;
unsigned idx = 0;
for(const Point &ct : centroids) ctrindex.insert(to_vec3(ct), idx++);
m_polys.reserve(m_polys.size() + centroids.size());
idx = 0;
for (const Point &c : centroids) {
thr();
double dx = c.x() - cc.x(), dy = c.y() - cc.y();
double l = std::sqrt(dx * dx + dy * dy);
double nx = dx / l, ny = dy / l;
const Point &ct = centroids[idx];
std::vector<PointIndexEl> result = ctrindex.nearest(to_vec3(ct), 2);
double dist = max_dist;
for (const PointIndexEl &el : result)
if (el.second != idx) {
dist = Line(to_vec2(el.first), ct).length();
break;
}
idx++;
if (dist >= max_dist) return;
Polygon r;
r.points.reserve(3);
r.points.emplace_back(cc);
Point n(scaled(nx), scaled(ny));
r.points.emplace_back(c + Point(n.y(), -n.x()));
r.points.emplace_back(c + Point(-n.y(), n.x()));
offset(r, scaled<float>(1.));
m_polys.emplace_back(r);
}
}
ConcaveHull::ConcaveHull(const Polygons &polys, double mergedist, ThrowOnCancel thr)
{
if(polys.empty()) return;
m_polys = polys;
merge_polygons();
if(m_polys.size() == 1) return;
Points centroids = calculate_centroids();
add_connector_rectangles(centroids, scaled(mergedist), thr);
merge_polygons();
}
ExPolygons ConcaveHull::to_expolygons() const
{
auto ret = reserve_vector<ExPolygon>(m_polys.size());
for (const Polygon &p : m_polys) ret.emplace_back(ExPolygon(p));
return ret;
}
ExPolygons offset_waffle_style_ex(const ConcaveHull &hull, coord_t delta)
{
ClipperLib::Paths paths = Slic3rMultiPoints_to_ClipperPaths(hull.polygons());
paths = fast_offset(paths, 2 * delta, ClipperLib::jtRound);
paths = fast_offset(paths, -delta, ClipperLib::jtRound);
ExPolygons ret = ClipperPaths_to_Slic3rExPolygons(paths);
for (ExPolygon &p : ret) p.holes = {};
return ret;
}
Polygons offset_waffle_style(const ConcaveHull &hull, coord_t delta)
{
return to_polygons(offset_waffle_style_ex(hull, delta));
}
}} // namespace Slic3r::sla