From 946b36bb4de80313fffd3a37f37406164c67d2d2 Mon Sep 17 00:00:00 2001 From: bubnikv Date: Tue, 29 Nov 2016 19:30:59 +0100 Subject: [PATCH] Supports using the EdgeGrid simplify_contour --- xs/src/libslic3r/SupportMaterial.cpp | 268 +++++++++++++++++++-------- xs/src/libslic3r/SupportMaterial.hpp | 7 +- 2 files changed, 198 insertions(+), 77 deletions(-) diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index cc9dd8f7b..835dea79d 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -5,6 +5,7 @@ #include "Print.hpp" #include "SupportMaterial.hpp" #include "Fill/FillBase.hpp" +#include "EdgeGrid.hpp" #include #include @@ -242,7 +243,10 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) // Determine the bottom contact surfaces of the supports over the top surfaces of the object. // 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); + std::vector layer_support_areas; + MyLayersPtr bottom_contacts = this->bottom_contact_layers_and_layer_support_areas( + object, top_contacts, layer_storage, + layer_support_areas); BOOST_LOG_TRIVIAL(info) << "Support generator - Trimming top contacts by bottom contacts"; @@ -263,7 +267,7 @@ void PrintObjectSupportMaterial::generate(PrintObject &object) BOOST_LOG_TRIVIAL(info) << "Support generator - Creating base layers"; // Fill in intermediate layers between the top / bottom support contact layers, trimmed by the object. - this->generate_base_layers(object, bottom_contacts, top_contacts, intermediate_layers); + this->generate_base_layers(object, bottom_contacts, top_contacts, intermediate_layers, layer_support_areas); #ifdef SLIC3R_DEBUG for (MyLayersPtr::const_iterator it = intermediate_layers.begin(); it != intermediate_layers.end(); ++ it) { @@ -429,7 +433,8 @@ Polygons collect_slices_outer(const Layer &layer) } // Find the top contact surfaces of the support or the raft. -PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_layers(const PrintObject &object, MyLayerStorage &layer_storage) const +PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_layers( + const PrintObject &object, MyLayerStorage &layer_storage) const { #ifdef SLIC3R_DEBUG static int iRun = 0; @@ -744,12 +749,7 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ return contact_out; } -struct PointHash { - size_t operator()(const Point &pt) const { - return std::hash()(pt.x) ^ std::hash()(pt.y); - } -}; - +#if 0 typedef std::unordered_set PointHashMap; void fillet(Polygon &poly, PointHashMap &new_points_hash_map) @@ -778,6 +778,35 @@ void fillet(Polygon &poly, PointHashMap &new_points_hash_map) if (! has_some) return; +#ifdef SLIC3R_DEBUG + static int iRun = 0; + ++ iRun; + { + FILE *pfile = ::fopen(debug_out_path("fillet-in-%d.bin", iRun).c_str(), "wb"); + size_t cnt = poly.points.size(); + ::fwrite(&cnt, 1, sizeof(cnt), pfile); + ::fwrite(poly.points.data(), cnt, sizeof(Point), pfile); + cnt = new_points_hash_map.size(); + ::fwrite(&cnt, 1, sizeof(cnt), pfile); + for (PointHashMap::iterator it = new_points_hash_map.begin(); it != new_points_hash_map.end(); ++ it) { + const Point &pt = *it; + ::fwrite(&pt, 1, sizeof(Point), pfile); + } + ::fclose(pfile); + } + ::Slic3r::SVG svg(debug_out_path("fillet-%d.svg", iRun), get_extents(poly)); + svg.draw(poly, "black", scale_(0.05)); + for (size_t i = 0; i < poly.points.size(); ++ i) { + const Point &pt1 = poly.points[i]; + const Point &pt2 = poly.points[(i+1)%poly.points.size()]; + if (new_points_hash_map.find(pt1) != new_points_hash_map.end()) + svg.draw(Line(pt1, pt2), "red", scale_(0.035)); + if (new_points_hash_map.find(pt1) != new_points_hash_map.end() && + new_points_hash_map.find(pt2) != new_points_hash_map.end()) + svg.draw(Line(pt1, pt2), "red", scale_(0.05)); + } +#endif + // Mark a range of points around the intersection points. const double rounding_range = scale_(1.5); std::vector pts; @@ -795,7 +824,7 @@ void fillet(Polygon &poly, PointHashMap &new_points_hash_map) if (d > rounding_range) break; point_flag[idx] |= 4; - pt = pt2; + //pt = pt2; } for (int j = 1; j < int(poly.points.size()); ++ j) { int idx = (i + int(poly.points.size()) - j) % poly.points.size(); @@ -804,7 +833,7 @@ void fillet(Polygon &poly, PointHashMap &new_points_hash_map) if (d > rounding_range) break; point_flag[idx] |= 4; - pt = pt2; + //pt = pt2; } } pts.push_back(Pointf(poly.points[i].x, poly.points[i].y)); @@ -850,6 +879,10 @@ void fillet(Polygon &poly, PointHashMap &new_points_hash_map) } if (j < poly.points.size()) poly.points.erase(poly.points.begin() + j, poly.points.end()); + +#ifdef SLIC3R_DEBUG + svg.draw_outline(poly, "blue", scale_(0.025)); +#endif /* SLIC3R_DEBUG */ } void fillet(Polygons &polygons, PointHashMap &new_points_hash_map) @@ -858,19 +891,46 @@ void fillet(Polygons &polygons, PointHashMap &new_points_hash_map) fillet(*it, new_points_hash_map); } -PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_contact_layers( - const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage) const +void union_and_fillet(Polygons &polygons, size_t n_polygons_old) +{ + if (n_polygons_old == polygons.size()) + // No new polygons. + return; + + // Fill in the new_points hash table with points of new contours. + PointHashMap new_points; + for (size_t i = n_polygons_old; i < polygons.size(); ++ i) { + const Polygon &poly = polygons[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. + polygons = union_(polygons, false); + // Fillet transition between the old and new points. + fillet(polygons, new_points); +} +#endif + +// Collect +PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_contact_layers_and_layer_support_areas( + const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage, + std::vector &layer_support_areas) const { #ifdef SLIC3R_DEBUG static int iRun = 0; ++ iRun; #endif /* SLIC3R_DEBUG */ + // Allocate empty surface areas, one per object layer. + layer_support_areas.assign(object.total_layer_count(), Polygons()); + // find object top surfaces // we'll use them to clip our support and detect where does it stick MyLayersPtr bottom_contacts; - if (! m_object_config->support_material_buildplate_only.value && ! top_contacts.empty()) + + if (! top_contacts.empty()) { + // There is some support to be built, if there are non-empty top surfaces detected. // Sum of unsupported contact areas above the current layer.print_z. Polygons projection; // Last top contact layer visited when collecting the projection of contact areas. @@ -878,36 +938,36 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta for (int layer_id = int(object.total_layer_count()) - 2; layer_id >= 0; -- layer_id) { BOOST_LOG_TRIVIAL(trace) << "Support generator - bottom_contact_layers - layer " << layer_id; const Layer &layer = *object.get_layer(layer_id); - Polygons top = collect_region_slices_by_type(layer, stTop); - if (top.empty()) - continue; + // Top surfaces of this layer, to be used to stop the surface volume from growing down. + Polygons top; + if (! m_object_config->support_material_buildplate_only) + top = collect_region_slices_by_type(layer, stTop); size_t projection_size_old = projection.size(); // Collect projections of all contact areas above or at the same level as this top surface. for (; contact_idx >= 0 && top_contacts[contact_idx]->print_z >= layer.print_z; -- contact_idx) { + Polygons polygons_new; // Contact surfaces are expanded away from the object, trimmed by the object. // Use a slight positive offset to overlap the touching regions. - polygons_append(projection, offset(top_contacts[contact_idx]->polygons, SCALED_EPSILON)); + polygons_append(polygons_new, offset(top_contacts[contact_idx]->polygons, SCALED_EPSILON)); + size_t size1 = polygons_new.size(); // These are the overhang surfaces. They are touching the object and they are not expanded away from the object. // Use a slight positive offset to overlap the touching regions. - polygons_append(projection, offset(*top_contacts[contact_idx]->aux_polygons, SCALED_EPSILON)); + polygons_append(polygons_new, offset(*top_contacts[contact_idx]->aux_polygons, SCALED_EPSILON)); +#if 0 + union_and_fillet(polygons_new, size1); +#else + union_(polygons_new); +#endif + polygons_append(projection, std::move(polygons_new)); } if (projection.empty()) continue; - 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. - projection = union_(projection, false); - // Fillet transition between the old and new points. - fillet(projection, new_points); - } - -#ifdef SLIC3R_DEBUG +#if 0 + union_and_fillet(projection, projection_size_old); +#else + union_(projection); +#endif + #ifdef SLIC3R_DEBUG { BoundingBox bbox = get_extents(projection); bbox.merge(get_extents(top)); @@ -916,48 +976,77 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::bottom_conta svg.draw(union_ex(projection, true), "red", 0.5f); svg.draw(layer.slices.expolygons, "green", 0.5f); } -#endif /* SLIC3R_DEBUG */ + #endif /* SLIC3R_DEBUG */ // 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. // touching are the contact surfaces supported exclusively by this top surfaaces. // Don't use a safety offset as it has been applied during insertion of polygons. - Polygons touching = intersection(top, projection, false); - if (touching.empty()) - continue; - // Allocate a new bottom contact layer. - MyLayer &layer_new = layer_allocate(layer_storage, sltBottomContact); - bottom_contacts.push_back(&layer_new); - // Grow top surfaces so that interface and support generation are generated - // with some spacing from object - it looks we don't need the actual - // top shapes so this can be done here - layer_new.height = m_soluble_interface ? - // Align the interface layer with the object's layer height. - object.get_layer(layer_id + 1)->height : - // Place a bridge flow interface layer over the top surface. - m_support_material_interface_flow.nozzle_diameter; - layer_new.print_z = layer.print_z + layer_new.height + - (m_soluble_interface ? 0. : m_object_config->support_material_contact_distance.value); - layer_new.bottom_z = layer.print_z; - layer_new.idx_object_layer_below = layer_id; - layer_new.bridging = ! m_soluble_interface; - //FIXME how much to inflate the top surface? - layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width())); + if (! top.empty()) { + Polygons touching = intersection(top, projection, false); + if (! touching.empty()) { + // Allocate a new bottom contact layer. + MyLayer &layer_new = layer_allocate(layer_storage, sltBottomContact); + bottom_contacts.push_back(&layer_new); + // Grow top surfaces so that interface and support generation are generated + // with some spacing from object - it looks we don't need the actual + // top shapes so this can be done here + layer_new.height = m_soluble_interface ? + // Align the interface layer with the object's layer height. + object.get_layer(layer_id + 1)->height : + // Place a bridge flow interface layer over the top surface. + m_support_material_interface_flow.nozzle_diameter; + layer_new.print_z = layer.print_z + layer_new.height + + (m_soluble_interface ? 0. : m_object_config->support_material_contact_distance.value); + layer_new.bottom_z = layer.print_z; + layer_new.idx_object_layer_below = layer_id; + layer_new.bridging = ! m_soluble_interface; + //FIXME how much to inflate the top surface? + layer_new.polygons = offset(touching, float(m_support_material_flow.scaled_width())); + #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 */ + } + } // ! top.empty() + + remove_sticks(projection); + remove_degenerate(projection); + + // Create an EdgeGrid, initialize it with projection, initialize signed distance field. + Slic3r::EdgeGrid::Grid grid; + coord_t grid_resolution = scale_(1.5f); + BoundingBox bbox = get_extents(projection); + bbox.offset(20); + bbox.align_to_grid(grid_resolution); + grid.set_bbox(bbox); + grid.create(projection, grid_resolution); + grid.calculate_sdf(); + + // Extract a bounding contour from the grid. + Polygons projection_simplified = grid.contours_simplified(); +#ifdef SLIC3R_DEBUG + { + BoundingBox bbox = get_extents(projection); + bbox.merge(get_extents(projection_simplified)); + + ::Slic3r::SVG svg(debug_out_path("support-bottom-contacts-simplified-%d-%d.svg", iRun, layer_id), bbox); + svg.draw(union_ex(projection, false), "blue", 0.5); + svg.draw(union_ex(projection_simplified, false), "red", 0.5); + } +#endif /* SLIC3R_DEBUG */ + projection = std::move(projection_simplified); + // Remove the areas that touched from the projection that will continue on next, lower, top surfaces. // 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 */ + layer_support_areas[layer_id] = projection; } - std::reverse(bottom_contacts.begin(), bottom_contacts.end()); - } // if (! m_object_config->support_material_buildplate_only.value && ! top_contacts.empty()) + } // ! top_contacts.empty() return bottom_contacts; } @@ -1093,7 +1182,8 @@ void PrintObjectSupportMaterial::generate_base_layers( const PrintObject &object, const MyLayersPtr &bottom_contacts, const MyLayersPtr &top_contacts, - MyLayersPtr &intermediate_layers) const + MyLayersPtr &intermediate_layers, + std::vector &layer_support_areas) const { #ifdef SLIC3R_DEBUG static int iRun = 0; @@ -1106,25 +1196,50 @@ void PrintObjectSupportMaterial::generate_base_layers( // coordf_t fillet_radius_scaled = scale_(m_object_config->support_material_spacing); int idx_top_contact_above = int(top_contacts.size()) - 1; int idx_bottom_contact_overlapping = int(bottom_contacts.size()) - 1; + int idx_object_layer_above = int(object.total_layer_count()) - 1; for (int idx_intermediate = int(intermediate_layers.size()) - 1; idx_intermediate >= 0; -- idx_intermediate) { + BOOST_LOG_TRIVIAL(trace) << "Support generator - generate_base_layers - creating layer " << + idx_intermediate << " of " << intermediate_layers.size(); MyLayer &layer_intermediate = *intermediate_layers[idx_intermediate]; - // New polygons for layer_intermediate. - Polygons polygons_new; - // Find a top_contact layer touching the layer_intermediate from above, if any, and collect its polygons into polygons_new. while (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->bottom_z > layer_intermediate.print_z + EPSILON) -- idx_top_contact_above; - if (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->print_z > layer_intermediate.print_z) - polygons_append(polygons_new, top_contacts[idx_top_contact_above]->polygons); - + + // New polygons for layer_intermediate. + Polygons polygons_new; + +#if 0 // Add polygons projected from the intermediate layer above. if (idx_intermediate + 1 < int(intermediate_layers.size())) polygons_append(polygons_new, intermediate_layers[idx_intermediate+1]->polygons); + if (idx_top_contact_above >= 0 && top_contacts[idx_top_contact_above]->print_z > layer_intermediate.print_z) { + // Contact surfaces are expanded away from the object, trimmed by the object. + // Use a slight positive offset to overlap the touching regions. + Polygons polygons_new2; + polygons_append(polygons_new2, offset(top_contacts[idx_top_contact_above]->polygons, SCALED_EPSILON)); + size_t size2 = polygons_new2.size(); + // These are the overhang surfaces. They are touching the object and they are not expanded away from the object. + // Use a slight positive offset to overlap the touching regions. + polygons_append(polygons_new2, offset(*top_contacts[idx_top_contact_above]->aux_polygons, SCALED_EPSILON)); + union_and_fillet(polygons_new2, size2); + if (! polygons_new2.empty()) { + size_t polygons_size_old = polygons_new.size(); + polygons_append(polygons_new, std::move(polygons_new2)); + union_and_fillet(polygons_new, polygons_size_old); + } + } +#else + // Use the precomputed layer_support_areas. + while (idx_object_layer_above > 0 && object.get_layer(idx_object_layer_above - 1)->print_z > layer_intermediate.print_z - EPSILON) + -- idx_object_layer_above; + polygons_new = layer_support_areas[idx_object_layer_above]; +#endif + // Polygons to trim polygons_new. - Polygons polygons_trimming; + Polygons polygons_trimming; // Find the first top_contact layer intersecting with this layer. int idx_top_contact_overlapping = idx_top_contact_above; @@ -1145,7 +1260,7 @@ void PrintObjectSupportMaterial::generate_base_layers( -- idx_bottom_contact_overlapping; // Collect all the top_contact layer intersecting with this layer. for (int i = idx_bottom_contact_overlapping; i >= 0; -- i) { - MyLayer &layer_bottom_overlapping = *bottom_contacts[idx_bottom_contact_overlapping]; + MyLayer &layer_bottom_overlapping = *bottom_contacts[i]; if (layer_bottom_overlapping.print_z < layer_intermediate.print_z - layer_intermediate.height + EPSILON) break; polygons_append(polygons_trimming, layer_bottom_overlapping.polygons); @@ -1203,6 +1318,9 @@ void PrintObjectSupportMaterial::generate_base_layers( size_t idx_object_layer_overlapping = 0; // For all intermediate support layers: for (MyLayersPtr::iterator it_layer = intermediate_layers.begin(); it_layer != intermediate_layers.end(); ++ it_layer) { + BOOST_LOG_TRIVIAL(trace) << "Support generator - generate_base_layers - trimmming layer " << + (it_layer - intermediate_layers.begin()) << " of " << intermediate_layers.size(); + MyLayer &layer_intermediate = *(*it_layer); if (layer_intermediate.polygons.empty()) // Empty support layer, nothing to trim. diff --git a/xs/src/libslic3r/SupportMaterial.hpp b/xs/src/libslic3r/SupportMaterial.hpp index 31a7899ce..abcaf676a 100644 --- a/xs/src/libslic3r/SupportMaterial.hpp +++ b/xs/src/libslic3r/SupportMaterial.hpp @@ -163,7 +163,9 @@ private: // Generate bottom contact layers supporting the top contact layers. // For a soluble interface material synchronize the layer heights with the object, // otherwise set the layer height to a bridging flow of a support interface nozzle. - MyLayersPtr bottom_contact_layers(const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage) const; + MyLayersPtr bottom_contact_layers_and_layer_support_areas( + const PrintObject &object, const MyLayersPtr &top_contacts, MyLayerStorage &layer_storage, + std::vector &layer_support_areas) const; // Trim the top_contacts layers with the bottom_contacts layers if they overlap, so there would not be enough vertical space for both of them. void trim_top_contacts_by_bottom_contacts(const PrintObject &object, const MyLayersPtr &bottom_contacts, MyLayersPtr &top_contacts) const; @@ -180,7 +182,8 @@ private: const PrintObject &object, const MyLayersPtr &bottom_contacts, const MyLayersPtr &top_contacts, - MyLayersPtr &intermediate_layers) const; + MyLayersPtr &intermediate_layers, + std::vector &layer_support_areas) const; Polygons generate_raft_base( const PrintObject &object,