diff --git a/sandboxes/CMakeLists.txt b/sandboxes/CMakeLists.txt index 81ea94a291..c9179cd5a9 100644 --- a/sandboxes/CMakeLists.txt +++ b/sandboxes/CMakeLists.txt @@ -1,6 +1,7 @@ #add_subdirectory(slasupporttree) #add_subdirectory(openvdb) # add_subdirectory(meshboolean) -add_subdirectory(its_neighbor_index) +#add_subdirectory(its_neighbor_index) # add_subdirectory(opencsg) -#add_subdirectory(aabb-evaluation) \ No newline at end of file +#add_subdirectory(aabb-evaluation) +add_subdirectory(clipper_experiments) diff --git a/sandboxes/clipper_experiments/CMakeLists.txt b/sandboxes/clipper_experiments/CMakeLists.txt new file mode 100644 index 0000000000..dc5d802679 --- /dev/null +++ b/sandboxes/clipper_experiments/CMakeLists.txt @@ -0,0 +1,12 @@ +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_executable(clipper_experiments ClipperExperiments.cpp) +#set_target_properties(clipper_experiments PROPERTY CXX_STANDARD 20) +#set_target_properties(clipper_experiments PROPERTY CXX_STANDARD_REQUIRED ON) + +target_link_libraries(clipper_experiments libslic3r) + +if (WIN32) + prusaslicer_copy_dlls(clipper_experiments) +endif() diff --git a/sandboxes/clipper_experiments/ClipperExperiments.cpp b/sandboxes/clipper_experiments/ClipperExperiments.cpp new file mode 100644 index 0000000000..e81a525076 --- /dev/null +++ b/sandboxes/clipper_experiments/ClipperExperiments.cpp @@ -0,0 +1,161 @@ +#include + +#include +#include + +using namespace Slic3r; + +inline ExPolygon rectangle(double w, double h) +{ + return {scaled(Vec2d(0, 0)), scaled(Vec2d(w, 0)), scaled(Vec2d(w, h)), + scaled(Vec2d(0, h))}; +} + +inline ExPolygon rectangle(double a) { return rectangle(a, a); } +inline ExPolygon &&translate(ExPolygon &&p, double x, double y) +{ + p.translate(scaled(Vec2d(x, y))); + return std::move(p); +} + +constexpr const char * FILLTYPE_STR[] = { + "pftEvenOdd", "pftNonZero", "pftPositive", "pftNegative" +}; + +constexpr const char * PTYPE_STR[] = { "ptSubject", "ptClip" }; + +constexpr const char * CLIPTYPE_STR[] = { "ctIntersection", "ctUnion", "ctDifference", "ctXor" }; + +constexpr const char * JOINTYPE_STR[] = { "jtSquare", "jtRound", "jtMiter"}; + +void eval_clipping(const char *prefix, const ExPolygon &a, const ExPolygon &b) +{ + BoundingBox bb{a}; bb.merge(BoundingBox{b}); + + SVG svgin(std::string(prefix) + "_input_clipping.svg", bb); + svgin.draw_outline(a); + svgin.draw_outline(b); + svgin.Close(); + + for (auto bPolyType : {ClipperLib::ptSubject, ClipperLib::ptClip}) + for (auto cliptype : {ClipperLib::ctIntersection, ClipperLib::ctUnion, ClipperLib::ctDifference, ClipperLib::ctXor}) + for (auto filltype : {ClipperLib::pftEvenOdd, ClipperLib::pftNegative, ClipperLib::pftNonZero, ClipperLib::pftPositive}) { + ClipperLib::Clipper clipper; + + clipper.AddPath(a.contour.points, ClipperLib::ptSubject, true); + for (auto &h : a.holes) + clipper.AddPath(h.points, ClipperLib::ptSubject, true); + + clipper.AddPath(b.contour.points, bPolyType, true); + for (auto &h : b.holes) + clipper.AddPath(h.points, bPolyType, true); + + ClipperLib::PolyTree tree; + clipper.Execute(cliptype, tree, filltype); + SVG svg{std::string{prefix} + "_clipping_" + CLIPTYPE_STR[cliptype] + "_" + PTYPE_STR[bPolyType] + "_" + FILLTYPE_STR[filltype] + ".svg", bb}; + svg.draw(PolyTreeToExPolygons(std::move(tree)), "green"); + } +} + +void generate_clipping_report(std::fstream &report, const char * prefix) +{ + report << "Clipping input is: ![img](" << prefix << "_input_clipping.svg)\nA to the left, B to the right\n" << std::endl; + for (auto ptype : PTYPE_STR) { + report << "When B is of type " << ptype << "\n\n"; + + report << "|Operation"; + for (auto filltype : FILLTYPE_STR) report << "|" << filltype; + report << "|\n"; + + for (size_t i = 0; i < std::size(FILLTYPE_STR) + 1; ++i) + report << "|----"; + report << "|\n"; + + for (auto cliptype : CLIPTYPE_STR) { + report << "|" << cliptype; + for (auto filltype : FILLTYPE_STR) + report << "|![img](" << prefix << "_clipping_" << cliptype << "_" << ptype << "_" << filltype << ".svg)"; + report << "|\n"; + } + + report << std::endl; + } +} + +void eval_offsetting(const char *prefix, const ExPolygons &polys) +{ + auto bb = get_extents(polys); + + SVG svgin(std::string(prefix) + "_input_offsetting.svg", bb); + svgin.draw_outline(polys); + svgin.Close(); + + for (auto delta : {-1., 0., 1.}) + for (auto jointype : {ClipperLib::jtMiter, ClipperLib::jtRound, ClipperLib::jtSquare}) { + ClipperLib::ClipperOffset offset; + + for (const ExPolygon &p : polys) { + offset.AddPath(p.contour.points, jointype, ClipperLib::etClosedPolygon); + for (auto &h : p.holes) + offset.AddPath(h.points, jointype, ClipperLib::etClosedPolygon); + } + + ClipperLib::PolyTree tree; + offset.Execute(tree, scaled(delta)); + + SVG svg{std::string{prefix} + "_offsetting_delta_" + std::to_string(delta) + "_" + JOINTYPE_STR[jointype] + ".svg", bb}; + svg.draw(PolyTreeToExPolygons(std::move(tree)), "green"); + } +} + +void generate_offsetting_report(std::fstream &report, const char * prefix) +{ + report << "Offsetting input is: ![img](" << prefix << "_input_offsetting.svg)" << std::endl; + + report << "|Delta"; + for (auto jointype : JOINTYPE_STR) report << "|" << jointype; + report << "|\n"; + + for (size_t i = 0; i < std::size(JOINTYPE_STR) + 1; ++i) + report << "|----"; + report << "|\n"; + + for (auto delta : {-1., 0., 1.}) { + report << "|" << delta; + for (auto jtype : JOINTYPE_STR) + report << "|![img](" << prefix << "_offsetting_delta_" << std::to_string(delta) << "_" << jtype << ".svg)"; + + report << "|\n"; + } + + report << std::endl; +} + +int main() +{ + auto a = rectangle(10.), b = translate(rectangle(10.), 5., 5.); + std::fstream report{"report.md", std::fstream::out}; + + eval_offsetting("two_squares_implicit_union", {a, b}); + generate_offsetting_report(report, "two_squares_implicit_union"); + + ExPolygons ab = union_ex({a, b}); + eval_offsetting("two_squares_explicit_union", ab); + generate_offsetting_report(report, "two_squares_explicit_union"); + + eval_clipping("two_squares", a, b); + + generate_clipping_report(report, "two_squares"); + report << "\n" << std::endl; + + a.holes.emplace_back(translate(rectangle(5), 2.5, 2.5)); + std::reverse(a.holes.front().begin(), a.holes.front().end()); + + b.holes.emplace_back(translate(rectangle(5.), 7.5, 7.5)); + std::reverse(b.holes.front().begin(), b.holes.front().end()); + + eval_clipping("two_squares_with_holes", a, b); + generate_clipping_report(report, "two_squares_with_holes"); + + return 0; +} diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 28fb093130..ea4ae1cdf5 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -61,7 +61,7 @@ namespace ClipperUtils { Points SinglePathProvider::s_end; } -static ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree &&polytree) +ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree &&polytree) { struct Inner { static void PolyTreeToExPolygonsRecursive(ClipperLib::PolyNode &&polynode, ExPolygons *expolygons) diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index bbd91c0fd5..4bc602b0e3 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -546,6 +546,8 @@ void traverse_pt(const ClipperLib::PolyNodes &nodes, ExOrJustPolygons *retval) }); } +ExPolygons PolyTreeToExPolygons(ClipperLib::PolyTree &&polytree); + /* OTHER */ Slic3r::Polygons simplify_polygons(const Slic3r::Polygons &subject, bool preserve_collinear = false);