diff --git a/src/libslic3r/CutSurface.cpp b/src/libslic3r/CutSurface.cpp index b86aa102e..0eb76f6e2 100644 --- a/src/libslic3r/CutSurface.cpp +++ b/src/libslic3r/CutSurface.cpp @@ -68,6 +68,9 @@ using FI = CGAL::SM_Face_index; using P3 = CGAL::Epick::Point_3; +using Project = Emboss::IProjection; +using Project3f = Emboss::IProject3f; + /// /// IntersectingElement /// @@ -154,7 +157,32 @@ struct IntersectingElement /// direction void set_skip_for_outward_projection(std::vector &skip_indicies, const indexed_triangle_set &its, - const Emboss::IProject3f &projection); + const Project3f &projection); + +/// +/// Set true for indicies outward and almost parallel together. +/// Note: internally calculate normals +/// +/// Flag to convert triangle to cgal +/// model +/// Direction to measure angle +/// Maximal allowed angle between opposit normal and projection direction [in DEG] +void set_skip_by_angle(std::vector &skip_indicies, + const indexed_triangle_set &its, + const Project3f &projection, + double max_angle = 89.); + +/// +/// Set true for indices out of area of interest +/// +/// Flag to convert triangle to cgal +/// model +/// Convert 2d point to pair of 3d points +/// after projection define AOI +void set_skip_for_out_of_aoi(std::vector &skip_indicies, + const indexed_triangle_set &its, + const Project &projection, + const ExPolygons &shapes); /// /// Convert triangle mesh model to CGAL Surface_mesh @@ -162,11 +190,11 @@ void set_skip_for_outward_projection(std::vector &skip_indicies, /// Add property map for source face index /// /// Model -/// Projection to filtrate out oposite triangles +/// Flags that triangle should be skiped /// CGAL mesh - half edge mesh -CutMesh to_cgal(const indexed_triangle_set &its, const ::Emboss::IProject3f & projection); +CutMesh to_cgal(const indexed_triangle_set &its, + const std::vector &skip_indicies); -using Project = Emboss::IProjection; /// /// Covert 2d shape (e.g. Glyph) to CGAL model /// @@ -293,7 +321,7 @@ void set_face_type(FaceTypeMap &face_type_map, void set_almost_parallel_type(FaceTypeMap &face_type_map, const CutMesh &mesh, - const Emboss::IProject3f &projection); + const Project3f &projection); /// /// Check if face is almost parallel @@ -306,7 +334,7 @@ void set_almost_parallel_type(FaceTypeMap &face_type_map, /// True when Triangle is almost parallel with direction of projection bool is_almost_parallel(FI fi, const CutMesh &mesh, - const Emboss::IProject3f &projection, + const Project3f &projection, float threshold = static_cast(std::cos(80 * M_PI / 180))); /// @@ -424,7 +452,7 @@ bool has_minimal_contour_points(const std::vector &outlines, /// True when triangle normal is toward projection otherwise FALSE bool is_toward_projection(FI fi, const CutMesh &mesh, - const Emboss::IProject3f &projection); + const Project3f &projection); /// /// Check orientation of triangle defined by vertices a, b, c in CCW order @@ -437,7 +465,7 @@ bool is_toward_projection(FI fi, bool is_toward_projection(const Vec3f &a, const Vec3f &b, const Vec3f &c, - const Emboss::IProject3f &projection); + const Project3f &projection); /// /// Check orientation of triangle /// @@ -447,7 +475,7 @@ bool is_toward_projection(const Vec3f &a, /// True when triangle normal is toward projection otherwise FALSE bool is_toward_projection(const stl_triangle_vertex_indices &t, const std::vector &vertices, - const Emboss::IProject3f &projection); + const Project3f &projection); /// /// Copy triangles from CGAL mesh into index triangle set @@ -502,7 +530,16 @@ SurfaceCut Slic3r::cut_surface(const indexed_triangle_set &model, const ExPolygons &shapes, const Emboss::IProjection &projection) { - priv::CutMesh cgal_model = priv::to_cgal(model, projection); + if (model.empty() || shapes.empty() ) return {}; + + std::vector skip_indicies(model.indices.size(), {false}); + // cut out of bounding box triangles + priv::set_skip_for_out_of_aoi(skip_indicies, model, projection, shapes); + // cut out opposit triangles + //priv::set_skip_for_outward_projection(skip_indicies, model, projection); + priv::set_skip_by_angle(skip_indicies, model, projection); + + priv::CutMesh cgal_model = priv::to_cgal(model, skip_indicies); #ifdef DEBUG_OUTPUT_DIR CGAL::IO::write_OFF(DEBUG_OUTPUT_DIR + "model.off", cgal_model); // only debug @@ -548,10 +585,11 @@ SurfaceCut Slic3r::cut_surface(const indexed_triangle_set &model, priv::store(cgal_model, face_type_map, DEBUG_OUTPUT_DIR + "constrained.off"); // only debug #endif // DEBUG_OUTPUT_DIR - priv::set_almost_parallel_type(face_type_map, cgal_model, projection); -#ifdef DEBUG_OUTPUT_DIR - priv::store(cgal_model, face_type_map, DEBUG_OUTPUT_DIR + "constrainedWithAlmostParallel.off"); // only debug -#endif // DEBUG_OUTPUT_DIR + // It is neccesary when almost parallel face are contained in projection + // priv::set_almost_parallel_type(face_type_map, cgal_model, projection); +//#ifdef DEBUG_OUTPUT_DIR +// priv::store(cgal_model, face_type_map, DEBUG_OUTPUT_DIR + "constrainedWithAlmostParallel.off"); // only debug +//#endif // DEBUG_OUTPUT_DIR priv::flood_fill_inner(cgal_model, face_type_map); // Seed fill the other faces inside the region. @@ -588,11 +626,12 @@ SurfaceCut Slic3r::cut_surface(const indexed_triangle_set &model, priv::store(result, DEBUG_OUTPUT_DIR + "cuts/"); // only debug #endif // DEBUG_OUTPUT_DIR + // TODO: fill skipped source triangles to surface of cut return merge(std::move(result)); } indexed_triangle_set Slic3r::cuts2model(const SurfaceCuts &cuts, - const Emboss::IProject3f &projection) + const priv::Project3f &projection) { indexed_triangle_set result; size_t count_vertices = 0; @@ -673,7 +712,7 @@ indexed_triangle_set Slic3r::cuts2model(const SurfaceCuts &cuts, } indexed_triangle_set Slic3r::cut2model(const SurfaceCut &cut, - const Emboss::IProject3f &projection) + const priv::Project3f &projection) { assert(!cut.empty()); size_t count_vertices = cut.vertices.size() * 2; @@ -737,7 +776,7 @@ indexed_triangle_set Slic3r::cut2model(const SurfaceCut &cut, bool priv::is_toward_projection(FI fi, const CutMesh &mesh, - const Emboss::IProject3f &projection) + const Project3f &projection) { HI hi = mesh.halfedge(fi); @@ -754,7 +793,7 @@ bool priv::is_toward_projection(FI fi, bool priv::is_toward_projection(const stl_triangle_vertex_indices &t, const std::vector &vertices, - const Emboss::IProject3f &projection) + const Project3f &projection) { return is_toward_projection(vertices[t[0]], vertices[t[1]], vertices[t[2]], projection); @@ -763,7 +802,7 @@ bool priv::is_toward_projection(const stl_triangle_vertex_indices &t, bool priv::is_toward_projection(const Vec3f &a, const Vec3f &b, const Vec3f &c, - const Emboss::IProject3f &projection) + const Project3f &projection) { P3 cgal_a(a.x(), a.y(), a.z()); P3 cgal_b(b.x(), b.y(), b.z()); @@ -779,8 +818,9 @@ bool priv::is_toward_projection(const Vec3f &a, void priv::set_skip_for_outward_projection(std::vector &skip_indicies, const indexed_triangle_set &its, - const Emboss::IProject3f &projection) -{ + const Project3f &projection) +{ + assert(skip_indicies.size() == its.indices.size()); for (const auto &t : its.indices) { size_t index = &t - &its.indices.front(); if (skip_indicies[index]) continue; @@ -789,14 +829,129 @@ void priv::set_skip_for_outward_projection(std::vector &skip_indicies, } } -priv::CutMesh priv::to_cgal(const indexed_triangle_set &its, - const Emboss::IProject3f &projection) +void priv::set_skip_by_angle(std::vector &skip_indicies, + const indexed_triangle_set &its, + const Project3f &projection, + double max_angle) { - if (its.empty()) return {}; - std::vector skip_indicies(its.indices.size(), {false}); - // cut out opposit triangles - set_skip_for_outward_projection(skip_indicies, its, projection); + float threshold = static_cast(cos(max_angle / 180. * M_PI)); + assert(skip_indicies.size() == its.indices.size()); + for (const stl_triangle_vertex_indices& face : its.indices) { + size_t index = &face - &its.indices.front(); + if (skip_indicies[index]) continue; + Vec3f n = its_face_normal(its, face); + const Vec3f v = its.vertices[face[0]]; + // Improve: For Orthogonal Projection it is same for each vertex + Vec3f projected = projection.project(v); + Vec3f project_dir = projected - v; + project_dir.normalize(); + float cos_alpha = project_dir.dot(n); + if (cos_alpha > threshold) continue; + skip_indicies[index] = true; + } +} +void priv::set_skip_for_out_of_aoi(std::vector &skip_indicies, + const indexed_triangle_set &its, + const Project &projection, + const ExPolygons &shapes) +{ + assert(skip_indicies.size() == its.indices.size()); + BoundingBox shapes_bb = get_extents(shapes); + + // 1`*----* 2` + // / 2 /| + // 1 *----* | + // | | * 3` + // | |/ + // 0 *----* 3 + ////////////////// + std::array, 4> bb; + int index = 0; + for (Point v : + {shapes_bb.min, Point{shapes_bb.min.x(), shapes_bb.max.y()}, + shapes_bb.max, Point{shapes_bb.max.x(), shapes_bb.min.y()}}) + bb[index++] = projection.create_front_back(v); + + // define planes to test + // 0 .. under + // 1 .. left + // 2 .. above + // 3 .. right + size_t prev_i = 3; + std::array, 4> point_normals; + for (size_t i = 0; i < 4; i++) { + const Vec3f &p1 = bb[i].first; + const Vec3f &p2 = bb[i].second; + const Vec3f &p3 = bb[prev_i].first; + prev_i = i; + + Vec3d v1 = (p2 - p1).cast(); + v1.normalize(); + Vec3d v2 = (p3 - p1).cast(); + v2.normalize(); + + Vec3d normal = v2.cross(v1); + normal.normalize(); + + point_normals[i] = {p1.cast(), normal}; + } + // same meaning as point normal + std::array, 4> is_on_sides; + for (size_t side = 0; side < 4; side++) + is_on_sides[side] = std::vector(its.vertices.size(), {false}); + + auto is_out_of = [&point_normals](int side, const Vec3d &v) -> bool { + const auto &[p, n] = point_normals[side]; + double signed_distance = (v - p).dot(n); + return signed_distance > 1e-5; + }; + + // inspect all vertices when it is out of bounding box + for (size_t i = 0; i < its.vertices.size(); i++) { + Vec3d v = its.vertices[i].cast(); + // under + above + for (int side : {0, 2}) { + if (is_out_of(side, v)) { + is_on_sides[side][i] = true; + break; + } + } + // left + right + for (int side : {1, 3}) { + if (is_out_of(side, v)) { + is_on_sides[side][i] = true; + break; + } + } + } + + auto is_all_on_one_side = [is_on_sides](const Vec3i &t) -> bool { + for (size_t side = 0; side < 4; side++) { + bool result = true; + for (auto vi : t){ + if (!is_on_sides[side][vi]) { + result = false; + break; + } + } + if (result) return true; + } + return false; + }; + + // inspect all triangles, when it is out of bounding box + for (size_t i = 0; i < its.indices.size(); i++) { + if (skip_indicies[i]) continue; + const auto& t = its.indices[i]; + if (is_all_on_one_side(t)) + skip_indicies[i] = true; + } +} + +priv::CutMesh priv::to_cgal(const indexed_triangle_set &its, + const std::vector &skip_indicies) +{ const std::vector &vertices = its.vertices; const std::vector &indices = its.indices; @@ -1022,7 +1177,7 @@ void priv::set_face_type(FaceTypeMap &face_type_map, void priv::set_almost_parallel_type(FaceTypeMap &face_type_map, const CutMesh &mesh, - const Emboss::IProject3f &projection) + const Project3f &projection) { for (const FI &fi : mesh.faces()) { auto &type = face_type_map[fi]; @@ -1040,7 +1195,7 @@ void priv::set_almost_parallel_type(FaceTypeMap &face_type_map, } } -bool priv::is_almost_parallel(FI fi, const CutMesh &mesh, const Emboss::IProject3f &projection, float threshold) +bool priv::is_almost_parallel(FI fi, const CutMesh &mesh, const Project3f &projection, float threshold) { HI hi = mesh.halfedge(fi); std::array vis = {