diff --git a/src/libslic3r/BoundingBox.hpp b/src/libslic3r/BoundingBox.hpp index 6b06f8bab..1056c134d 100644 --- a/src/libslic3r/BoundingBox.hpp +++ b/src/libslic3r/BoundingBox.hpp @@ -53,7 +53,7 @@ public: PointClass size() const; double radius() const; void translate(coordf_t x, coordf_t y) { assert(this->defined); PointClass v(x, y); this->min += v; this->max += v; } - void translate(const Vec2d &v) { this->min += v; this->max += v; } + void translate(const PointClass &v) { this->min += v; this->max += v; } void offset(coordf_t delta); BoundingBoxBase inflated(coordf_t delta) const throw() { BoundingBoxBase out(*this); out.offset(delta); return out; } PointClass center() const; diff --git a/src/libslic3r/ClosestPoint.hpp b/src/libslic3r/ClosestPoint.hpp index f12554809..43512b0ce 100644 --- a/src/libslic3r/ClosestPoint.hpp +++ b/src/libslic3r/ClosestPoint.hpp @@ -2,6 +2,7 @@ #define slic3r_ClosestPoint_hpp_ #include +#include namespace Slic3r { @@ -11,51 +12,9 @@ namespace Slic3r { /// Seach for closest index to this point /// Search inside of thoose points /// Index of closest point from sorted_pts -template size_t find_closest(const P &p, const std::vector

&pts); - -///

-/// Use a plane sweep algorithm to find closest point in sorted points -/// https://www.cs.mcgill.ca/~cs251/ClosestPair/ClosestPairPS.html -/// -/// Seach for closest index to this point -/// Sorted points by X coordinate -/// Index of closest point from sorted_pts template -size_t find_closest_in_sorted(const P &p, const std::vector

&sorted_pts); - -///

-/// Use a plane sweep algorithm to find closest point from pts in sorted_pts -/// https://www.cs.mcgill.ca/~cs251/ClosestPair/ClosestPairPS.html -/// -/// Seach for closest index to thoose points -/// Sorted points by X coordinate -/// Indices to pts(first) and sorted_pts(second) -template -std::pair find_closest_in_sorted( - const std::vector

&pts, const std::vector

&sorted_pts); - -namespace closestPoint { -///

-/// Function used to sort points before searching for closest -/// -/// First point -/// Second Point -/// True when, p1.x < p2.x -template bool sort_fnc(const P &p1, const P &p2){ return p1.x() < p2.x(); } - -/// Function used to find upper bound in sorted points. -template bool upper_fnc(V value, const P &p){ return value < p.x(); } -/// Function used to find lower bound in sorted points. -template bool lower_fnc(const P &p, V value){ return value > p.x(); } -/// Calc manhatn size of point. Mainly to explain meaning -template uint32_t manhattan_size(const P &p1, const P &p2) -{ return std::abs(p1.x()-p2.x()) + abs(p1.y()-p2.y()); } -} // namespace closestPoint -} // namespace Slic3r - -template -size_t Slic3r::find_closest(const P &p, const std::vector

&pts) -{ +// /*SFINAE*/ typename std::enable_if>::type * = nullptr > +size_t find_closest(const P &p, const std::vector

&pts){ // check input if (pts.empty()) return std::numeric_limits::max(); if (pts.size() == 1) return 0; @@ -79,6 +38,94 @@ size_t Slic3r::find_closest(const P &p, const std::vector

&pts) return pts_ord[closest_index].ord; } +///

+/// Use a plane sweep algorithm to find closest point in sorted points +/// https://www.cs.mcgill.ca/~cs251/ClosestPair/ClosestPairPS.html +/// +/// Seach for closest index to this point +/// Sorted points by X coordinate +/// Index of closest point from sorted_pts +template +size_t find_closest_in_sorted(const P &p, const std::vector

&sorted_pts); + +///

+/// Use a plane sweep algorithm to find closest point from pts in sorted_pts +/// https://www.cs.mcgill.ca/~cs251/ClosestPair/ClosestPairPS.html +/// +/// Seach for closest index from thoose points +/// Seach for closest index to thoose points +/// Sorted points by X coordinate(by function closestPoint::sort_fnc) +/// Indices to pts(first) and sorted_pts(second) +template +std::pair find_closest_in_sorted( + const std::vector

