diff --git a/xs/src/libslic3r/EdgeGrid.cpp b/xs/src/libslic3r/EdgeGrid.cpp index 801a27e3a..9249d0ff1 100644 --- a/xs/src/libslic3r/EdgeGrid.cpp +++ b/xs/src/libslic3r/EdgeGrid.cpp @@ -9,7 +9,9 @@ #endif /* SLIC3R_GUI */ #include "libslic3r.h" +#include "ClipperUtils.hpp" #include "EdgeGrid.hpp" +#include "SVG.hpp" #if 0 // Enable debugging and assert in this file. @@ -756,8 +758,8 @@ void EdgeGrid::Grid::calculate_sdf() float search_radius = float(m_resolution<<1); m_signed_distance_field.assign(nrows * ncols, search_radius); // For each cell: - for (size_t r = 0; r < m_rows; ++ r) { - for (size_t c = 0; c < m_cols; ++ c) { + for (int r = 0; r < (int)m_rows; ++ r) { + for (int c = 0; c < (int)m_cols; ++ c) { const Cell &cell = m_cells[r * m_cols + c]; // For each segment in the cell: for (size_t i = cell.begin; i != cell.end; ++ i) { @@ -842,6 +844,8 @@ void EdgeGrid::Grid::calculate_sdf() #if 0 static int iRun = 0; ++ iRun; + if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) + wxImage::AddHandler(new wxPNGHandler); //#ifdef SLIC3R_GUI { wxImage img(ncols, nrows); @@ -1356,9 +1360,101 @@ Polygons EdgeGrid::Grid::contours_simplified(coord_t offset, bool fill_holes) co return out; } +inline int segments_could_intersect( + const Slic3r::Point &ip1, const Slic3r::Point &ip2, + const Slic3r::Point &jp1, const Slic3r::Point &jp2) +{ + Slic3r::Point iv = ip1.vector_to(ip2); + Slic3r::Point vij1 = ip1.vector_to(jp1); + Slic3r::Point vij2 = ip1.vector_to(jp2); + int64_t tij1 = int64_t(iv.x) * int64_t(vij1.y) - int64_t(iv.y) * int64_t(vij1.x); // cross(iv, vij1) + int64_t tij2 = int64_t(iv.x) * int64_t(vij2.y) - int64_t(iv.y) * int64_t(vij2.x); // cross(iv, vij2) + int sij1 = (tij1 > 0) ? 1 : ((tij1 < 0) ? -1 : 0); // signum + int sij2 = (tij2 > 0) ? 1 : ((tij2 < 0) ? -1 : 0); + return sij1 * sij2; +} + +inline bool segments_intersect( + const Slic3r::Point &ip1, const Slic3r::Point &ip2, + const Slic3r::Point &jp1, const Slic3r::Point &jp2) +{ + return segments_could_intersect(ip1, ip2, jp1, jp2) <= 0 && + segments_could_intersect(jp1, jp2, ip1, ip2) <= 0; +} + +std::vector> EdgeGrid::Grid::intersecting_edges() const +{ + std::vector> out; + // For each cell: + for (int r = 0; r < (int)m_rows; ++ r) { + for (int c = 0; c < (int)m_cols; ++ c) { + const Cell &cell = m_cells[r * m_cols + c]; + // For each pair of segments in the cell: + for (size_t i = cell.begin; i != cell.end; ++ i) { + const Slic3r::Points &ipts = *m_contours[m_cell_data[i].first]; + size_t ipt = m_cell_data[i].second; + // End points of the line segment and their vector. + const Slic3r::Point &ip1 = ipts[ipt]; + const Slic3r::Point &ip2 = ipts[(ipt + 1 == ipts.size()) ? 0 : ipt + 1]; + for (size_t j = i + 1; j != cell.end; ++ j) { + const Slic3r::Points &jpts = *m_contours[m_cell_data[j].first]; + size_t jpt = m_cell_data[j].second; + // End points of the line segment and their vector. + const Slic3r::Point &jp1 = jpts[jpt]; + const Slic3r::Point &jp2 = jpts[(jpt + 1 == jpts.size()) ? 0 : jpt + 1]; + if (&ipts == &jpts && (&ip1 == &jp2 || &jp1 == &ip2)) + // Segments of the same contour share a common vertex. + continue; + if (segments_intersect(ip1, ip2, jp1, jp2)) { + // The two segments intersect. Add them to the output. + int jfirst = (&jpts < &ipts) || (&jpts == &ipts && jpt < ipt); + out.emplace_back(jfirst ? + std::make_pair(std::make_pair(&ipts, ipt), std::make_pair(&jpts, jpt)) : + std::make_pair(std::make_pair(&ipts, ipt), std::make_pair(&jpts, jpt))); + } + } + } + } + } + Slic3r::sort_remove_duplicates(out); + return out; +} + +bool EdgeGrid::Grid::has_intersecting_edges() const +{ + // For each cell: + for (int r = 0; r < (int)m_rows; ++ r) { + for (int c = 0; c < (int)m_cols; ++ c) { + const Cell &cell = m_cells[r * m_cols + c]; + // For each pair of segments in the cell: + for (size_t i = cell.begin; i != cell.end; ++ i) { + const Slic3r::Points &ipts = *m_contours[m_cell_data[i].first]; + size_t ipt = m_cell_data[i].second; + // End points of the line segment and their vector. + const Slic3r::Point &ip1 = ipts[ipt]; + const Slic3r::Point &ip2 = ipts[(ipt + 1 == ipts.size()) ? 0 : ipt + 1]; + for (size_t j = i + 1; j != cell.end; ++ j) { + const Slic3r::Points &jpts = *m_contours[m_cell_data[j].first]; + size_t jpt = m_cell_data[j].second; + // End points of the line segment and their vector. + const Slic3r::Point &jp1 = jpts[jpt]; + const Slic3r::Point &jp2 = jpts[(jpt + 1 == jpts.size()) ? 0 : jpt + 1]; + if (! (&ipts == &jpts && (&ip1 == &jp2 || &jp1 == &ip2)) && + segments_intersect(ip1, ip2, jp1, jp2)) + return true; + } + } + } + } + return false; +} + #if 0 void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coord_t resolution, const char *path) { + if (wxImage::FindHandler(wxBITMAP_TYPE_PNG) == nullptr) + wxImage::AddHandler(new wxPNGHandler); + unsigned int w = (bbox.max.x - bbox.min.x + resolution - 1) / resolution; unsigned int h = (bbox.max.y - bbox.min.y + resolution - 1) / resolution; wxImage img(w, h); @@ -1450,4 +1546,59 @@ void EdgeGrid::save_png(const EdgeGrid::Grid &grid, const BoundingBox &bbox, coo } #endif /* SLIC3R_GUI */ +// Find all pairs of intersectiong edges from the set of polygons. +std::vector> intersecting_edges(const Polygons &polygons) +{ + double len = 0; + size_t cnt = 0; + BoundingBox bbox; + for (const Polygon &poly : polygons) { + if (poly.points.size() < 2) + continue; + for (size_t i = 0; i < poly.points.size(); ++ i) { + bbox.merge(poly.points[i]); + size_t j = (i == 0) ? (poly.points.size() - 1) : i - 1; + len += poly.points[i].distance_to(poly.points[j]); + ++ cnt; + } + } + len /= double(cnt); + bbox.offset(20); + EdgeGrid::Grid grid; + grid.set_bbox(bbox); + grid.create(polygons, len); + return grid.intersecting_edges(); +} + +// Find all pairs of intersectiong edges from the set of polygons, highlight them in an SVG. +void export_intersections_to_svg(const std::string &filename, const Polygons &polygons) +{ + std::vector> intersections = intersecting_edges(polygons); + BoundingBox bbox = get_extents(polygons); + SVG svg(filename.c_str(), bbox); + svg.draw(union_ex(polygons), "gray", 0.25f); + svg.draw_outline(polygons, "black"); + std::set intersecting_contours; + for (const std::pair &ie : intersections) { + intersecting_contours.insert(ie.first.first); + intersecting_contours.insert(ie.second.first); + } + // Highlight the contours with intersections. + coord_t line_width = coord_t(scale_(0.01)); + for (const Points *ic : intersecting_contours) { + svg.draw_outline(Polygon(*ic), "green"); + svg.draw_outline(Polygon(*ic), "black", line_width); + } + // Paint the intersections. + for (const std::pair &intersecting_edges : intersections) { + auto edge = [](const EdgeGrid::Grid::ContourEdge &e) { + return Line(e.first->at(e.second), + e.first->at((e.second + 1 == e.first->size()) ? 0 : e.second + 1)); + }; + svg.draw(edge(intersecting_edges.first), "red", line_width); + svg.draw(edge(intersecting_edges.second), "red", line_width); + } + svg.Close(); +} + } // namespace Slic3r diff --git a/xs/src/libslic3r/EdgeGrid.hpp b/xs/src/libslic3r/EdgeGrid.hpp index ab1aa4ed0..f99ab30c4 100644 --- a/xs/src/libslic3r/EdgeGrid.hpp +++ b/xs/src/libslic3r/EdgeGrid.hpp @@ -60,6 +60,11 @@ public: // For supports: Contours enclosing the rasterized edges. Polygons contours_simplified(coord_t offset, bool fill_holes) const; + typedef std::pair ContourPoint; + typedef std::pair ContourEdge; + std::vector> intersecting_edges() const; + bool has_intersecting_edges() const; + protected: struct Cell { Cell() : begin(0), end(0) {} @@ -113,6 +118,13 @@ extern void save_png(const Grid &grid, const BoundingBox &bbox, coord_t resoluti #endif /* SLIC3R_GUI */ } // namespace EdgeGrid + +// Find all pairs of intersectiong edges from the set of polygons. +extern std::vector> intersecting_edges(const Polygons &polygons); + +// Find all pairs of intersectiong edges from the set of polygons, highlight them in an SVG. +extern void export_intersections_to_svg(const std::string &filename, const Polygons &polygons); + } // namespace Slic3r #endif /* slic3r_EdgeGrid_hpp_ */ diff --git a/xs/src/libslic3r/SupportMaterial.cpp b/xs/src/libslic3r/SupportMaterial.cpp index 2f0fbbfd6..e31b7a201 100644 --- a/xs/src/libslic3r/SupportMaterial.cpp +++ b/xs/src/libslic3r/SupportMaterial.cpp @@ -458,6 +458,8 @@ Polygons collect_slices_outer(const Layer &layer) class SupportGridPattern { public: + // Achtung! The support_polygons need to be trimmed by trimming_polygons, otherwise + // the selection by island_samples (see the island_samples() method) will not work! SupportGridPattern( // Support islands, to be stretched into a grid. Already trimmed with min(lower_layer_offset, m_gap_xy) const Polygons &support_polygons, @@ -485,6 +487,18 @@ public: bbox.align_to_grid(grid_resolution); m_grid.set_bbox(bbox); m_grid.create(*m_support_polygons, grid_resolution); +#if 0 + if (m_grid.has_intersecting_edges()) { + // EdgeGrid fails to produce valid signed distance function for self-intersecting polygons. + m_support_polygons_rotated = simplify_polygons(*m_support_polygons); + m_support_polygons = &m_support_polygons_rotated; + m_grid.set_bbox(bbox); + m_grid.create(*m_support_polygons, grid_resolution); +// assert(! m_grid.has_intersecting_edges()); + printf("SupportGridPattern: fixing polygons with intersection %s\n", + m_grid.has_intersecting_edges() ? "FAILED" : "SUCCEEDED"); + } +#endif m_grid.calculate_sdf(); // Sample a single point per input support polygon, keep it as a reference to maintain corresponding // polygons if ever these polygons get split into parts by the trimming polygons. @@ -499,9 +513,12 @@ public: { // Generate islands, so each island may be tested for overlap with m_island_samples. assert(std::abs(2 * offset_in_grid) < m_grid.resolution()); - ExPolygons islands = diff_ex( - m_grid.contours_simplified(offset_in_grid, fill_holes), - *m_trimming_polygons, false); +#ifdef SLIC3R_DEBUG + Polygons support_polygons_simplified = m_grid.contours_simplified(offset_in_grid, fill_holes); + ExPolygons islands = diff_ex(support_polygons_simplified, *m_trimming_polygons, false); +#else + ExPolygons islands = diff_ex(m_grid.contours_simplified(offset_in_grid, fill_holes), *m_trimming_polygons, false); +#endif // Extract polygons, which contain some of the m_island_samples. Polygons out; @@ -551,7 +568,10 @@ public: bbox.merge(get_extents(islands)); if (!out.empty()) bbox.merge(get_extents(out)); + if (!support_polygons_simplified.empty()) + bbox.merge(get_extents(support_polygons_simplified)); SVG svg(debug_out_path("extract_support_from_grid_trimmed-%d.svg", iRun).c_str(), bbox); + svg.draw(union_ex(support_polygons_simplified), "gray", 0.25f); svg.draw(islands, "red", 0.5f); svg.draw(union_ex(out), "green", 0.5f); svg.draw(union_ex(*m_support_polygons), "blue", 0.5f); @@ -568,7 +588,121 @@ public: return out; } +#ifdef SLIC3R_DEBUG + void serialize(const std::string &path) + { + FILE *file = ::fopen(path.c_str(), "wb"); + ::fwrite(&m_support_spacing, 8, 1, file); + ::fwrite(&m_support_angle, 8, 1, file); + uint32_t n_polygons = m_support_polygons->size(); + ::fwrite(&n_polygons, 4, 1, file); + for (uint32_t i = 0; i < n_polygons; ++ i) { + const Polygon &poly = (*m_support_polygons)[i]; + uint32_t n_points = poly.size(); + ::fwrite(&n_points, 4, 1, file); + for (uint32_t j = 0; j < n_points; ++ j) { + const Point &pt = poly.points[j]; + ::fwrite(&pt.x, sizeof(coord_t), 1, file); + ::fwrite(&pt.y, sizeof(coord_t), 1, file); + } + } + n_polygons = m_trimming_polygons->size(); + ::fwrite(&n_polygons, 4, 1, file); + for (uint32_t i = 0; i < n_polygons; ++ i) { + const Polygon &poly = (*m_trimming_polygons)[i]; + uint32_t n_points = poly.size(); + ::fwrite(&n_points, 4, 1, file); + for (uint32_t j = 0; j < n_points; ++ j) { + const Point &pt = poly.points[j]; + ::fwrite(&pt.x, sizeof(coord_t), 1, file); + ::fwrite(&pt.y, sizeof(coord_t), 1, file); + } + } + ::fclose(file); + } + + static SupportGridPattern deserialize(const std::string &path, int which = -1) + { + SupportGridPattern out; + out.deserialize_(path, which); + return out; + } + + // Deserialization constructor + bool deserialize_(const std::string &path, int which = -1) + { + FILE *file = ::fopen(path.c_str(), "rb"); + if (file == nullptr) + return false; + + m_support_polygons = &m_support_polygons_deserialized; + m_trimming_polygons = &m_trimming_polygons_deserialized; + + ::fread(&m_support_spacing, 8, 1, file); + ::fread(&m_support_angle, 8, 1, file); + //FIXME + //m_support_spacing *= 0.01 / 2; + uint32_t n_polygons; + ::fread(&n_polygons, 4, 1, file); + m_support_polygons_deserialized.reserve(n_polygons); + int32_t scale = 1; + for (uint32_t i = 0; i < n_polygons; ++ i) { + Polygon poly; + uint32_t n_points; + ::fread(&n_points, 4, 1, file); + poly.points.reserve(n_points); + for (uint32_t j = 0; j < n_points; ++ j) { + coord_t x, y; + ::fread(&x, sizeof(coord_t), 1, file); + ::fread(&y, sizeof(coord_t), 1, file); + poly.points.emplace_back(Point(x * scale, y * scale)); + } + if (which == -1 || which == i) + m_support_polygons_deserialized.emplace_back(std::move(poly)); + printf("Polygon %d, area: %lf\n", i, area(poly.points)); + } + ::fread(&n_polygons, 4, 1, file); + m_trimming_polygons_deserialized.reserve(n_polygons); + for (uint32_t i = 0; i < n_polygons; ++ i) { + Polygon poly; + uint32_t n_points; + ::fread(&n_points, 4, 1, file); + poly.points.reserve(n_points); + for (uint32_t j = 0; j < n_points; ++ j) { + coord_t x, y; + ::fread(&x, sizeof(coord_t), 1, file); + ::fread(&y, sizeof(coord_t), 1, file); + poly.points.emplace_back(Point(x * scale, y * scale)); + } + m_trimming_polygons_deserialized.emplace_back(std::move(poly)); + } + ::fclose(file); + + m_support_polygons_deserialized = simplify_polygons(m_support_polygons_deserialized, false); + //m_support_polygons_deserialized = to_polygons(union_ex(m_support_polygons_deserialized, false)); + + // Create an EdgeGrid, initialize it with projection, initialize signed distance field. + coord_t grid_resolution = coord_t(scale_(m_support_spacing)); + BoundingBox bbox = get_extents(*m_support_polygons); + bbox.offset(20); + bbox.align_to_grid(grid_resolution); + m_grid.set_bbox(bbox); + m_grid.create(*m_support_polygons, grid_resolution); + m_grid.calculate_sdf(); + // Sample a single point per input support polygon, keep it as a reference to maintain corresponding + // polygons if ever these polygons get split into parts by the trimming polygons. + m_island_samples = island_samples(*m_support_polygons); + return true; + } + + const Polygons& support_polygons() const { return *m_support_polygons; } + const Polygons& trimming_polygons() const { return *m_trimming_polygons; } + const EdgeGrid::Grid& grid() const { return m_grid; } + +#endif /* SLIC3R_DEBUG */ + private: + SupportGridPattern() {} SupportGridPattern& operator=(const SupportGridPattern &rhs); #if 0 @@ -639,6 +773,12 @@ private: // Internal sample points of supporting expolygons. These internal points are used to pick regions corresponding // to the initial supporting regions, after these regions werre grown and possibly split to many by the trimming polygons. Points m_island_samples; + +#ifdef SLIC3R_DEBUG + // support for deserialization of m_support_polygons, m_trimming_polygons + Polygons m_support_polygons_deserialized; + Polygons m_trimming_polygons_deserialized; +#endif /* SLIC3R_DEBUG */ }; namespace SupportMaterialInternal { @@ -793,6 +933,30 @@ namespace SupportMaterialInternal { } } +#ifdef SLIC3R_DEBUG +static int Test() +{ +// for (int i = 0; i < 30; ++ i) + { + int i = -1; +// SupportGridPattern grid("d:\\temp\\support-top-contacts-final-run1-layer460-z70.300000-prev.bin", i); +// SupportGridPattern grid("d:\\temp\\support-top-contacts-final-run1-layer460-z70.300000.bin", i); + auto grid = SupportGridPattern::deserialize("d:\\temp\\support-top-contacts-final-run1-layer27-z5.650000.bin", i); + std::vector> intersections = grid.grid().intersecting_edges(); + if (! intersections.empty()) + printf("Intersections between contours!\n"); + Slic3r::export_intersections_to_svg("d:\\temp\\support_polygon_intersections.svg", grid.support_polygons()); + Slic3r::SVG::export_expolygons("d:\\temp\\support_polygons.svg", union_ex(grid.support_polygons(), false)); + Slic3r::SVG::export_expolygons("d:\\temp\\trimming_polygons.svg", union_ex(grid.trimming_polygons(), false)); + Polygons extracted = grid.extract_support(scale_(0.21 / 2), true); + Slic3r::SVG::export_expolygons("d:\\temp\\extracted.svg", union_ex(extracted, false)); + printf("hu!"); + } + return 0; +} +static int run_support_test = Test(); +#endif /* SLIC3R_DEBUG */ + // Generate top contact layers supporting overhangs. // For a soluble interface material synchronize the layer heights with the object, otherwise leave the layer height undefined. // If supports over bed surface only are requested, don't generate contact layers over an object. @@ -1095,6 +1259,8 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ } } + // Achtung! The contact_polygons need to be trimmed by slices_margin_cached, otherwise + // the selection by island_samples (see the SupportGridPattern::island_samples() method) will not work! SupportGridPattern support_grid_pattern( // Support islands, to be stretched into a grid. contact_polygons, @@ -1113,9 +1279,14 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ // Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions. Polygons dense_interface_polygons = diff(overhang_polygons, offset2(lower_layer_polygons, - no_interface_offset * 0.5f, no_interface_offset * (0.6f + 0.5f), SUPPORT_SURFACES_OFFSET_PARAMETERS)); -// offset(lower_layer_polygons, no_interface_offset * 0.6f, SUPPORT_SURFACES_OFFSET_PARAMETERS)); if (! dense_interface_polygons.empty()) { - //FIXME do it for the bridges only? + dense_interface_polygons = + // Achtung! The dense_interface_polygons need to be trimmed by slices_margin_cached, otherwise + // the selection by island_samples (see the SupportGridPattern::island_samples() method) will not work! + diff( + // Regularize the contour. + offset(dense_interface_polygons, no_interface_offset * 0.1f), + slices_margin_cached); SupportGridPattern support_grid_pattern( // Support islands, to be stretched into a grid. dense_interface_polygons, @@ -1125,8 +1296,34 @@ PrintObjectSupportMaterial::MyLayersPtr PrintObjectSupportMaterial::top_contact_ m_object_config->support_material_spacing.value + m_support_material_flow.spacing(), Geometry::deg2rad(m_object_config->support_material_angle.value)); new_layer.polygons = support_grid_pattern.extract_support(m_support_material_flow.scaled_spacing()/2 + 5, false); + #ifdef SLIC3R_DEBUG + { + support_grid_pattern.serialize(debug_out_path("support-top-contacts-final-run%d-layer%d-z%f.bin", iRun, layer_id, layer.print_z)); + + BoundingBox bbox = get_extents(contact_polygons); + bbox.merge(get_extents(new_layer.polygons)); + ::Slic3r::SVG svg(debug_out_path("support-top-contacts-final0-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z)); + svg.draw(union_ex(*new_layer.contact_polygons, false), "gray", 0.5f); + svg.draw(union_ex(contact_polygons, false), "blue", 0.5f); + svg.draw(union_ex(dense_interface_polygons, false), "green", 0.5f); + svg.draw(union_ex(new_layer.polygons, true), "red", 0.5f); + svg.draw_outline(union_ex(new_layer.polygons, true), "black", "black", scale_(0.1f)); + } + #endif /* SLIC3R_DEBUG */ } } + #ifdef SLIC3R_DEBUG + { + BoundingBox bbox = get_extents(contact_polygons); + bbox.merge(get_extents(new_layer.polygons)); + ::Slic3r::SVG svg(debug_out_path("support-top-contacts-final-run%d-layer%d-z%f.svg", iRun, layer_id, layer.print_z)); + svg.draw(union_ex(*new_layer.contact_polygons, false), "gray", 0.5f); + svg.draw(union_ex(contact_polygons, false), "blue", 0.5f); + svg.draw(union_ex(overhang_polygons, false), "green", 0.5f); + svg.draw(union_ex(new_layer.polygons, true), "red", 0.5f); + svg.draw_outline(union_ex(new_layer.polygons, true), "black", "black", scale_(0.1f)); + } + #endif /* SLIC3R_DEBUG */ // Even after the contact layer was expanded into a grid, some of the contact islands may be too tiny to be extruded. // Remove those tiny islands from new_layer.polygons and new_layer.contact_polygons.