diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 89363bd23..605fb8b84 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -289,8 +289,9 @@ cmake_policy(SET CMP0011 NEW) find_package(CGAL REQUIRED) cmake_policy(POP) -add_library(libslic3r_cgal STATIC MeshBoolean.cpp MeshBoolean.hpp TryCatchSignal.hpp - TryCatchSignal.cpp) +add_library(libslic3r_cgal STATIC MeshBoolean.hpp MeshBoolean.cpp + TryCatchSignal.hpp TryCatchSignal.cpp + Triangulation.hpp Triangulation.cpp) target_include_directories(libslic3r_cgal PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) # Reset compile options of libslic3r_cgal. Despite it being linked privately, CGAL options diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp index 02c750698..ce32efd7e 100644 --- a/src/libslic3r/Emboss.cpp +++ b/src/libslic3r/Emboss.cpp @@ -6,6 +6,8 @@ #define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation #include "imgui/imstb_truetype.h" // stbtt_fontinfo +#include // CGAL project + using namespace Slic3r; Emboss::FontItem::FontItem(const std::string &name, const std::string &path) @@ -371,7 +373,10 @@ Emboss::FontList Emboss::get_font_list_by_folder() { } #else -void Emboss::get_font_list() {} +Emboss::FontList Emboss::get_font_list() { + // not implemented + return {}; +} std::optional Emboss::get_font_path(const std::wstring &font_face_name){ // not implemented @@ -545,7 +550,7 @@ indexed_triangle_set Emboss::polygons2model(const Polygons &shape2d, std::make_move_iterator(back_points.end())); // CW order of triangle indices - std::vector shape_triangles = triangulate(shape2d); + std::vector shape_triangles = Triangulation::triangulate(shape2d); result.indices.reserve(shape_triangles.size() * 2 + count_point * 2); // top triangles - change to CCW for (const Vec3i &t : shape_triangles) @@ -575,164 +580,6 @@ indexed_triangle_set Emboss::polygons2model(const Polygons &shape2d, return result; } -#include -#include -#include -Emboss::Indices Emboss::triangulate(const Points &points, const HalfEdges &half_edges) -{ - // IMPROVE use int point insted of float !!! - - // use cgal triangulation - using K = CGAL::Exact_predicates_inexact_constructions_kernel; - using Itag = CGAL::Exact_predicates_tag; - using CDT = CGAL::Constrained_Delaunay_triangulation_2; - using Point = CDT::Point; - - // construct a constrained triangulation - CDT cdt; - std::map map; // for indices - std::vector vertices_handle; // for constriants - vertices_handle.reserve(points.size()); - for (const auto& p: points) { - Point cdt_p(p.x(), p.y()); - auto handl = cdt.insert(cdt_p); - vertices_handle.push_back(handl); - // point index - uint32_t pi = &p - &points.front(); - map[handl] = pi; - } - - // triangle can not contain forbiden edge - for (const std::pair &edge : half_edges) { - const CDT::Vertex_handle& vh1 = vertices_handle[edge.first]; - const CDT::Vertex_handle& vh2 = vertices_handle[edge.second]; - cdt.insert_constraint(vh1, vh2); - } - - auto faces = cdt.finite_face_handles(); - std::vector indices; - indices.reserve(faces.size()); - for (CDT::Face_handle face : faces) { - // point indices - std::array pi; - for (size_t i = 0; i < 3; ++i) - pi[i] = map[face->vertex(i)]; - - // Do not use triangles with opposit edges - if (half_edges.find(std::make_pair(pi[1], pi[0])) != half_edges.end()) continue; - if (half_edges.find(std::make_pair(pi[2], pi[1])) != half_edges.end()) continue; - if (half_edges.find(std::make_pair(pi[0], pi[2])) != half_edges.end()) continue; - - indices.emplace_back(pi[0], pi[1], pi[2]); - } - return indices; -} - -Emboss::Indices Emboss::triangulate(const Polygon &polygon) -{ - const Points &pts = polygon.points; - std::set> edges; - for (uint32_t i = 1; i < pts.size(); ++i) edges.insert({i - 1, i}); - edges.insert({(uint32_t)pts.size() - 1, uint32_t(0)}); - Emboss::Indices indices = triangulate(pts, edges); - remove_outer(indices, edges); - return indices; -} - -Emboss::Indices Emboss::triangulate(const Polygons &polygons) -{ - size_t count = count_points(polygons); - Points points; - points.reserve(count); - for (const Polygon &polygon : polygons) - points.insert(points.end(), polygon.points.begin(), - polygon.points.end()); - - std::set> edges; - uint32_t offset = 0; - for (const Polygon& polygon : polygons) { - const Points &pts = polygon.points; - for (uint32_t i = 1; i < pts.size(); ++i) { - uint32_t i2 = i + offset; - edges.insert({i2 - 1, i2}); - } - uint32_t size = static_cast(pts.size()); - // add connection from first to last point - edges.insert({offset + size - 1, offset}); - offset += size; - } - Emboss::Indices indices = triangulate(points, edges); - remove_outer(indices, edges); - return indices; -} - -void Emboss::remove_outer(Indices &indices, const HalfEdges &half_edges) { - uint32_t no_triangle = indices.size(); - std::map edge2triangle; - // triangles with all edges out of half_edge, candidate to remove - std::vector triangles_to_check; - triangles_to_check.reserve(indices.size()/3); - for (const auto& t : indices) { - uint32_t index = &t - &indices.front(); - bool is_border = false; - for (size_t j = 0; j < 3; ++j) { - size_t j2 = (j == 0) ? 2 : (j - 1); - HalfEdge he(t[j2], t[j]); - if (half_edges.find(he) != half_edges.end()) - is_border = true; - else - edge2triangle[he] = index; - } - if (!is_border) { - triangles_to_check.push_back(index); - } - } - - std::set remove; - std::queue insert; - for (uint32_t index : triangles_to_check) { - auto it = remove.find(index); - if (it != remove.end()) continue; // already removed - - bool is_edge = false; - const Vec3i &t = indices[index]; - for (size_t j = 0; j < 3; ++j) { - size_t j2 = (j == 0) ? 2 : (j - 1); - // opposit - HalfEdge he(t[j], t[j2]); - if (edge2triangle.find(he) == edge2triangle.end()) is_edge = true; - } - - if (!is_edge) continue; // correct - - insert.push(index); - while (!insert.empty()) { - uint32_t i = insert.front(); - insert.pop(); - if (remove.find(i) != remove.end()) continue; - remove.insert(i); - - for (size_t j = 0; j < 3; ++j) { - size_t j2 = (j == 0) ? 2 : (j - 1); - // opposit - HalfEdge he(t[j], t[j2]); - auto it = edge2triangle.find(he); - if (it == edge2triangle.end()) continue; // edge - insert.push(it->second); - } - } - } - - // remove indices - std::vector rem(remove.begin(), remove.end()); - std::sort(rem.begin(), rem.end()); - uint32_t offset = 0; - for (uint32_t i : rem) { - indices.erase(indices.begin() + (i - offset)); - ++offset; - } -} - std::pair Emboss::ProjectZ::project(const Point &p) const { Vec3f front(p.x(),p.y(),0.f); diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp index 1a38b537a..9082ccce0 100644 --- a/src/libslic3r/Emboss.hpp +++ b/src/libslic3r/Emboss.hpp @@ -146,28 +146,6 @@ public: /// Projected shape into space static indexed_triangle_set polygons2model(const Polygons &shape2d, const IProject& projection); - // define oriented connection of 2 vertices(defined by its index) - using HalfEdge = std::pair; - using HalfEdges = std::set; - using Indices = std::vector; - /// - /// Connect points by triangulation to create filled surface by triangle indices - /// - /// Points to connect - /// Constraint for edges, pair is from point(first) to point(second) - /// Triangles - static Indices triangulate(const Points &points, const HalfEdges &half_edges); - static Indices triangulate(const Polygon &polygon); - static Indices triangulate(const Polygons &polygons); - - /// - /// Filter out triagles without both side edge or inside half edges - /// Main purpose: Filter out triangles which lay outside of ExPolygon given to triangulation - /// - /// Triangles - /// Only outer edges - static void remove_outer(Indices &indices, const HalfEdges &half_edges); - class ProjectZ : public IProject { public: diff --git a/src/libslic3r/Triangulation.cpp b/src/libslic3r/Triangulation.cpp new file mode 100644 index 000000000..a953531cc --- /dev/null +++ b/src/libslic3r/Triangulation.cpp @@ -0,0 +1,165 @@ +#include "Triangulation.hpp" + +#include +#include +#include + +using namespace Slic3r; + +Triangulation::Indices Triangulation::triangulate(const Points & points, + const HalfEdges &half_edges) +{ + // IMPROVE use int point insted of float !!! + + // use cgal triangulation + using K = CGAL::Exact_predicates_inexact_constructions_kernel; + using Itag = CGAL::Exact_predicates_tag; + using CDT = + CGAL::Constrained_Delaunay_triangulation_2; + using Point = CDT::Point; + + // construct a constrained triangulation + CDT cdt; + std::map map; // for indices + std::vector vertices_handle; // for constriants + vertices_handle.reserve(points.size()); + for (const auto &p : points) { + Point cdt_p(p.x(), p.y()); + auto handl = cdt.insert(cdt_p); + vertices_handle.push_back(handl); + // point index + uint32_t pi = &p - &points.front(); + map[handl] = pi; + } + + // triangle can not contain forbiden edge + for (const std::pair &edge : half_edges) { + const CDT::Vertex_handle &vh1 = vertices_handle[edge.first]; + const CDT::Vertex_handle &vh2 = vertices_handle[edge.second]; + cdt.insert_constraint(vh1, vh2); + } + + auto faces = cdt.finite_face_handles(); + std::vector indices; + indices.reserve(faces.size()); + for (CDT::Face_handle face : faces) { + // point indices + std::array pi; + for (size_t i = 0; i < 3; ++i) pi[i] = map[face->vertex(i)]; + + // Do not use triangles with opposit edges + if (half_edges.find(std::make_pair(pi[1], pi[0])) != half_edges.end()) + continue; + if (half_edges.find(std::make_pair(pi[2], pi[1])) != half_edges.end()) + continue; + if (half_edges.find(std::make_pair(pi[0], pi[2])) != half_edges.end()) + continue; + + indices.emplace_back(pi[0], pi[1], pi[2]); + } + return indices; +} + +Triangulation::Indices Triangulation::triangulate(const Polygon &polygon) +{ + const Points & pts = polygon.points; + std::set> edges; + for (uint32_t i = 1; i < pts.size(); ++i) edges.insert({i - 1, i}); + edges.insert({(uint32_t) pts.size() - 1, uint32_t(0)}); + Triangulation::Indices indices = triangulate(pts, edges); + remove_outer(indices, edges); + return indices; +} + +Triangulation::Indices Triangulation::triangulate(const Polygons &polygons) +{ + size_t count = count_points(polygons); + Points points; + points.reserve(count); + for (const Polygon &polygon : polygons) + points.insert(points.end(), polygon.points.begin(), + polygon.points.end()); + + std::set> edges; + uint32_t offset = 0; + for (const Polygon &polygon : polygons) { + const Points &pts = polygon.points; + for (uint32_t i = 1; i < pts.size(); ++i) { + uint32_t i2 = i + offset; + edges.insert({i2 - 1, i2}); + } + uint32_t size = static_cast(pts.size()); + // add connection from first to last point + edges.insert({offset + size - 1, offset}); + offset += size; + } + Triangulation::Indices indices = triangulate(points, edges); + remove_outer(indices, edges); + return indices; +} + +void Triangulation::remove_outer(Indices &indices, const HalfEdges &half_edges) +{ + uint32_t no_triangle = indices.size(); + std::map edge2triangle; + // triangles with all edges out of half_edge, candidate to remove + std::vector triangles_to_check; + triangles_to_check.reserve(indices.size() / 3); + for (const auto &t : indices) { + uint32_t index = &t - &indices.front(); + bool is_border = false; + for (size_t j = 0; j < 3; ++j) { + size_t j2 = (j == 0) ? 2 : (j - 1); + HalfEdge he(t[j2], t[j]); + if (half_edges.find(he) != half_edges.end()) + is_border = true; + else + edge2triangle[he] = index; + } + if (!is_border) { triangles_to_check.push_back(index); } + } + + std::set remove; + std::queue insert; + for (uint32_t index : triangles_to_check) { + auto it = remove.find(index); + if (it != remove.end()) continue; // already removed + + bool is_edge = false; + const Vec3i &t = indices[index]; + for (size_t j = 0; j < 3; ++j) { + size_t j2 = (j == 0) ? 2 : (j - 1); + // opposit + HalfEdge he(t[j], t[j2]); + if (edge2triangle.find(he) == edge2triangle.end()) is_edge = true; + } + + if (!is_edge) continue; // correct + + insert.push(index); + while (!insert.empty()) { + uint32_t i = insert.front(); + insert.pop(); + if (remove.find(i) != remove.end()) continue; + remove.insert(i); + + for (size_t j = 0; j < 3; ++j) { + size_t j2 = (j == 0) ? 2 : (j - 1); + // opposit + HalfEdge he(t[j], t[j2]); + auto it = edge2triangle.find(he); + if (it == edge2triangle.end()) continue; // edge + insert.push(it->second); + } + } + } + + // remove indices + std::vector rem(remove.begin(), remove.end()); + std::sort(rem.begin(), rem.end()); + uint32_t offset = 0; + for (uint32_t i : rem) { + indices.erase(indices.begin() + (i - offset)); + ++offset; + } +} \ No newline at end of file diff --git a/src/libslic3r/Triangulation.hpp b/src/libslic3r/Triangulation.hpp new file mode 100644 index 000000000..f8907206a --- /dev/null +++ b/src/libslic3r/Triangulation.hpp @@ -0,0 +1,46 @@ +#ifndef libslic3r_MeshBoolean_hpp_ +#define libslic3r_MeshBoolean_hpp_ + +#include +#include +#include +#include +#include + +namespace Slic3r { + +class Triangulation +{ +public: + Triangulation() = delete; + + // define oriented connection of 2 vertices(defined by its index) + using HalfEdge = std::pair; + using HalfEdges = std::set; + using Indices = std::vector; + + /// + /// Connect points by triangulation to create filled surface by triangle + /// indices + /// + /// Points to connect + /// Constraint for edges, pair is from point(first) to + /// point(second) Triangles + static Indices triangulate(const Points & points, + const HalfEdges &half_edges); + static Indices triangulate(const Polygon &polygon); + static Indices triangulate(const Polygons &polygons); + + /// + /// Filter out triagles without both side edge or inside half edges + /// Main purpose: Filter out triangles which lay outside of ExPolygon + /// given to triangulation + /// + /// Triangles + /// Only outer edges + static void remove_outer(Indices &indices, const HalfEdges &half_edges); + +}; + +} // namespace Slic3r +#endif // libslic3r_MeshBoolean_hpp_ \ No newline at end of file diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index b0e6d7427..91c36c4ed 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -335,9 +335,11 @@ void GLGizmoEmboss::draw_add_button() { Emboss::FontList font_list; font_list.reserve(input_files.size()); - for (auto &input_file : input_files) { - std::string name = input_file.AfterLast('\\').c_str(); - std::string path = input_file.c_str(); + for (auto &input_file : input_files) { + std::string path = std::string(input_file.c_str()); + size_t pos = path.find_last_of('\\'); + size_t pos2 = path.find_last_of('.'); + std::string name = path.substr(pos + 1, pos2 - pos-1); font_list.emplace_back(name, path); } // set last added font as active