&pts, const std::vector

&sorted_pts); + +namespace closestPoint { +///

+/// Function used to sort points before searching for closest +/// +/// First point +/// Second Point +/// True when, p1.x < p2.x +template bool sort_fnc(const P &p1, const P &p2){ return p1.x() < p2.x(); } + +/// Function used to find upper bound in sorted points. +template bool upper_fnc(V value, const P &p){ return value < p.x(); } +/// Function used to find lower bound in sorted points. +template bool lower_fnc(const P &p, V value){ return value > p.x(); } +/// Calc manhatn size of point. Mainly to explain meaning +template uint32_t manhattan_size(const P &p1, const P &p2) +{ return std::abs(p1.x()-p2.x()) + abs(p1.y()-p2.y()); } + +template +typename std::vector

::const_iterator get_lower_bound(uint32_t& dist, const P &p, + typename std::vector

::const_iterator it_x, const std::vector

& pts) +{ + if(it_x == pts.begin()) return pts.begin(); + assert(it_x > pts.begin() && it_x < pts.end()); + + using V = decltype(p.x()); + using It = std::vector

::const_iterator; + It res = std::lower_bound(pts.begin(), it_x, p.x() - dist, lower_fnc); + for (auto it = it_x - 1; it > res; --it) { + uint32_t diff_y = std::abs(it->y() - p.y()); + if (diff_y > dist) continue; + uint32_t diff_x = std::abs(it->x() - p.x()); + uint32_t act_dist = diff_y + diff_x; + if (dist > act_dist) { + dist = act_dist; + res = std::lower_bound(res, it_x, p.x() - dist, lower_fnc); + } + } + return res; +} + +template +typename std::vector

::const_iterator get_uppper_bound(uint32_t& dist, const P &p, + typename std::vector

::const_iterator it_x, const std::vector

& pts) +{ + assert(it_x >= pts.begin() && it_x < pts.end()); + + using V = decltype(p.x()); + using It = std::vector

::const_iterator; + It res = std::upper_bound(it_x, pts.end(), p.x() + dist, upper_fnc); + for (auto it = it_x + 1; it < res; ++it) { + uint32_t diff_y = std::abs(it->y() - p.y()); + if (diff_y > dist) continue; + uint32_t diff_x = std::abs(it->x() - p.x()); + uint32_t act_dist = diff_y + diff_x; + if (dist > act_dist) { + // IMPROVE: calc euclid distance when e.g. (diff_Biggery < 2*diff_smaller) + dist = act_dist; + res = std::upper_bound(it_x, res, p.x() + dist, upper_fnc); + } + } + return res; +} + +} // namespace closestPoint +} // namespace Slic3r + template size_t Slic3r::find_closest_in_sorted(const P &p, const std::vector

&pts) { @@ -99,44 +146,12 @@ size_t Slic3r::find_closest_in_sorted(const P &p, const std::vector

&pts) if (is_it_x_end) --it_x; // manhatn distance to closest point uint32_t manhattan_dist = manhattan_size(*it_x, p); - - // node for lower bound - It it_l; - if (it_x == pts.begin()) { - it_l = it_x; - } else { - it_l = std::lower_bound(pts.begin(), it_x, p.x() - manhattan_dist, lower_fnc); - for (auto it = it_x - 1; it > it_l; --it) { - uint32_t diff_y = std::abs(it->y() - p.y()); - if (diff_y > manhattan_dist) continue; - uint32_t diff_x = std::abs(it->x() - p.x()); - uint32_t act_dist = diff_y + diff_x; - if (manhattan_dist > act_dist) { - manhattan_dist = act_dist; - it_l = std::lower_bound(it_l, it_x, p.x() - manhattan_dist, lower_fnc); - } - } - } + It it_l = get_lower_bound(manhattan_dist,p, it_x, pts); // node for upper bound - It it_u; - if (is_it_x_end) { - it_u = pts.end(); - } else { - it_u = std::upper_bound(it_x, pts.end(), p.x() + manhattan_dist, upper_fnc); - for (auto it = it_x + 1; it < it_u; ++it) { - uint32_t diff_y = std::abs(it->y() - p.y()); - if (diff_y > manhattan_dist) continue; - uint32_t diff_x = std::abs(it->x() - p.x()); - uint32_t act_dist = diff_y + diff_x; - if (manhattan_dist > act_dist) { - // IMPROVE: calc euclid distance when e.g. (diff_Biggery < 2*diff_smaller) - manhattan_dist = act_dist; - it_u = std::upper_bound(it_x, it_u, p.x() + manhattan_dist, upper_fnc); - } - } - } - + It it_u = (is_it_x_end) ? pts.end() : + get_uppper_bound(manhattan_dist, p, it_x, pts); + // find closest by squer distance float dist_sq = std::numeric_limits::max(); size_t result = it_x - pts.begin(); @@ -155,10 +170,83 @@ size_t Slic3r::find_closest_in_sorted(const P &p, const std::vector

&pts) } template -std::pair find_closest_in_sorted( +std::pair Slic3r::find_closest_in_sorted( const std::vector

&pts, const std::vector

&sorted_pts) { - return {0, 0}; + using namespace closestPoint; + // check that input is really sorted + assert(std::is_sorted(sorted_pts.begin(), sorted_pts.end(), sort_fnc

)); + + std::pair res = {std::numeric_limits::max(), + std::numeric_limits::max()}; + // check inputs + if (pts.empty() || sorted_pts.empty()) return res; + // Speed up for one element in vector + if (pts.size() == 1) + return {0, find_closest_in_sorted(pts.front(), sorted_pts)}; + if (sorted_pts.size() == 1) + return {find_closest(sorted_pts.front(), pts), 0}; + + using V = decltype(p.x()); + using It = std::vector

::const_iterator; + + uint32_t res_dist = std::numeric_limits::max(); + float res_dist_sq = std::numeric_limits::max(); + auto set_res_dist_sq = [&res_dist_sq, &res_dist](float dist_sq) { + res_dist_sq = dist_sq; + float dist = sqrt(dist_sq); + // crop dist to unsigned int + uint32_t dist_ui = static_cast(std::ceil(dist)); + if (res_dist < dist_ui) res_dist = dist_ui; + }; + // find limit for manhatn distance + bool is_first = true; + for (const P &p : pts) { + if (is_first) { + is_first = false; + size_t index = find_closest_in_sorted(p, sorted_pts); + res = {0, index}; + const P &p2 = sorted_pts[index]; + V d_x = p.x()-p2.x(); + V d_y = p.y()-p2.y(); + set_res_dist_sq(d_x * d_x + d_y * d_y); + continue; + } + // closest point node in X + It it_x = std::upper_bound(pts.begin(), pts.end(), p.x(), upper_fnc); + bool is_it_x_end = it_x == pts.end(); + // it_x can't pointing to end so change to last point + if (is_it_x_end) --it_x; + // manhatn distance to closest point + uint32_t p_dist = manhattan_size(*it_x, p); + + It it_l = get_lower_bound(res_dist, p, it_x, pts); + + // node for upper bound + It it_u = pts.end(); + if (!is_it_x_end) it_u = get_uppper_bound(res_dist, p, it_x, pts); + + // find closest by squer distance + float dist_sq = res_dist_sq; + for (It it = it_l; it < it_u; ++it) { + uint32_t diff_y = std::abs(it->y() - p.y()); + if (diff_y > res_dist) continue; + float diff_x = it->x() - p.x(); + // calculate square distance + float d = (float) diff_y * diff_y + diff_x * diff_x; + if (dist_sq > d) { + dist_sq = d; + size_t i1 = &p - &pts.begin(); + size_t i2 = it - pts.begin(); + res = {i1, i2}; + } + } + + // found closer? + if (res_dist_sq > dist_sq) + set_res_dist_sq(dist_sq); + } + return res; } #endif // slic3r_ClosestPoint_hpp_ diff --git a/src/libslic3r/CutSurface.cpp b/src/libslic3r/CutSurface.cpp index 1c954bca9..3c506286c 100644 --- a/src/libslic3r/CutSurface.cpp +++ b/src/libslic3r/CutSurface.cpp @@ -409,14 +409,14 @@ VDistances calc_distances(const SurfacePatches &patches, ///

/// Select distances in similar depth between expolygons /// -/// All distances +/// All distances - Vector distances for each shape point /// Vector of letters /// Convert index to addresss inside of shape /// Best projection distances ProjectionDistances choose_best_distance( - const std::vector &distances, - const ExPolygons &shapes, - const ShapePoint2index &shape_point_2_index); + const VDistances &distances, + const ExPolygons &shapes, + const ShapePoint2index &shape_point_2_index); /// /// Create mask for patches @@ -667,7 +667,6 @@ void priv::set_skip_for_out_of_aoi(std::vector &skip_indicies, const BoundingBox &shapes_bb) { assert(skip_indicies.size() == its.indices.size()); - // 1`*----* 2` // / 2 /| // 1 *----* | @@ -739,6 +738,59 @@ void priv::set_skip_for_out_of_aoi(std::vector &skip_indicies, } } +indexed_triangle_set Slic3r::its_mask(const indexed_triangle_set &its, + const std::vector &mask) +{ + if (its.indices.size() != mask.size()) { + assert(false); + return {}; + } + + std::vector cvt_vetices(its.vertices.size(), {std::numeric_limits::max()}); + size_t vertices_count = 0; + size_t faces_count = 0; + for (const auto &t : its.indices) { + size_t index = &t - &its.indices.front(); + if (!mask[index]) continue; + ++faces_count; + for (const auto vi : t) { + uint32_t &cvt = cvt_vetices[vi]; + if (cvt == std::numeric_limits::max()) + cvt = vertices_count++; + } + } + if (faces_count == 0) return {}; + assert(vertices_count <= vertices.size()); + assert(faces_count <= indices.size()); + + indexed_triangle_set result; + result.indices.reserve(faces_count); + result.vertices = std::vector(vertices_count); + for (size_t i = 0; i < its.vertices.size(); ++i) { + uint32_t index = cvt_vetices[i]; + if (index == std::numeric_limits::max()) continue; + result.vertices[index] = its.vertices[i]; + } + + for (const stl_triangle_vertex_indices &f : its.indices) + if (mask[&f - &its.indices.front()]) + result.indices.push_back(stl_triangle_vertex_indices( + cvt_vetices[f[0]], cvt_vetices[f[1]], cvt_vetices[f[2]])); + + return result; +} + +indexed_triangle_set Slic3r::its_cut_AoI(const indexed_triangle_set &its, + const BoundingBox &bb, + const Emboss::IProjection &projection) +{ + std::vector skip_indicies(its.indices.size(), false); + priv::set_skip_for_out_of_aoi(skip_indicies, its, projection, bb); + // invert values in vector of bool + skip_indicies.flip(); + return its_mask(its, skip_indicies); +} + void priv::set_skip_by_angle(std::vector &skip_indicies, const indexed_triangle_set &its, const Project3f &projection, @@ -769,7 +821,6 @@ priv::CutMesh priv::to_cgal(const indexed_triangle_set &its, const std::vector &vertices = its.vertices; const std::vector &indices = its.indices; - std::vector use_indices(indices.size(), {false}); std::vector use_vetices(vertices.size(), {false}); size_t vertices_count = 0; @@ -780,7 +831,6 @@ priv::CutMesh priv::to_cgal(const indexed_triangle_set &its, size_t index = &t - &indices.front(); if (skip_indicies[index]) continue; ++faces_count; - use_indices[index] = true; size_t count_used_vertices = 0; for (const auto vi : t) { if (!use_vetices[vi]) { @@ -820,14 +870,14 @@ priv::CutMesh priv::to_cgal(const indexed_triangle_set &its, if (!flip) { for (const stl_triangle_vertex_indices &f : indices) { - if (!use_indices[&f - &indices.front()]) continue; + if (skip_indicies[&f - &indices.front()]) continue; result.add_face(to_filtrated_vertices_index[f[0]], to_filtrated_vertices_index[f[1]], to_filtrated_vertices_index[f[2]]); } } else { for (const stl_triangle_vertex_indices &f : indices) { - if (!use_indices[&f - &indices.front()]) continue; + if (skip_indicies[&f - &indices.front()]) continue; result.add_face(to_filtrated_vertices_index[f[2]], to_filtrated_vertices_index[f[1]], to_filtrated_vertices_index[f[0]]); @@ -1655,6 +1705,18 @@ struct ClosePoint // search in all shapes points to found closest point to given point uint32_t get_closest_point_index(const Point &p, const ExPolygons &shapes, const VDistances &distances); +using PNode = std::pair; +using PNodes = std::vector; +PNodes create_nodes(const ExPolygons &shapes, const VDistances &distances, size_t shapes_point_count); + +/// +/// Search in all shapes points(nodes) to found closest point to given point +/// +/// Point to search closest +/// Points sorted by X +/// Index into shapes of point closest to given point +uint32_t get_closest_point_index2(const Point &p, const PNodes& nodes); + // Search for closest projection to wanted distance const ProjectionDistance *get_closest_projection(const ProjectionDistances &distance, float wanted_distance); @@ -1702,6 +1764,105 @@ uint32_t priv::get_closest_point_index(const Point &p, return cp.index; } +priv::PNodes priv::create_nodes(const ExPolygons &shapes, const VDistances &distances, size_t shapes_point_count) { + PNodes pts; + pts.reserve(shapes_point_count); + uint32_t index = 0; + auto add_polygon = [&pts, &index, &distances](const Polygon &poly) { + for (const Point &p : poly.points) { + // skip empty distances + if (distances[index].empty()) { + ++index; + continue; + } + pts.emplace_back(p, index++); + } + }; + for (const ExPolygon s : shapes) { + add_polygon(s.contour); + for (const Polygon &h : s.holes) add_polygon(h); + } + + std::sort(pts.begin(), pts.end(), [](const PNode &n1, const PNode &n2) { + return n1.first.x() < n2.first.x(); + }); + return pts; +} + +uint32_t priv::get_closest_point_index2(const Point &p, const PNodes &nodes) +{ + assert(!nodes.empty()); + + // function to find upper point node + auto f_u = [](coord_t value, const PNode& n) { + return value < n.first.x(); + }; + + // function to find lower point node + auto f_l = [](const PNode &n, coord_t value) { + return value > n.first.x(); + }; + + // closest point node in X + auto it_x = std::upper_bound(nodes.begin(), nodes.end(), p.x(), f_u); + // manhatn distance to closest point + auto manhattan_size = [](const Point &p) -> uint32_t { + return std::abs(p.x()) + abs(p.y()); + }; + uint32_t manhattan_dist = (it_x != nodes.end())? + manhattan_size(it_x->first - p) : + manhattan_size(nodes.back().first - p); + // node for lower bound + auto it_l = std::lower_bound(nodes.begin(), it_x, p.x() - manhattan_dist, f_l); + auto it = it_x; + while (it > it_l) { + uint32_t diff_y = std::abs(it->first.y() - p.y()); + if (diff_y > manhattan_dist) { + --it; + continue; + } + uint32_t diff_x = std::abs(it->first.x() - p.x()); + uint32_t act_dist = diff_y + diff_x; + if (manhattan_dist > act_dist) { + manhattan_dist = act_dist; + it_l = std::lower_bound(it_l, it_x, p.x() - manhattan_dist, f_l); + } + --it; + } + + // node for upper bound + auto it_u = std::upper_bound(it_x, nodes.end(), p.x() + manhattan_dist, f_u); + it = it_x; + while (it < it_u) { + ++it; + uint32_t diff_y = std::abs(it->first.y() - p.y()); + if (diff_y > manhattan_dist) continue; + uint32_t diff_x = std::abs(it->first.x() - p.x()); + uint32_t act_dist = diff_y + diff_x; + if (manhattan_dist > act_dist) { + // IMPROVE: calc euclid distance when e.g. (diff_Biggery < 2*diff_smaller) + manhattan_dist = act_dist; + it_u = std::upper_bound(it_x, it_u, p.x() + manhattan_dist, f_u); + } + } + + // find closest by squer distance + it = it_l; + ClosePoint cp; + for (it = it_l; it < it_u; ++it) { + uint32_t diff_y = std::abs(it->first.y() - p.y()); + if (diff_y > manhattan_dist) continue; + float diff_x = it->first.x() - p.x(); + // calculate square distance + float d = (float) diff_y * diff_y + diff_x * diff_x; + if (cp.dist_sq > d) { + cp.dist_sq = d; + cp.index = it->second; + } + } + return cp.index; +} + const priv::ProjectionDistance *priv::get_closest_projection( const ProjectionDistances &distance, float wanted_distance) { @@ -1904,9 +2065,9 @@ priv::ClosePoint priv::find_close_point(const Point &p, // IMPROVE: create better structure to find closest points e.g. Tree // IMPROVE2: when select distance fill in all distances from Patch priv::ProjectionDistances priv::choose_best_distance( - const std::vector &distances, - const ExPolygons &shapes, - const ShapePoint2index &s2i) + const VDistances &distances, + const ExPolygons &shapes, + const ShapePoint2index &s2i) { // collect one closest projection for each outline point ProjectionDistances result(distances.size()); @@ -1924,6 +2085,9 @@ priv::ProjectionDistances priv::choose_best_distance( // Select point from shapes(text contour) which is closest to center (all in 2d) uint32_t unfinished_index = get_closest_point_index(center, shapes, distances); + //PNodes pts = create_nodes(shapes, distances, s2i.get_count()); + //uint32_t unfinished_index2 = get_closest_point_index2(center, pts); + do { const ProjectionDistance* pd = get_closest_projection(distances[unfinished_index], wanted_distance); // selection of closest_id should proove that pd has value diff --git a/src/libslic3r/CutSurface.hpp b/src/libslic3r/CutSurface.hpp index ecb08ed02..f923bb7e0 100644 --- a/src/libslic3r/CutSurface.hpp +++ b/src/libslic3r/CutSurface.hpp @@ -48,5 +48,25 @@ SurfaceCut cut_surface(const ExPolygons &shapes, indexed_triangle_set cut2model(const SurfaceCut &cut, const Emboss::IProject3f &projection); +/// +/// Separate (A)rea (o)f (I)nterest .. AoI from model +/// NOTE: Only 2d filtration, do not filtrate by Z coordinate +/// +/// Input model +/// Bounding box to project into space +/// Define tranformation of BB into space +/// Triangles lay at least partialy inside of projected Bounding box +indexed_triangle_set its_cut_AoI(const indexed_triangle_set &its, + const BoundingBox &bb, + const Emboss::IProjection &projection); + +/// +/// Separate triangles by mask +/// +/// Input model +/// Mask - same size as its::indices +/// Copy of indices by mask(with their vertices) +indexed_triangle_set its_mask(const indexed_triangle_set &its, const std::vector &mask); + } // namespace Slic3r #endif // slic3r_CutSurface_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp index d4093a3e0..69260fba4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp @@ -940,8 +940,8 @@ bool GLGizmoEmboss::process() const TextConfiguration &tc = data.text_configuration; if (tc.font_item.prop.use_surface) { // Model to cut surface from. - UseSurfaceData::ModelSource source = UseSurfaceData::create_source(m_volume); - if (source.its.empty()) return false; + UseSurfaceData::ModelSources sources = UseSurfaceData::create_sources(m_volume); + if (sources.empty()) return false; Transform3d text_tr = m_volume->get_matrix(); auto& fix_3mf = m_volume->text_configuration->fix_3mf_tr; @@ -953,7 +953,7 @@ bool GLGizmoEmboss::process() assert(is_outside || m_volume->is_negative_volume() || m_volume->is_modifier()); UseSurfaceData surface_data{std::move(data), text_tr, is_outside, - std::move(source)}; + std::move(sources)}; job = std::make_unique(std::move(surface_data)); } else { job = std::make_unique(std::move(data)); diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index 2be50293d..9a4fcb6e5 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -318,7 +318,7 @@ void EmbossUpdateJob::finalize(bool canceled, std::exception_ptr &eptr) priv::update_volume(std::move(m_result), m_input); } -UseSurfaceData::ModelSource UseSurfaceData::create_source( +UseSurfaceData::ModelSources UseSurfaceData::create_sources( const ModelVolume *text_volume) { if (text_volume == nullptr) return {}; @@ -326,54 +326,17 @@ UseSurfaceData::ModelSource UseSurfaceData::create_source( const ModelVolumePtrs &volumes = text_volume->get_object()->volumes; // no other volume in object if (volumes.size() <= 1) return {}; - - // One volume transformation is used for transform others - // Most common cut is into one volume so it is cheaper to do not transform this volume - // NOTE: Preparation is made on Main thred! - auto find_biggest = [&text_volume] - (const ModelVolumePtrs& volumes, const ObjectID& text_volume_id)->const ModelVolume * { - const ModelVolume *biggest = nullptr; - size_t biggest_size = 0; - for (const ModelVolume *v: volumes) { - if (v->id() == text_volume_id) continue; - if (!v->is_model_part()) continue; - const TriangleMesh &tm = v->mesh(); - if (tm.empty()) continue; - if (tm.its.empty()) continue; - if (biggest_size > tm.its.indices.size()) continue; - biggest_size = tm.its.indices.size(); - biggest = v; - } - assert(biggest != nullptr); - return biggest; - }; - const ModelVolume *biggest_volume = find_biggest(volumes, text_volume->id()); - const TriangleMesh &biggest_tm = biggest_volume->mesh(); - - // IMPROVE: Copy only AOI by shapes and projection - // NOTE: copy of source mesh tringles and it slows down on big meshes - ModelSource result{ - {biggest_tm.its}, // copy !!! - biggest_volume->get_transformation().get_matrix(), - biggest_tm.bounding_box() - }; - - Transform3d tr_inv = result.tr.inverse(); - // Improve create object from part or use gl_volume - // Get first model part in object + ModelSources result; + result.reserve(volumes.size() - 1); for (const ModelVolume *v : volumes) { if (v->id() == text_volume->id()) continue; - if (v->id() == biggest_volume->id()) continue; + // skip modifiers and negative volumes, ... if (!v->is_model_part()) continue; const TriangleMesh &tm = v->mesh(); if (tm.empty()) continue; if (tm.its.empty()) continue; - - Transform3d tr = v->get_matrix() * tr_inv; - indexed_triangle_set its = tm.its; // copy !!! - its_transform(its, tr); - result.its.emplace_back(std::move(its)); + result.push_back({v->get_mesh_shared_ptr(), v->get_matrix()}); } return result; } @@ -386,6 +349,22 @@ UseSurfaceJob::UseSurfaceJob(UseSurfaceData &&input) assert(priv::check(m_input, true)); } +static const UseSurfaceData::ModelSource* get_biggest( + const UseSurfaceData::ModelSources &sources) +{ + const UseSurfaceData::ModelSource *biggest = nullptr; + for (const UseSurfaceData::ModelSource &s : sources) { + if (biggest == nullptr) { + biggest = &s; + continue; + } + if (biggest->mesh->its.indices.size() < s.mesh->its.indices.size()) { + biggest = &s; + } + } + return biggest; +} + void UseSurfaceJob::process(Ctl &ctl) { if (!priv::check(m_input)) throw std::runtime_error("Bad input data for UseSurfaceJob."); @@ -406,28 +385,66 @@ void UseSurfaceJob::process(Ctl &ctl) { if (was_canceled()) return; - Transform3d mesh_tr_inv = m_input.source.tr.inverse(); - Transform3d cut_projection_tr = mesh_tr_inv * m_input.text_tr; - Transform3d emboss_tr = cut_projection_tr.inverse(); - BoundingBoxf3 mesh_bb_tr = m_input.source.bb.transformed(emboss_tr); - std::pair z_range{mesh_bb_tr.min.z(), mesh_bb_tr.max.z()}; + // Define alignment of text - left, right, center, top bottom, .... + BoundingBox bb = get_extents(shapes); + Point projection_center = bb.center(); + for (ExPolygon &shape : shapes) shape.translate(-projection_center); + bb.translate(-projection_center); const Emboss::FontFile &ff = *m_input.font_file.font_file; - double shape_scale = Emboss::get_shape_scale(fp, ff); + double shape_scale = Emboss::get_shape_scale(fp, ff); + + size_t biggest_count = 0; + size_t biggest_index = 0; + const UseSurfaceData::ModelSource *biggest = nullptr; + std::vector s_to_itss(m_input.sources.size(), std::numeric_limits::max()); + std::vector itss; + itss.reserve(m_input.sources.size()); + for (const UseSurfaceData::ModelSource &s : m_input.sources) { + Transform3d mesh_tr_inv = s.tr.inverse(); + Transform3d cut_projection_tr = mesh_tr_inv * m_input.text_tr; + std::pair z_range{0., 1.}; + Emboss::OrthoProject cut_projection = + priv::create_projection_for_cut(cut_projection_tr, shape_scale, z_range); + // copy only part of source model + indexed_triangle_set its = its_cut_AoI(s.mesh->its, bb, cut_projection); + if (its.indices.empty()) continue; + if (biggest_count < its.vertices.size()) { + biggest_count = its.vertices.size(); + biggest = &s; + } + s_to_itss[&s - &m_input.sources.front()] = itss.size(); + itss.emplace_back(std::move(its)); + } + if (itss.empty()) + throw priv::EmbossJobException(_u8L("There is no volume in projection direction.").c_str()); + + Transform3d tr_inv = biggest->tr.inverse(); + size_t itss_index = s_to_itss[biggest - &m_input.sources.front()]; + BoundingBoxf3 mesh_bb = bounding_box(itss[itss_index]); + for (const UseSurfaceData::ModelSource &s : m_input.sources) { + if (&s == biggest) continue; + size_t itss_index = s_to_itss[&s - &m_input.sources.front()]; + if (itss_index == std::numeric_limits::max()) continue; + Transform3d tr = s.tr * tr_inv; + indexed_triangle_set &its = itss[itss_index]; + its_transform(its, tr); + BoundingBoxf3 bb = bounding_box(its); + mesh_bb.merge(bb); + } + + Transform3d mesh_tr_inv = tr_inv; + Transform3d cut_projection_tr = mesh_tr_inv * m_input.text_tr; + Transform3d emboss_tr = cut_projection_tr.inverse(); + BoundingBoxf3 mesh_bb_tr = mesh_bb.transformed(emboss_tr); + std::pair z_range{mesh_bb_tr.min.z(), mesh_bb_tr.max.z()}; Emboss::OrthoProject cut_projection = priv::create_projection_for_cut( cut_projection_tr, shape_scale, z_range); float projection_ratio = (-z_range.first + priv::safe_extension) / (z_range.second - z_range.first + 2 * priv::safe_extension); - // Define alignment of text - left, right, center, top bottom, .... - BoundingBox bb = get_extents(shapes); - Point projection_center = bb.center(); - for (ExPolygon &shape : shapes) - shape.translate(-projection_center); - // Use CGAL to cut surface from triangle mesh - SurfaceCut cut = cut_surface(shapes, m_input.source.its, - cut_projection, projection_ratio); + SurfaceCut cut = cut_surface(shapes, itss, cut_projection, projection_ratio); if (cut.empty()) throw priv::EmbossJobException( _u8L("There is no valid surface for text projection.").c_str()); @@ -512,8 +529,8 @@ bool priv::check(const EmbossDataUpdate &input, bool is_main_thread){ } bool priv::check(const UseSurfaceData &input, bool is_main_thread){ bool res = check((EmbossDataUpdate) input, is_main_thread); - assert(!input.source.its.empty()); - res &= !input.source.its.empty(); + assert(!input.sources.empty()); + res &= !input.sources.empty(); return res; } diff --git a/src/slic3r/GUI/Jobs/EmbossJob.hpp b/src/slic3r/GUI/Jobs/EmbossJob.hpp index 32b757dc5..7966a8179 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.hpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.hpp @@ -161,22 +161,20 @@ struct UseSurfaceData : public EmbossDataUpdate struct ModelSource { - // IMPROVE: Copy only AOI by shapes and projection - // NOTE: copy of source mesh tringles and it slows down on big meshes - std::vector its; + // source volumes + std::shared_ptr mesh; // Transformation of volume inside of object Transform3d tr; - // extract bounds for projection - BoundingBoxf3 bb; }; - ModelSource source; + using ModelSources = std::vector; + ModelSources sources; /// /// Copied triangles from object to be able create mesh for cut surface from /// /// Define text in object /// Source data for cut surface from - static ModelSource create_source(const ModelVolume *text_volume); + static ModelSources create_sources(const ModelVolume *text_volume); }; ///