Step forward in the C++ supports.
This commit is contained in:
parent
8b0784f26c
commit
bde2ee6a7e
1 changed files with 158 additions and 18 deletions
|
@ -10,6 +10,7 @@
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
// #define SLIC3R_DEBUG
|
// #define SLIC3R_DEBUG
|
||||||
|
|
||||||
|
@ -243,15 +244,6 @@ void PrintObjectSupportMaterial::generate(PrintObject &object)
|
||||||
// Depending on whether the support is soluble or not, the contact layer thickness is decided.
|
// Depending on whether the support is soluble or not, the contact layer thickness is decided.
|
||||||
MyLayersPtr bottom_contacts = this->bottom_contact_layers(object, top_contacts, layer_storage);
|
MyLayersPtr bottom_contacts = this->bottom_contact_layers(object, top_contacts, layer_storage);
|
||||||
|
|
||||||
#ifdef SLIC3R_DEBUG
|
|
||||||
for (MyLayersPtr::const_iterator it = bottom_contacts.begin(); it != bottom_contacts.end(); ++ it) {
|
|
||||||
const MyLayer &layer = *(*it);
|
|
||||||
::Slic3r::SVG svg(debug_out_path("support-bottom-contacts-%d-%lf.svg", iRun, layer.print_z), get_extents(layer.polygons));
|
|
||||||
Slic3r::ExPolygons expolys = union_ex(layer.polygons, false);
|
|
||||||
svg.draw(expolys);
|
|
||||||
}
|
|
||||||
#endif /* SLIC3R_DEBUG */
|
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << "Support generator - Trimming top contacts by bottom contacts";
|
BOOST_LOG_TRIVIAL(info) << "Support generator - Trimming top contacts by bottom contacts";
|
||||||
|
|
||||||
// Because the top and bottom contacts are thick slabs, they may overlap causing over extrusion
|
// Because the top and bottom contacts are thick slabs, they may overlap causing over extrusion
|
||||||
|
@ -664,10 +656,9 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
||||||
offset(
|
offset(
|
||||||
diff_polygons,
|
diff_polygons,
|
||||||
SUPPORT_MATERIAL_MARGIN / NUM_MARGIN_STEPS,
|
SUPPORT_MATERIAL_MARGIN / NUM_MARGIN_STEPS,
|
||||||
CLIPPER_OFFSET_SCALE,
|
|
||||||
ClipperLib::jtRound,
|
ClipperLib::jtRound,
|
||||||
// round mitter limit
|
// round mitter limit
|
||||||
scale_(0.05) * CLIPPER_OFFSET_SCALE),
|
scale_(0.05)),
|
||||||
slices_margin);
|
slices_margin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -753,9 +744,128 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_
|
||||||
return contact_out;
|
return contact_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct PointHash {
|
||||||
|
size_t operator()(const Point &pt) const {
|
||||||
|
return std::hash<coord_t>()(pt.x) ^ std::hash<coord_t>()(pt.y);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::unordered_set<Point,PointHash> PointHashMap;
|
||||||
|
|
||||||
|
void fillet(Polygon &poly, PointHashMap &new_points_hash_map)
|
||||||
|
{
|
||||||
|
if (poly.points.size() < 3)
|
||||||
|
// an invalid contour will not be modified.
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Flag describing a contour point.
|
||||||
|
std::vector<char> point_flag(std::vector<char>(poly.points.size(), 0));
|
||||||
|
|
||||||
|
// Does a point belong to new points?
|
||||||
|
for (size_t i = 0; i < poly.points.size(); ++ i)
|
||||||
|
if (new_points_hash_map.find(poly.points[i]) != new_points_hash_map.end())
|
||||||
|
// Mark the point as from the new contour.
|
||||||
|
point_flag[i] = 1;
|
||||||
|
|
||||||
|
// Mark the intersection points between the old and new contours.
|
||||||
|
size_t j = poly.points.size() - 1;
|
||||||
|
bool has_some = false;
|
||||||
|
for (size_t i = 0; i < poly.points.size(); j = i, ++ i)
|
||||||
|
if ((point_flag[i] ^ point_flag[j]) & 1) {
|
||||||
|
point_flag[(point_flag[i] & 1) ? j : i] |= 2;
|
||||||
|
has_some = true;
|
||||||
|
}
|
||||||
|
if (! has_some)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Mark a range of points around the intersection points.
|
||||||
|
const double rounding_range = scale_(1.5);
|
||||||
|
std::vector<Pointf> pts;
|
||||||
|
pts.reserve(poly.points.size());
|
||||||
|
for (int i = 0; i < int(poly.points.size()); ++ i) {
|
||||||
|
if (point_flag[i] & 2) {
|
||||||
|
point_flag[i] |= 4;
|
||||||
|
// Extend a filetting span left / right from i by an Euclidian distance of rounding_range.
|
||||||
|
double d = 0.f;
|
||||||
|
const Point *pt = &poly.points[i];
|
||||||
|
for (int j = 1; j < int(poly.points.size()); ++ j) {
|
||||||
|
int idx = (i + j) % poly.points.size();
|
||||||
|
const Point *pt2 = &poly.points[idx];
|
||||||
|
d += pt->distance_to(*pt2);
|
||||||
|
if (d > rounding_range)
|
||||||
|
break;
|
||||||
|
point_flag[idx] |= 4;
|
||||||
|
pt = pt2;
|
||||||
|
}
|
||||||
|
for (int j = 1; j < int(poly.points.size()); ++ j) {
|
||||||
|
int idx = (i + int(poly.points.size()) - j) % poly.points.size();
|
||||||
|
const Point *pt2 = &poly.points[idx];
|
||||||
|
d += pt->distance_to(*pt2);
|
||||||
|
if (d > rounding_range)
|
||||||
|
break;
|
||||||
|
point_flag[idx] |= 4;
|
||||||
|
pt = pt2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pts.push_back(Pointf(poly.points[i].x, poly.points[i].y));
|
||||||
|
}
|
||||||
|
|
||||||
|
//FIXME avoid filetting over long edges. Insert new points into long edges at the ends of the filetting interval.
|
||||||
|
|
||||||
|
// Perform the filetting over the marked vertices.
|
||||||
|
std::vector<Pointf> pts2(pts);
|
||||||
|
double laplacian_weight = 0.5;
|
||||||
|
for (size_t i_round = 0; i_round < 5; ++ i_round) {
|
||||||
|
for (size_t i = 0; i < int(pts.size()); ++ i) {
|
||||||
|
if (point_flag[i] & 4) {
|
||||||
|
size_t prev = (i == 0) ? pts.size() - 1 : i - 1;
|
||||||
|
size_t next = (i + 1 == pts.size()) ? 0 : i + 1;
|
||||||
|
Pointf &p0 = pts[prev];
|
||||||
|
Pointf &p1 = pts[i];
|
||||||
|
Pointf &p2 = pts[next];
|
||||||
|
// Is the point reflex?
|
||||||
|
coordf_t c = cross(p1 - p0, p2 - p1);
|
||||||
|
if (c < 0)
|
||||||
|
// The point is reflex, perform Laplacian smoothing.
|
||||||
|
pts2[i] = (1. - laplacian_weight) * pts[i] + (0.5 * laplacian_weight) * (pts[prev] + pts[next]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pts.swap(pts2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mark vertices representing short edges for removal.
|
||||||
|
|
||||||
|
// Convert the filetted points back, remove points marked for removal.
|
||||||
|
j = 0;
|
||||||
|
for (size_t i = 0; i < poly.points.size(); ++ i) {
|
||||||
|
if (point_flag[i] & 8)
|
||||||
|
// Remove this point.
|
||||||
|
continue;
|
||||||
|
if (point_flag[i] & 4)
|
||||||
|
// Update the point coordinates.
|
||||||
|
poly.points[i] = Point(pts[i].x, pts[i].y);
|
||||||
|
if (j < i)
|
||||||
|
poly.points[j] = poly.points[i];
|
||||||
|
++ j;
|
||||||
|
}
|
||||||
|
if (j < poly.points.size())
|
||||||
|
poly.points.erase(poly.points.begin() + j, poly.points.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void fillet(Polygons &polygons, PointHashMap &new_points_hash_map)
|
||||||
|
{
|
||||||
|
for (Polygons::iterator it = polygons.begin(); it != polygons.end(); ++ it)
|
||||||
|
fillet(*it, new_points_hash_map);
|
||||||
|
}
|
||||||
|
|
||||||
PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_contact_layers(
|
PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_contact_layers(
|
||||||
const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage) const
|
const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage) const
|
||||||
{
|
{
|
||||||
|
#ifdef SLIC3R_DEBUG
|
||||||
|
static int iRun = 0;
|
||||||
|
++ iRun;
|
||||||
|
#endif /* SLIC3R_DEBUG */
|
||||||
|
|
||||||
// find object top surfaces
|
// find object top surfaces
|
||||||
// we'll use them to clip our support and detect where does it stick
|
// we'll use them to clip our support and detect where does it stick
|
||||||
MyLayersPtr bottom_contacts;
|
MyLayersPtr bottom_contacts;
|
||||||
|
@ -784,9 +894,30 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
|
||||||
if (projection.empty())
|
if (projection.empty())
|
||||||
continue;
|
continue;
|
||||||
if (projection_size_old < projection.size()) {
|
if (projection_size_old < projection.size()) {
|
||||||
|
// Fill in the new_points hash table with points of new contours.
|
||||||
|
PointHashMap new_points;
|
||||||
|
for (size_t i = projection_size_old; i < projection.size(); ++ i) {
|
||||||
|
const Polygon &poly = projection[i];
|
||||||
|
for (size_t j = 0; j < poly.points.size(); ++ j)
|
||||||
|
new_points.insert(poly.points[j]);
|
||||||
|
}
|
||||||
// Merge the newly added regions. Don't use the safety offset, the offset has been added already.
|
// Merge the newly added regions. Don't use the safety offset, the offset has been added already.
|
||||||
projection = union_(projection, false);
|
projection = union_(projection, false);
|
||||||
|
// Fillet transition between the old and new points.
|
||||||
|
fillet(projection, new_points);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SLIC3R_DEBUG
|
||||||
|
{
|
||||||
|
BoundingBox bbox = get_extents(projection);
|
||||||
|
bbox.merge(get_extents(top));
|
||||||
|
::Slic3r::SVG svg(debug_out_path("support-bottom-layers-raw-%d-%lf.svg", iRun, layer.print_z), bbox);
|
||||||
|
svg.draw(union_ex(top, false), "blue", 0.5f);
|
||||||
|
svg.draw(union_ex(projection, true), "red", 0.5f);
|
||||||
|
svg.draw(layer.slices.expolygons, "green", 0.5f);
|
||||||
|
}
|
||||||
|
#endif /* SLIC3R_DEBUG */
|
||||||
|
|
||||||
// Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any
|
// Now find whether any projection of the contact surfaces above layer.print_z not yet supported by any
|
||||||
// top surfaces above layer.print_z falls onto this top surface.
|
// top surfaces above layer.print_z falls onto this top surface.
|
||||||
// touching are the contact surfaces supported exclusively by this top surfaaces.
|
// touching are the contact surfaces supported exclusively by this top surfaaces.
|
||||||
|
@ -813,11 +944,21 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta
|
||||||
//FIXME how much to inflate the top surface?
|
//FIXME how much to inflate the top surface?
|
||||||
layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width()));
|
layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width()));
|
||||||
// Remove the areas that touched from the projection that will continue on next, lower, top surfaces.
|
// Remove the areas that touched from the projection that will continue on next, lower, top surfaces.
|
||||||
projection = diff(projection, touching);
|
// projection = diff(projection, touching);
|
||||||
|
projection = diff(projection, to_polygons(layer.slices.expolygons), true);
|
||||||
|
|
||||||
|
#ifdef SLIC3R_DEBUG
|
||||||
|
{
|
||||||
|
::Slic3r::SVG svg(debug_out_path("support-bottom-contacts-%d-%lf.svg", iRun, layer_new.print_z), get_extents(layer_new.polygons));
|
||||||
|
Slic3r::ExPolygons expolys = union_ex(layer_new.polygons, false);
|
||||||
|
svg.draw(expolys);
|
||||||
}
|
}
|
||||||
|
#endif /* SLIC3R_DEBUG */
|
||||||
}
|
}
|
||||||
|
|
||||||
std::reverse(bottom_contacts.begin(), bottom_contacts.end());
|
std::reverse(bottom_contacts.begin(), bottom_contacts.end());
|
||||||
|
} // if (! m_object_config->support_material_buildplate_only.value && ! top_contacts.empty())
|
||||||
|
|
||||||
return bottom_contacts;
|
return bottom_contacts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1038,9 +1179,8 @@ void PrintObjectSupportMaterial::generate_base_layers(
|
||||||
$fillet_radius_scaled,
|
$fillet_radius_scaled,
|
||||||
-$fillet_radius_scaled,
|
-$fillet_radius_scaled,
|
||||||
# Use a geometric offsetting for filleting.
|
# Use a geometric offsetting for filleting.
|
||||||
CLIPPER_OFFSET_SCALE,
|
|
||||||
JT_ROUND,
|
JT_ROUND,
|
||||||
0.2*$fillet_radius_scaled*CLIPPER_OFFSET_SCALE),
|
0.2*$fillet_radius_scaled),
|
||||||
$trim_polygons,
|
$trim_polygons,
|
||||||
false); // don't apply the safety offset.
|
false); // don't apply the safety offset.
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue