diff --git a/resources/icons/add_text_modifier.svg b/resources/icons/add_text_modifier.svg
index 49f4d4252..0a8376741 100644
--- a/resources/icons/add_text_modifier.svg
+++ b/resources/icons/add_text_modifier.svg
@@ -1,4 +1,4 @@
diff --git a/resources/icons/add_text_negative.svg b/resources/icons/add_text_negative.svg
index e447d5328..b125f0839 100644
--- a/resources/icons/add_text_negative.svg
+++ b/resources/icons/add_text_negative.svg
@@ -1,4 +1,4 @@
diff --git a/resources/icons/add_text_part.svg b/resources/icons/add_text_part.svg
index 01f0ff1ea..3d36ed0fc 100644
--- a/resources/icons/add_text_part.svg
+++ b/resources/icons/add_text_part.svg
@@ -1,4 +1,4 @@
diff --git a/src/libslic3r/CutSurface.cpp b/src/libslic3r/CutSurface.cpp
index b76431e97..0d4cd2aa1 100644
--- a/src/libslic3r/CutSurface.cpp
+++ b/src/libslic3r/CutSurface.cpp
@@ -9,29 +9,18 @@ void Slic3r::append(SurfaceCut &sc, SurfaceCut &&sc_add)
return;
}
- if (!sc_add.cut.empty()) {
+ if (!sc_add.contours.empty()) {
SurfaceCut::Index offset = static_cast(
sc.vertices.size());
- size_t require = sc.cut.size() + sc_add.cut.size();
- if (sc.cut.capacity() < require) sc.cut.reserve(require);
- for (std::vector &cut : sc_add.cut)
+ size_t require = sc.contours.size() + sc_add.contours.size();
+ if (sc.contours.capacity() < require) sc.contours.reserve(require);
+ for (std::vector &cut : sc_add.contours)
for (SurfaceCut::Index &i : cut) i += offset;
- append(sc.cut, std::move(sc_add.cut));
+ append(sc.contours, std::move(sc_add.contours));
}
its_merge(sc, std::move(sc_add));
}
-#if !ENABLE_NEW_CGAL
-
-SurfaceCuts Slic3r::cut_surface(const indexed_triangle_set &model,
- const ExPolygons &shapes,
- const Emboss::IProject &projection)
-{
- return {};
-}
-
-#else
-
#include
#include
#include
@@ -172,7 +161,10 @@ struct Visitor {
// Properties of the object mesh.
VertexShapeMap vert_shape_map;
-
+
+ // check for anomalities
+ bool* is_valid;
+
// keep source of intersection for each intersection
// used to copy data into vert_shape_map
std::vector intersections;
@@ -214,6 +206,7 @@ struct Visitor {
bool is_source_coplanar);
///
+ /// Called when a new vertex is added in tm (either an edge split or a vertex inserted in the interior of a face).
/// Fill vertex_shape_map by intersections
///
/// Order number of intersection point
@@ -264,14 +257,29 @@ void set_face_type(FaceTypeMap &face_type_map,
const Project &project,
const CutMesh &shape_mesh);
+
+///
+/// Check orientation(normal direction) of face on mesh
+///
+/// Face index to inspect
+/// Mesh contained fi
+/// Define direction of projection
+/// TRUE for cutted face otherwise FALSE
+bool is_toward_projection(FI fi,
+ const CutMesh &mesh,
+ const Project &projection);
+
///
/// Change FaceType from not_constrained to inside
/// For neighbor(or neighbor of neighbor of ...) of inside triangles.
/// Process only not_constrained triangles
///
/// Corefined mesh
+/// Projection from 2d to 3d
/// In/Out map with faces type
-void flood_fill_inner(const CutMesh &mesh, FaceTypeMap &face_type_map);
+void flood_fill_inner(const CutMesh &mesh,
+ const Project &projection,
+ FaceTypeMap &face_type_map);
using ReductionMap = CutMesh::Property_map;
///
@@ -290,6 +298,38 @@ void create_reduce_map(ReductionMap &reduction_map,
const FaceTypeMap &face_type_map,
const VertexShapeMap &vert_shape_map);
+// connected faces(triangles) and outlines(halfEdges) for one surface cut
+using CutAOI = std::pair, std::vector>;
+using CutAOIs = std::vector;
+
+///
+/// Create areas from mesh surface
+///
+/// Model
+/// Cutted shapes
+/// Define Triangles of interest.
+/// Edge between inside / outside.
+/// NOTE: Not const because it need to flag proccessed faces
+/// Areas of interest from mesh
+CutAOIs create_cut_area_of_interests(const CutMesh &mesh,
+ const ExPolygons &shapes,
+ FaceTypeMap &face_type_map);
+
+///
+/// Filter out cuts which are behind another.
+/// Prevent overlapping embossed shape in space.
+///
+/// AOIs
+/// triangle model
+/// 2d cutted shapes
+/// Projection from 2d to 3d
+/// Identify source of intersection
+void filter_cuts(CutAOIs &cuts,
+ const CutMesh &mesh,
+ const ExPolygons &shapes,
+ const Project &projection,
+ const VertexShapeMap &vert_shape_map);
+
using ConvertMap = CutMesh::Property_map;
///
/// Create surface cuts from mesh model
@@ -303,11 +343,11 @@ using ConvertMap = CutMesh::Property_map;
/// Used only inside function.
/// Store conversion from mesh to result.
/// Created surface cuts
-SurfaceCuts create_surface_cut(const CutMesh &mesh,
- const ExPolygons &shapes,
- const ReductionMap &reduction_map,
- FaceTypeMap &face_type_map,
- ConvertMap &convert_map);
+SurfaceCuts create_surface_cuts(const CutAOIs &cutAOIs,
+ const CutMesh &mesh,
+ const ReductionMap &reduction_map,
+ ConvertMap &convert_map);
+
///
/// Collect connected inside faces
@@ -349,7 +389,7 @@ SurfaceCut create_index_triangle_set(const std::vector &faces,
/// Reduction of vertices
/// Map to convert CGAL vertex to its::vertex
/// Cuts - outlines of surface
-SurfaceCut::CutType create_cut(const std::vector &outlines,
+SurfaceCut::CutContour create_cut(const std::vector &outlines,
const CutMesh &mesh,
const ReductionMap &reduction_map,
const ConvertMap &v2v);
@@ -371,10 +411,12 @@ SurfaceCuts Slic3r::cut_surface(const indexed_triangle_set &model,
const Emboss::IProject &projection)
{
priv::CutMesh cgal_model = priv::to_cgal(model);
+ CGAL::IO::write_OFF("C:/data/temp/model.off", cgal_model); // only debug
std::string edge_shape_map_name = "e:IntersectingElement";
std::string face_shape_map_name = "f:IntersectingElement";
- priv::CutMesh cgal_shape = priv::to_cgal(shapes, projection, edge_shape_map_name, face_shape_map_name);
+ priv::CutMesh cgal_shape = priv::to_cgal(shapes, projection, edge_shape_map_name, face_shape_map_name);
+ CGAL::IO::write_OFF("C:/data/temp/shape.off", cgal_shape); // only debug
auto edge_shape_map = cgal_shape.property_map(edge_shape_map_name).first;
auto face_shape_map = cgal_shape.property_map(face_shape_map_name).first;
@@ -382,9 +424,11 @@ SurfaceCuts Slic3r::cut_surface(const indexed_triangle_set &model,
std::string vert_shape_map_name = "v:IntersectingElement";
// pointer to edge or face shape_map
priv::VertexShapeMap vert_shape_map = cgal_model.add_property_map(vert_shape_map_name).first;
-
- // create anotation visitor
- priv::Visitor visitor{cgal_model, cgal_shape, edge_shape_map, face_shape_map, vert_shape_map};
+
+ // detect anomalities in visitor.
+ bool is_valid = true;
+ // create anotation visitor - Must be copyable
+ priv::Visitor visitor{cgal_model, cgal_shape, edge_shape_map, face_shape_map, vert_shape_map, &is_valid};
// bool map for affected edge
priv::EcmType ecm = get(priv::DynamicEdgeProperty(), cgal_model);
@@ -395,6 +439,8 @@ SurfaceCuts Slic3r::cut_surface(const indexed_triangle_set &model,
const auto& q = CGAL::parameters::do_not_modify(true);
CGAL::Polygon_mesh_processing::corefine(cgal_model, cgal_shape, p, q);
+ if (!is_valid) return {};
+
std::string face_type_map_name = "f:side";
priv::FaceTypeMap face_type_map = cgal_model.add_property_map(face_type_map_name).first;
@@ -403,7 +449,7 @@ SurfaceCuts Slic3r::cut_surface(const indexed_triangle_set &model,
priv::store(cgal_model, face_type_map, "C:/data/temp/constrained.off"); // only debug
// Seed fill the other faces inside the region.
- priv::flood_fill_inner(cgal_model, face_type_map);
+ priv::flood_fill_inner(cgal_model, projection, face_type_map);
priv::store(cgal_model, face_type_map, "C:/data/temp/filled.off"); // only debug
std::string vertex_reduction_map_name = "v:reduction";
@@ -411,11 +457,16 @@ SurfaceCuts Slic3r::cut_surface(const indexed_triangle_set &model,
priv::create_reduce_map(vertex_reduction_map, cgal_model, face_type_map, vert_shape_map);
priv::store(cgal_model, vertex_reduction_map, "C:/data/temp/reduction.off"); // only debug
+ priv::CutAOIs cutAOIs = create_cut_area_of_interests(cgal_model, shapes, face_type_map);
+
+ // Filter out NO top one cuts
+ priv::filter_cuts(cutAOIs, cgal_model, shapes, projection, vert_shape_map);
+
// conversion map between vertex index in cgal_model and indices in result
// used instead of std::map
std::string vertec_convert_map_name = "v:convert";
priv::ConvertMap vertex_convert_map = cgal_model.add_property_map(vertec_convert_map_name).first;
- SurfaceCuts result = priv::create_surface_cut(cgal_model, shapes, vertex_reduction_map, face_type_map, vertex_convert_map);
+ SurfaceCuts result = priv::create_surface_cuts(cutAOIs, cgal_model, vertex_reduction_map, vertex_convert_map);
priv::store(result, "C:/data/temp/cut"); // only debug
@@ -423,6 +474,87 @@ SurfaceCuts Slic3r::cut_surface(const indexed_triangle_set &model,
return result;
}
+indexed_triangle_set Slic3r::cuts2model(const SurfaceCuts &cuts,
+ const Emboss::IProject &projection)
+{
+ indexed_triangle_set result;
+ size_t count_vertices = 0;
+ size_t count_indices = 0;
+ for (const SurfaceCut &cut : cuts) {
+ assert(!cut.empty());
+ count_indices += cut.indices.size()*2;
+ // indices from from zig zag
+ for (const auto &c : cut.contours) {
+ assert(!c.empty());
+ count_indices += c.size() * 2;
+ }
+ count_vertices += cut.vertices.size()*2;
+ }
+ result.vertices.reserve(count_vertices);
+ result.indices.reserve(count_indices);
+
+ size_t indices_offset = 0;
+ for (const SurfaceCut &cut : cuts) {
+ // front
+ for (const auto &v : cut.vertices)
+ result.vertices.push_back(v);
+ for (const auto &i : cut.indices)
+ result.indices.emplace_back(i.x() + indices_offset,
+ i.y() + indices_offset,
+ i.z() + indices_offset);
+
+ // back
+ for (const auto &v : cut.vertices) {
+ Vec3f v2 = projection.project(v);
+ result.vertices.push_back(v2);
+ }
+ size_t back_offset = indices_offset + cut.vertices.size();
+ for (const auto &i : cut.indices) {
+ assert(i.x() + back_offset < result.vertices.size());
+ assert(i.y() + back_offset < result.vertices.size());
+ assert(i.z() + back_offset < result.vertices.size());
+ // Y and Z is swapped CCW triangles for back side
+ result.indices.emplace_back(i.x() + back_offset,
+ i.z() + back_offset,
+ i.y() + back_offset);
+ }
+
+ // zig zag indices
+ for (const auto &contour : cut.contours) {
+ size_t prev_ci = contour.back();
+ size_t prev_front_index = indices_offset + prev_ci;
+ size_t prev_back_index = back_offset + prev_ci;
+ for (size_t ci : contour) {
+ size_t front_index = indices_offset + ci;
+ size_t back_index = back_offset + ci;
+ assert(front_index < result.vertices.size());
+ assert(prev_front_index < result.vertices.size());
+ assert(back_index < result.vertices.size());
+ assert(prev_back_index < result.vertices.size());
+
+ result.indices.emplace_back(
+ front_index,
+ prev_front_index,
+ back_index
+ );
+ result.indices.emplace_back(
+ prev_front_index,
+ prev_back_index,
+ back_index
+ );
+ prev_front_index = front_index;
+ prev_back_index = back_index;
+ }
+ }
+
+ indices_offset = result.vertices.size();
+ }
+
+ assert(count_vertices == result.vertices.size());
+ assert(count_indices == result.indices.size());
+ return result;
+}
+
priv::CutMesh priv::to_cgal(const indexed_triangle_set &its)
{
CutMesh result;
@@ -530,11 +662,11 @@ void priv::set_face_type(FaceTypeMap &face_type_map,
const Project &project,
const CutMesh &shape_mesh)
{
- for (auto& fi : mesh.faces()) {
+ for (const FI& fi : mesh.faces()) {
FaceType face_type = FaceType::not_constrained;
- auto hi_end = mesh.halfedge(fi);
- auto hi = hi_end;
+ HI hi_end = mesh.halfedge(fi);
+ HI hi = hi_end;
do {
EI edge_index = mesh.edge(hi);
// is edge new created - constrained?
@@ -587,7 +719,7 @@ void priv::set_face_type(FaceTypeMap &face_type_map,
shape_mesh.point(VI(j)),
shape_mesh.point(VI(i + 1)),
shape_mesh.point(VI(j + 1)), p);
- is_inside = abcp == CGAL::POSITIVE;
+ is_inside = abcp == CGAL::NEGATIVE;
} else if (i_from < i_to || (i_from == i_to && type_from < type_to)) {
// TODO: check that it is continous indices of contour
bool is_last = shape_from.is_first() && shape_to.is_last() &&
@@ -601,16 +733,7 @@ void priv::set_face_type(FaceTypeMap &face_type_map,
}
if (is_inside) {
- // Is this face oriented towards p or away from p?
- const auto &a = mesh.point(mesh.source(hi));
- const auto &b = mesh.point(mesh.target(hi));
- const auto &c = mesh.point(mesh.target(mesh.next(hi)));
-
- Vec3f a_(a.x(), a.y(), a.z());
- Vec3f p_ = project.project(a_);
- CGAL::Epick::Point_3 p{p_.x(), p_.y(), p_.z()};
- auto abcp = CGAL::orientation(a, b, c, p);
- if (abcp == CGAL::POSITIVE)
+ if (is_toward_projection(fi, mesh, project))
face_type = FaceType::inside;
else
is_inside = false;
@@ -625,7 +748,27 @@ void priv::set_face_type(FaceTypeMap &face_type_map,
}
}
-void priv::flood_fill_inner(const CutMesh &mesh, FaceTypeMap &face_type_map)
+bool priv::is_toward_projection(FI fi,
+ const CutMesh &mesh,
+ const Project &projection)
+{
+ HI hi = mesh.halfedge(fi);
+ const auto &a = mesh.point(mesh.source(hi));
+ const auto &b = mesh.point(mesh.target(hi));
+ const auto &c = mesh.point(mesh.target(mesh.next(hi)));
+
+ Vec3f a_(a.x(), a.y(), a.z());
+ Vec3f p_ = projection.project(a_);
+
+ CGAL::Epick::Point_3 p{p_.x(), p_.y(), p_.z()};
+
+ return CGAL::orientation(a, b, c, p) == CGAL::NEGATIVE;
+}
+
+
+void priv::flood_fill_inner(const CutMesh &mesh,
+ const Project &projection,
+ FaceTypeMap &face_type_map)
{
for (FI fi : mesh.faces()) {
if (face_type_map[fi] != FaceType::not_constrained) continue;
@@ -663,9 +806,15 @@ void priv::flood_fill_inner(const CutMesh &mesh, FaceTypeMap &face_type_map)
HI hi_end = hi;
do {
FI fi_opposite = mesh.face(mesh.opposite(hi));
- FaceType side = face_type_map[fi_opposite];
- if (side == FaceType::not_constrained)
- queue.emplace(fi_opposite);
+ FaceType& side = face_type_map[fi_opposite];
+ if (side == FaceType::not_constrained) {
+ if (is_toward_projection(fi_opposite, mesh, projection)) {
+ queue.emplace(fi_opposite);
+ } else {
+ // Is in opposit direction
+ side = FaceType::outside;
+ }
+ }
hi = mesh.next(hi);
} while (hi != hi_end);
}
@@ -712,6 +861,13 @@ void priv::Visitor::intersection_point_detected(std::size_t i_id,
intersection_ptr = &edge_shape_map[shape.edge(h_f)];
if (sdim == 0) vert_shape_map[object.target(h_e)] = intersection_ptr;
}
+
+ if (intersection_ptr->point_index == std::numeric_limits::max()) {
+ // there is unexpected intersection
+ // Top (or Bottom) shape contour edge (or vertex) intersection
+ // Suggest to change projection min/max limits
+ *is_valid = false;
+ }
intersections[i_id] = intersection_ptr;
}
@@ -721,7 +877,8 @@ void priv::Visitor::new_vertex_added(std::size_t i_id, VI v, const CutMesh &tm)
assert(i_id < intersections.size());
const IntersectingElement *intersection_ptr = intersections[i_id];
assert(intersection_ptr != nullptr);
- assert(intersection_ptr->point_index != std::numeric_limits::max());
+ // intersection was not filled in function intersection_point_detected
+ //assert(intersection_ptr->point_index != std::numeric_limits::max());
vert_shape_map[v] = intersection_ptr;
}
@@ -895,14 +1052,14 @@ SurfaceCut priv::create_index_triangle_set(const std::vector &faces,
}
-SurfaceCut::CutType priv::create_cut(const std::vector &outlines,
+SurfaceCut::CutContour priv::create_cut(const std::vector &outlines,
const CutMesh &mesh,
const ReductionMap &reduction_map,
const ConvertMap &v2v)
{
using Index = SurfaceCut::Index;
- SurfaceCut::CutType cut;
- SurfaceCut::CutType unclosed_cut;
+ SurfaceCut::CutContour cut;
+ SurfaceCut::CutContour unclosed_cut;
for (HI hi : outlines) {
VI vi_s = mesh.source(hi);
VI vi_t = mesh.target(hi);
@@ -976,49 +1133,194 @@ SurfaceCut::CutType priv::create_cut(const std::vector &outlines,
return cut;
}
-SurfaceCuts priv::create_surface_cut(const CutMesh &mesh,
- const ExPolygons &shapes,
- const ReductionMap &reduction_map,
- FaceTypeMap &face_type_map,
- ConvertMap &convert_map)
+priv::CutAOIs priv::create_cut_area_of_interests(const CutMesh &mesh,
+ const ExPolygons &shapes,
+ FaceTypeMap &face_type_map)
{
- // faces from one surface cut
- std::vector faces;
- // IMPROVE: Size can't be greater but it is too big.
- faces.reserve(mesh.faces().size());
- std::vector outlines;
- // IMPROVE: Create better guess of size
- size_t max_outline_count = mesh.faces().size()/2;
- outlines.reserve(max_outline_count);
+ // IMPROVE: Create better heuristic for count.
+ size_t faces_per_cut = mesh.faces().size() / shapes.size();
+ size_t outlines_per_cut = faces_per_cut / 2;
+ size_t cuts_per_model = shapes.size() * 2;
+ CutAOIs result;
+ result.reserve(cuts_per_model);
+
+ // It is faster to use one queue for all cuts
+ std::queue process;
+ for (FI fi : mesh.faces()) {
+ if (face_type_map[fi] != FaceType::inside) continue;
+
+ CutAOI cut;
+ std::vector &faces = cut.first;
+ std::vector &outlines = cut.second;
+
+ // faces for one surface cut
+ faces.reserve(faces_per_cut);
+ // outline for one surface cut
+ outlines.reserve(outlines_per_cut);
+
+ assert(process.empty());
+ // Process queue of faces to separate to surface_cut
+ process.push(fi);
+ collect_surface_data(process, faces, outlines, face_type_map, mesh);
+
+ assert(!faces.empty());
+ assert(!outlines.empty());
+ result.emplace_back(std::move(cut));
+ }
+ return result;
+}
+
+void priv::filter_cuts(CutAOIs &cuts,
+ const CutMesh &mesh,
+ const ExPolygons &shapes,
+ const Project &projection,
+ const VertexShapeMap &vert_shape_map)
+{
+ auto get_point = [&shapes](const IntersectingElement &intersection) -> Point {
+ assert(intersection.vertex_base != std::numeric_limits::max());
+ assert(intersection.point_index != std::numeric_limits::max());
+ size_t offset = 0;
+ for (const ExPolygon &s : shapes) {
+ if (offset == intersection.vertex_base) {
+ assert(s.contour.size() > intersection.point_index);
+ return s.contour[intersection.point_index];
+ }
+ // *2 .. see description of IntersectingElement::vertex_base
+ offset += 2*s.contour.size();
+ assert(offset <= intersection.vertex_base);
+
+ for (const Polygon &h : s.holes) {
+ if (offset == intersection.vertex_base) {
+ assert(h.points.size() > intersection.point_index);
+ return h.points[intersection.point_index];
+ }
+ // *2 .. see description of IntersectingElement::vertex_base
+ offset += 2*h.points.size();
+ assert(offset <= intersection.vertex_base);
+ }
+ }
+
+ // index is out of shape
+ assert(false);
+ return Point{};
+ };
+
+ struct CutIndex
+ {
+ // index in vector into cuts
+ size_t cut_index = std::numeric_limits::max();
+ // vertex index inside of mesh
+ VI vi;
+ };
+ size_t count = count_points(shapes);
+ // each source point from shapes could has only one nearest projection
+ std::vector indices(count);
+
+ // flags which cut is not first
+ std::vector del_cuts(cuts.size(), false);
+
+ // check whether vertex is behind another cut
+ auto is_behind = [&vert_shape_map, &indices, &del_cuts, &get_point,
+ &projection, &mesh]
+ (VI vi, size_t cut_index) -> bool {
+ const IntersectingElement *i = vert_shape_map[vi];
+
+ // Is vertex made by corefine?
+ if (i == nullptr) return false;
+
+ assert(i->vertex_base != std::numeric_limits::max());
+ assert(i->vertex_base%2 == 0);
+ assert(i->point_index != std::numeric_limits::max());
+ assert(i->attr != (unsigned char)IntersectingElement::Type::undefined);
+
+ // Use only straigh edge
+ if (i->get_type() != IntersectingElement::Type::edge_1)
+ return false;
+
+
+ size_t index = i->vertex_base/2 + i->point_index;
+ CutIndex &ci = indices[index];
+
+ // is first cut for vertex OR
+ // is remembred cut is deleted?
+ if (ci.cut_index == std::numeric_limits::max() ||
+ del_cuts[ci.cut_index] ) {
+ ci.cut_index = cut_index;
+ ci.vi = vi;
+ return false;
+ }
+
+ if (ci.cut_index == cut_index) {
+ assert(ci.vi == vi);
+ return false;
+ }
+
+ // compare distances of vertices
+ Point p = get_point(*i);
+ Vec3f source_point = projection.project(p).first;
+ const auto &prev = mesh.point(ci.vi);
+ Vec3f prev_point(prev.x(), prev.y(), prev.z());
+ float prev_sq_norm = (source_point - prev_point).squaredNorm();
+
+ const auto &act = mesh.point(vi);
+ Vec3f act_point(act.x(), act.y(), act.z());
+ float act_sq_norm = (source_point - act_point).squaredNorm();
+
+ if (act_sq_norm > prev_sq_norm) {
+ del_cuts[cut_index] = true;
+ return true;
+ }
+
+ // previous cut is behind actual one
+ del_cuts[ci.cut_index] = true;
+ ci.cut_index = cut_index;
+ ci.vi = vi;
+ return false;
+ };
+
+ // filter top one cuts
+ for (const CutAOI &cut : cuts) {
+ size_t cut_index = &cut - &cuts.front();
+ const std::vector &outlines = cut.second;
+ for (HI hi : outlines) {
+ if (is_behind(mesh.source(hi), cut_index) ||
+ is_behind(mesh.target(hi), cut_index))
+ break;
+ }
+ }
+
+ // remove flagged cuts
+ for (size_t i = del_cuts.size(); i > 0; --i) {
+ size_t index = i - 1;
+ if (del_cuts[index])
+ cuts.erase(cuts.begin() + index);
+ }
+}
+
+
+SurfaceCuts priv::create_surface_cuts(const CutAOIs &cuts,
+ const CutMesh &mesh,
+ const ReductionMap &reduction_map,
+ ConvertMap &convert_map)
+{
// initialize convert_map to MAX values
for (VI vi : mesh.vertices())
convert_map[vi] = std::numeric_limits::max();
- std::queue process;
-
- SurfaceCuts result;
- for (FI fi: mesh.faces()) {
- if (face_type_map[fi] != FaceType::inside) continue;
-
- faces.clear();
- outlines.clear();
-
- assert(process.empty());
- // Process queue of faces to separate to surface_cut
- process.push(fi);
- collect_surface_data(process, faces, outlines, face_type_map, mesh);
+ SurfaceCuts result;
+ for (const CutAOI &cut : cuts) {
+ const std::vector& faces = cut.first;
+ const std::vector &outlines = cut.second;
+ // convert_map could be used separately for each surface cut.
+ // But it is moore faster to use one memory allocation for them all.
SurfaceCut sc = create_index_triangle_set(faces, outlines.size(), mesh, reduction_map, convert_map);
// connect outlines
- sc.cut = create_cut(outlines, mesh, reduction_map, convert_map);
-
- // TODO: create vertex2contour map
-
+ sc.contours = create_cut(outlines, mesh, reduction_map, convert_map);
result.emplace_back(std::move(sc));
}
-
return result;
}
@@ -1065,5 +1367,3 @@ void priv::store(const SurfaceCuts &cut, const std::string &file_prefix) {
its_write_obj(c, file.c_str());
}
}
-
-#endif // ENABLE_NEW_CGAL
diff --git a/src/libslic3r/CutSurface.hpp b/src/libslic3r/CutSurface.hpp
index dc8400d65..64d46fdd9 100644
--- a/src/libslic3r/CutSurface.hpp
+++ b/src/libslic3r/CutSurface.hpp
@@ -11,29 +11,6 @@
namespace Slic3r{
-///
-/// Address of contour point in ExPolygon
-///
-struct ExPolygonPoint
-{
- // Index of Polygon in ExPolygon
- // 0 .. ExPolygon::contour
- // N .. ExPolygon::hole[N-1]
- size_t poly_id;
-
- // Index of point in Polygon
- size_t index;
-};
-
-///
-/// Address of contour point in ExPolygons
-///
-struct ExPolygonsPoint : public ExPolygonPoint
-{
- // Index of ExPolygon in ExPolygons
- size_t expoly_id;
-};
-
///
/// Represents cutted surface from object
/// Extend index triangle set by outlines
@@ -43,11 +20,11 @@ struct SurfaceCut : public indexed_triangle_set
// connected cutted surface
indexed_triangle_set mesh;
- // verticex index(index to mesh vertices)
+ // vertex indices(index to mesh vertices)
using Index = unsigned int;
- using CutType = std::vector>;
+ using CutContour = std::vector>;
// list of circulated open surface
- CutType cut;
+ CutContour contours;
// Conversion map from vertex index to contour point
// Could be used for filtration of surface cuts
@@ -78,5 +55,14 @@ SurfaceCuts cut_surface(const indexed_triangle_set &model,
const ExPolygons &shapes,
const Emboss::IProject &projection);
+///
+/// Create model from surface cuts by projection
+///
+/// Surfaces from model
+/// Way of emboss
+/// Mesh
+indexed_triangle_set cuts2model(const SurfaceCuts &cuts,
+ const Emboss::IProject &projection);
+
} // namespace Slic3r
#endif // slic3r_CutSurface_hpp_
diff --git a/src/libslic3r/Emboss.cpp b/src/libslic3r/Emboss.cpp
index e67bf4d8e..07be4e026 100644
--- a/src/libslic3r/Emboss.cpp
+++ b/src/libslic3r/Emboss.cpp
@@ -47,8 +47,7 @@ public:
bool Private::is_valid(const Emboss::FontFile &font, unsigned int index) {
if (font.data == nullptr) return false;
if (font.data->empty()) return false;
- if (font.count == 0) return false;
- if (index >= font.count) return false;
+ if (index >= font.infos.size()) return false;
return true;
}
@@ -512,9 +511,7 @@ std::unique_ptr Emboss::create_font_file(
infos.emplace_back(FontFile::Info{ascent, descent, linegap, units_per_em});
}
-
- return std::make_unique(
- std::move(data), collection_size, std::move(infos));
+ return std::make_unique(std::move(data), std::move(infos));
}
std::unique_ptr Emboss::create_font_file(const char *file_path)
@@ -693,7 +690,7 @@ void Emboss::apply_transformation(const FontProp &font_prop,
bool Emboss::is_italic(const FontFile &font, unsigned int font_index)
{
- if (font_index >= font.count) return false;
+ if (font_index >= font.infos.size()) return false;
std::optional font_info_opt = Private::load_font_info(font.data->data(), font_index);
if (!font_info_opt.has_value()) return false;
@@ -901,3 +898,17 @@ Transform3d Emboss::create_transformation_onto_surface(const Vec3f &position,
transform.rotate(up_rot);
return transform;
}
+
+
+// OrthoProject
+
+std::pair Emboss::OrthoProject::project(const Point &p) const {
+ Vec3d front(p.x(), p.y(), 0.);
+ Vec3f front_tr = (m_matrix * front).cast();
+ return std::make_pair(front_tr, project(front_tr));
+}
+
+Vec3f Emboss::OrthoProject::project(const Vec3f &point) const
+{
+ return point + m_direction;
+}
diff --git a/src/libslic3r/Emboss.hpp b/src/libslic3r/Emboss.hpp
index edc093b97..143890108 100644
--- a/src/libslic3r/Emboss.hpp
+++ b/src/libslic3r/Emboss.hpp
@@ -68,9 +68,6 @@ public:
// data are stored inside unique_ptr
std::unique_ptr> data;
- // count of fonts when data are collection of fonts
- unsigned int count;
-
struct Info
{
// vertical position is "scale*(ascent - descent + lineGap)"
@@ -83,26 +80,22 @@ public:
std::vector infos;
FontFile(std::unique_ptr> data,
- unsigned int count,
std::vector &&infos)
- : data(std::move(data))
- , count(count)
- , infos(std::move(infos))
+ : data(std::move(data)), infos(std::move(infos))
{
assert(this->data != nullptr);
assert(!this->data->empty());
- assert(count == this->infos.size());
}
+
bool operator==(const FontFile &other) const {
- if (count != other.count || data->size() != other.data->size())
+ if (data->size() != other.data->size())
return false;
//if(*data != *other.data) return false;
- for (unsigned int i = 0; i < count; i++)
+ for (size_t i = 0; i < infos.size(); i++)
if (infos[i].ascent != other.infos[i].ascent ||
infos[i].descent == other.infos[i].descent ||
infos[i].linegap == other.infos[i].linegap)
return false;
-
return true;
}
};
@@ -263,7 +256,19 @@ public:
Vec3f project(const Vec3f &point) const override{
return core->project(point);
}
+ };
+ class OrthoProject: public Emboss::IProject {
+ Transform3d m_matrix;
+ // size and direction of emboss for ortho projection
+ Vec3f m_direction;
+ public:
+ OrthoProject(Transform3d matrix, Vec3f direction)
+ : m_matrix(matrix), m_direction(direction)
+ {}
+ // Inherited via IProject
+ std::pair project(const Point &p) const override;
+ Vec3f project(const Vec3f &point) const override;
};
};
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
index 8c7483b12..e682a50de 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
@@ -271,8 +271,10 @@ bool GLGizmoEmboss::on_mouse_for_translate(const wxMouseEvent &mouse_event)
return false;
}
+ // Dragging starts out of window
+ if (!m_dragging_mouse_offset.has_value()) return false;
+
const Camera &camera = wxGetApp().plater()->get_camera();
- assert(m_dragging_mouse_offset.has_value());
Vec2d offseted_mouse = mouse_pos + *m_dragging_mouse_offset;
auto hit = m_raycast_manager.unproject(offseted_mouse, camera, &condition);
if (!hit.has_value()) {
@@ -910,6 +912,202 @@ void GLGizmoEmboss::select_stored_font_item()
m_stored_font_item = it->second;
}
+///
+/// choose valid source object for cut surface
+///
+/// Source model volume
+/// triangle set OR nullptr
+const indexed_triangle_set *get_source_object(const ModelVolume* mv) {
+ if (mv == nullptr) return nullptr;
+ if (!mv->text_configuration.has_value()) return nullptr;
+ const auto &volumes = mv->get_object()->volumes;
+ // no other volume in object
+ if (volumes.size() <= 1) return nullptr;
+
+ // Improve create object from part or use gl_volume
+ // Get first model part in object
+ for (const ModelVolume *v : volumes) {
+ if (v->id() == mv->id()) continue;
+ if (!v->is_model_part()) continue;
+ const TriangleMesh &tm = v->mesh();
+ if (tm.empty()) continue;
+ return &tm.its;
+ }
+
+ // No valid source volume in objct volumes
+ return nullptr;
+}
+
+///
+/// Choose valid source Volume to project on(cut surface from).
+///
+/// Volume with text
+/// ModelVolume to project on
+const ModelVolume *get_source_volume(const ModelVolume *text_volume)
+{
+ if (text_volume == nullptr) return nullptr;
+ if (!text_volume->text_configuration.has_value()) return nullptr;
+ const auto &volumes = text_volume->get_object()->volumes;
+ // no other volume in object
+ if (volumes.size() <= 1) return nullptr;
+
+ // Improve create object from part or use gl_volume
+ // Get first model part in object
+ 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;
+ return v;
+ }
+
+ // No valid source volume in objct volumes
+ return nullptr;
+}
+
+// search area range for cut surface
+struct SurfaceConfig
+{
+ // zero is after move on surface + depth move
+ float min = -10.f; // [in mm]
+ float max = 100.f;// [in mm]
+};
+static SurfaceConfig surface_cfg;
+
+double get_shape_scale(const FontProp &fp, const Emboss::FontFile &ff)
+{
+ const auto &cn = fp.collection_number;
+ unsigned int font_index = (cn.has_value()) ? *cn : 0;
+ int unit_per_em = ff.infos[font_index].unit_per_em;
+ double scale = fp.size_in_mm / unit_per_em;
+ // Shape is scaled for store point coordinate as integer
+ return scale * Emboss::SHAPE_SCALE;
+}
+
+///
+/// Create cut_projection for cut surface
+///
+/// Volume transformation in object
+/// Configuration of embossig
+/// Font file for size --> unit per em
+/// Bounding box of shape to center result volume
+/// Orthogonal cut_projection
+std::unique_ptr create_projection_for_cut(
+ Transform3d tr,
+ const TextConfiguration &tc,
+ const Emboss::FontFile &ff,
+ const BoundingBox shape_bb)
+{
+ double z_dir = -(surface_cfg.max - surface_cfg.min);
+ Vec3f dir = (tr * Vec3d(0., 0., z_dir)).cast();
+
+ tr.scale(get_shape_scale(tc.font_item.prop, ff));
+
+ // Text aligmnemnt to center 2D
+ Vec2d move = -(shape_bb.max + shape_bb.min).cast() / 2.;
+ tr.translate(Vec3d(move.x(), move.y(), -surface_cfg.min));
+
+ return std::make_unique(tr, dir);
+}
+
+#include "libslic3r/CutSurface.hpp"
+///
+/// Create tranformation for emboss
+///
+/// True .. raise, False .. engrave
+/// Text configuration
+/// Text voliume transformation inside object
+/// Cutted surfaces from model
+/// Projection
+static std::unique_ptr create_emboss_projection(
+ bool is_outside,
+ const TextConfiguration &tc,
+ Transform3d tr,
+ SurfaceCuts &cuts)
+{
+ // Offset of clossed side to model
+ const float surface_offset = 1e-3; // [in mm]
+
+ const FontProp &fp = tc.font_item.prop;
+ float front_move, back_move;
+ if (is_outside) {
+ front_move = fp.emboss;
+ back_move = -surface_offset;
+ } else {
+ front_move = surface_offset;
+ back_move = -fp.emboss;
+ }
+ Matrix3d rot = tr.rotation();
+
+ float z_dir = back_move - front_move;
+ Vec3f dir = (rot * Vec3d(0., 0., z_dir)).cast();
+
+ // move to front distance
+ Vec3f move = (rot * Vec3d(0., 0., front_move)).cast();
+ for (SurfaceCut &cut : cuts)
+ its_translate(cut, move);
+
+ // Transformation is not used
+ return std::make_unique(Transform3d::Identity(), dir);
+}
+
+void GLGizmoEmboss::use_surface() {
+ const ModelVolume *source = get_source_volume(m_volume);
+ if (source == nullptr) return;
+
+ auto ffc = m_font_manager.get_font().font_file_with_cache;
+ if (!ffc.has_value()) return;
+
+ const TextConfiguration &tc = *m_volume->text_configuration;
+ const char *text = tc.text.c_str();
+ const FontProp& fp = tc.font_item.prop;
+ ExPolygons shapes = Emboss::text2shapes(ffc, text, fp);
+
+ if (shapes.empty()) return;
+ if (shapes.front().contour.empty()) return;
+
+ BoundingBox bb = get_extents(shapes);
+
+ Transform3d input_tr = m_volume->get_matrix();
+ if (tc.fix_3mf_tr.has_value())
+ input_tr = input_tr * tc.fix_3mf_tr->inverse();
+ Transform3d cut_projection_tr = source->get_matrix().inverse() * input_tr;
+
+ const Emboss::FontFile &ff = *ffc.font_file;
+ auto cut_projection = create_projection_for_cut(cut_projection_tr, tc, ff, bb);
+ if (cut_projection == nullptr) return;
+
+ SurfaceCuts cuts = cut_surface(source->mesh().its, shapes, *cut_projection);
+ if (cuts.empty()) return;
+
+ bool is_outside = m_volume->is_model_part();
+ assert(is_outside || m_volume->is_negative_volume() || m_volume->is_modifier());
+
+ //Transform3d wanted_tr = ;
+
+ // NOTE! - It needs to translate cuts
+ Transform3d tr = source->get_matrix().inverse() * input_tr;
+ auto projection = create_emboss_projection(is_outside, tc, tr, cuts);
+ if (projection == nullptr) return;
+
+ indexed_triangle_set new_its = cuts2model(cuts, *projection);
+ its_write_obj(new_its, "C:/data/temp/projected.obj"); // only debug
+
+ TriangleMesh tm(std::move(new_its));
+ // center triangle mesh
+ Vec3d shift = tm.bounding_box().center();
+ tm.translate(-shift.cast());
+
+ Transform3d trafo = Transform3d::Identity();
+ trafo.translate(shift);
+ trafo = source->get_matrix() * trafo;
+ m_volume->set_transformation(trafo);
+ m_volume->set_mesh(std::move(tm));
+ m_volume->set_new_unique_id();
+ wxGetApp().plater()->canvas3D()->reload_scene(true);
+}
+
void GLGizmoEmboss::draw_window()
{
#ifdef ALLOW_DEBUG_MODE
@@ -955,6 +1153,17 @@ void GLGizmoEmboss::draw_window()
if (ImGui::Button(_u8L("Close").c_str())) close();
+ ImGui::SameLine();
+ if (ImGui::Button(_u8L("UseSurface").c_str()))
+ use_surface();
+
+ ImGui::SameLine();
+ ImGui::SetNextItemWidth(150);
+ ImGui::InputFloat("##min_cut", &surface_cfg.min);
+ ImGui::SameLine();
+ ImGui::SetNextItemWidth(150);
+ ImGui::InputFloat("##max_cut", &surface_cfg.max);
+
// Option to create text volume when reselecting volumes
m_imgui->disabled_begin(!exist_font_file);
if (m_volume == nullptr) {
@@ -1901,10 +2110,10 @@ void GLGizmoEmboss::draw_advanced()
", lineGap=" + std::to_string(font_info.linegap) +
", unitPerEm=" + std::to_string(font_info.unit_per_em) +
", cache(" + std::to_string(cache_size) + " glyphs)";
- if (font_file->count > 1) {
+ if (font_file->infos.size() > 1) {
unsigned int collection = font_prop.collection_number.has_value() ?
*font_prop.collection_number : 0;
- ff_property += ", collect=" + std::to_string(collection+1) + "/" + std::to_string(font_file->count);
+ ff_property += ", collect=" + std::to_string(collection+1) + "/" + std::to_string(font_file->infos.size());
}
m_imgui->text_colored(ImGuiWrapper::COL_GREY_DARK, ff_property);
#endif // SHOW_FONT_FILE_PROPERTY
@@ -2002,14 +2211,14 @@ void GLGizmoEmboss::draw_advanced()
}
// when more collection add selector
- if (font_file->count > 1) {
+ if (font_file->infos.size() > 1) {
ImGui::Text("%s", tr.collection.c_str());
ImGui::SameLine(m_gui_cfg->advanced_input_offset);
ImGui::SetNextItemWidth(m_gui_cfg->advanced_input_width);
unsigned int selected = font_prop.collection_number.has_value() ?
*font_prop.collection_number : 0;
if (ImGui::BeginCombo("## Font collection", std::to_string(selected).c_str())) {
- for (unsigned int i = 0; i < font_file->count; ++i) {
+ for (unsigned int i = 0; i < font_file->infos.size(); ++i) {
ImGui::PushID(1 << (10 + i));
bool is_selected = (i == selected);
if (ImGui::Selectable(std::to_string(i).c_str(), is_selected)) {
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
index c1de15f95..f3b9e7ca6 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoEmboss.hpp
@@ -112,6 +112,9 @@ private:
void do_translate(const Vec3d& relative_move);
void do_rotate(float relative_z_angle);
+ // TODO: only for developing - remove it
+ void use_surface();
+
///
/// Reversible input float with option to restor default value
/// TODO: make more general, static and move to ImGuiWrapper
diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp
index 542103d39..a31932d5e 100644
--- a/src/slic3r/GUI/Jobs/EmbossJob.cpp
+++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp
@@ -314,6 +314,7 @@ TriangleMesh priv::create_mesh(const char *text,
const auto &cn = font_prop.collection_number;
unsigned int font_index = (cn.has_value()) ? *cn : 0;
+ assert(font_index < font.font_file->infos.size());
int unit_per_em = font.font_file->infos[font_index].unit_per_em;
float scale = font_prop.size_in_mm / unit_per_em;
float depth = font_prop.emboss / scale;
diff --git a/tests/libslic3r/test_emboss.cpp b/tests/libslic3r/test_emboss.cpp
index 38f91c23e..b0c980784 100644
--- a/tests/libslic3r/test_emboss.cpp
+++ b/tests/libslic3r/test_emboss.cpp
@@ -296,7 +296,6 @@ TEST_CASE("Italic check", "[Emboss]")
}
#endif // not __APPLE__
-#if ENABLE_NEW_CGAL
#include "libslic3r/CutSurface.hpp"
TEST_CASE("Cut surface", "[]")
{
@@ -304,6 +303,7 @@ TEST_CASE("Cut surface", "[]")
char letter = '%';
float flatness = 2.;
unsigned int font_index = 0; // collection
+ float z_depth = 50.f; // projection size
auto font = Emboss::create_font_file(font_path.c_str());
REQUIRE(font != nullptr);
@@ -315,8 +315,10 @@ TEST_CASE("Cut surface", "[]")
ExPolygons shape = glyph->shape;
REQUIRE(!shape.empty());
- float z_depth = 50.f;
- Emboss::ProjectZ projection(z_depth);
+ Transform3d tr = Transform3d::Identity();
+ tr.translate(Vec3d(0., 0., z_depth));
+ tr.scale(Emboss::SHAPE_SCALE);
+ Emboss::OrthoProject cut_projection(tr, Vec3f(0.f, 0.f, -50));
auto object = its_make_cube(782 - 49 + 50, 724 + 10 + 50, 5);
its_translate(object, Vec3f(49 - 25, -10 - 25, 2.5));
@@ -324,8 +326,16 @@ TEST_CASE("Cut surface", "[]")
its_translate(cube2, Vec3f(100, -40, 40));
its_merge(object, std::move(cube2));
- auto surfaces = cut_surface(object, shape, projection);
+ auto surfaces = cut_surface(object, shape, cut_projection);
CHECK(!surfaces.empty());
+
+ Emboss::OrthoProject projection(Transform3d::Identity(), Vec3f(0.f, 0.f, -10.f));
+ for (auto &surface : surfaces)
+ its_translate(surface, Vec3f(0.f, 0.f, 10));
+
+ indexed_triangle_set its = cuts2model(surfaces, projection);
+ CHECK(!its.empty());
+ its_write_obj(its, "C:/data/temp/projected.obj");
}
@@ -1080,4 +1090,3 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
// REQUIRE(!MeshBoolean::cgal::does_self_intersect(cube));
}
-#endif // ENABLE_NEW_CGAL