WIP: cut surface of model
update emboss icons to not be soo huge - pixel preccisse
This commit is contained in:
parent
7ec422d12f
commit
a1d7040902
@ -1,4 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16px" height="16px">
|
||||
<path fill="#808080" d="M 2,1 V 4 H 6.5 V 14.998047 L 9.5,15 V 4 H 14 V 1 Z m 0.500047,0.5 h 2.06836 L 4.359422,2.2460937 3.191453,2.5585937 2.500047,1.8828125 Z m 3.107422,0 h 1.632813 l 0.394531,0.3867187 -0.3125,1.1191406 -1.166016,0.3125 -0.830078,-0.8125 z m 5.251953,0 H 12.50786 L 12.4727,1.625 11.306685,1.9375 Z M 13.5,1.6464843 V 2 L 13.43754,1.8847657 Z m -3.730421,0.1855469 0.830078,0.8125 L 10.386766,3.5 H 9 V 3.9669613 L 8.287152,3.2636716 8.599652,2.1445309 Z M 12.730516,2.5917969 13.5,3.3496093 V 3.5000001 H 11.423875 L 11.5645,2.904297 Z M 4.619188,3.2109375 5,3.5 H 3.5 Z m 2.960937,0.7597656 0.830078,0.8125 -0.3125,1.1191406 L 7,6.1957387 7.0002733,4.1260205 Z M 8.357469,6.8691406 9,7.5 V 8.3470521 L 8.875047,8.8007811 7.709032,9.1132811 7,8.4263586 v -0.574671 l 0.191453,-0.670047 z m 0.641742,5.0046944 -0.514789,0.137887 -0.830078,-0.8125 0.3125,-1.119141 1.033945,-0.2773181 z M 7,11.963263 7.779344,12.71875 7.466844,13.837891 7,13.960601 Z M 9,14.5 H 8.841844 L 8.431688,14.095703 8.744188,12.976562 9,12.908257 Z" />
|
||||
<circle fill="#ed6b21" cx="3.5" cy="12.5" r="2.5" />
|
||||
<path fill="#808080" d="m 2,1 v 4 h 4 v 10 h 4 v -10 h 4 v -4 z m 1.121094,1 h 1.414062 l 2,2 H 5.115235 Z m 2.121094,0 h 1.414062 l 2.34375,2.34375 v 1.4140625 z m 2.121093,0 H 8.771485 L 10.769532,3.998 H 9.355469 Z m 2.121094,0 h 1.414063 L 12.890625,4 h -1.414062 z m 2.121094,0 H 13 L 13,3.3964375 Z M 3,2.59175 4.408203,4 H 3 Z M 7,4.4706562 9,6.4647969 V 7.8788594 L 7,5.8847188 Z M 7,6.59175 9,8.5858906 V 10 L 7,8 Z m 0,2.1210938 2,2 v 1.414063 L 7,10.126906 Z m 0,2.1210942 2,2 V 14 L 8.7611513,14 7,12.248 Z M 7,13 8,14 H 7 Z" />
|
||||
<path fill="#ed6b21" d="M 3.5 10 A 2.5 2.5 0 0 0 1 12.5 A 2.5 2.5 0 0 0 3.5 15 A 2.5 2.5 0 0 0 6 12.5 A 2.5 2.5 0 0 0 3.5 10 z M 3.6445312 11 A 1.5 1.5 0 0 1 5 12.355469 L 3.6445312 11 z M 3.0117188 11 L 4.9179688 13 A 1.5 1.5 0 0 1 4.2890625 13.773438 L 2.2246094 11.708984 A 1.5 1.5 0 0 1 3 11 z M 2 12.21875 L 3.78125 14 A 1.5 1.5 0 0 1 3.5 14 A 1.5 1.5 0 0 1 2 12.5 A 1.5 1.5 0 0 1 2 12.21875 z " />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.0 KiB |
@ -1,4 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16px" height="16px">
|
||||
<path fill="#808080" d="m 2,1 v 2 h 3.5 l 1,1 v 10 h 2 V 3 H 13 V 1 Z m 11.5,0 v 2.1 l 0.5,0.5 V 1 Z M 13.1,3.5 H 9.3 l 0.5,0.5 h 3.8 z m -7.8,0 H 2 V 4 H 5.8 Z M 9,4 v 10.3 l 0.5,0.5 V 4.4 Z M 8.6,14.5 H 6.5 V 15 h 2.6 z"/>
|
||||
<path fill="#ed6b21" d="m 1.5,12 c -0.5,0 -1,0 -1,0.5 0,0.5 0.5,0.5 1,0.5 H 5 C 5.5,13 6,13 6,12.5 6,12 5.5,12 5,12 Z"/>
|
||||
<path fill="#808080" d="m 2,1 v 4 h 4 v 10 h 4 V 5 h 4 V 1 Z m 1,1 h 10 v 1.6582034 l -0.5,-0.5 V 2.5 H 12 V 3 H 8 V 13 H 7.5 v 0.5 h 0.646484 l 0.5,0.5 H 7 V 4 L 6,3 H 3.5 v 0.5 h 2.292969 l 0.5,0.5 H 3 Z m 5.853516,1.511719 h 3.292968 L 12.646484,4 H 9.353516 Z M 8.5,3.8769534 9,4.3652346 V 13.658203 L 8.5,13.13086 Z" />
|
||||
<path fill="#ed6b21" d="M 1,12 C 0.5,12 0,12 0,12.5 0,13 0.5,13 1,13 h 3.5 c 0.5,0 1,0 1,-0.5 C 5.5,12 5,12 4.5,12 Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 441 B After Width: | Height: | Size: 541 B |
@ -1,4 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16px" height="16px">
|
||||
<path fill="#808080" d="m 2.35,1 0.5,0.5 H 14.5 L 14,1 Z M 2,1.35 V 4 L 2.5,4.5 V 1.86 Z M 3,2 3,5 h 4.5 v 11 h 3 V 5 H 15 V 2 Z M 6.5,5.5 V 15 L 7,15.5 v -10 z"/>
|
||||
<path fill="#ed6b21" d="M 2.5,10 C 2,10 2,10.5 2,11 v 1 L 1,12 C 0.5,12 0,12 0,12.5 0,13 0.5,13 1,13 l 1,0 v 1 C 2,14.5 2,15 2.5,15 3,15 3,14.5 3,14 V 13 H 4 C 4.5,13 5,13 5,12.5 5,12 4.5,12 4,12 H 3 V 11 C 3,10.5 3,10 2.5,10 Z"/>
|
||||
<path fill="#808080" d="m 2,0 v 4 l 1,1 h 4 v 9.285156 L 6.5,13.770689 V 5.5 H 6 V 14 l 1,1 h 4 V 5 h 4 V 1 L 14,0 Z M 2.841797,0.5 H 13.783911 L 14.289061,1 H 3.351563 Z M 2.5,0.859375 3,1.363281 v 2.933594 l -0.5,-0.5 z M 4,2 H 14 V 4 H 10 V 14 H 8 V 4 H 4 Z" />
|
||||
<path fill="#ed6b21" d="M 2.5,10 C 2,10 2,10.5 2,11 v 1 L 1,12 C 0.5,12 0,12 0,12.5 0,13 0.5,13 1,13 l 1,0 v 1 C 2,14.5 2,15 2.5,15 3,15 3,14.5 3,14 V 13 H 4 C 4.5,13 5,13 5,12.5 5,12 4.5,12 4,12 H 3 V 11 C 3,10.5 3,10 2.5,10 Z" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 490 B After Width: | Height: | Size: 592 B |
@ -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<SurfaceCut::Index>(
|
||||
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<SurfaceCut::Index> &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<SurfaceCut::Index> &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 <CGAL/Polygon_mesh_processing/corefinement.h>
|
||||
#include <CGAL/Exact_integer.h>
|
||||
#include <CGAL/Surface_mesh.h>
|
||||
@ -173,6 +162,9 @@ 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<const IntersectingElement*> intersections;
|
||||
@ -214,6 +206,7 @@ struct Visitor {
|
||||
bool is_source_coplanar);
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="i_id">Order number of intersection point</param>
|
||||
@ -264,14 +257,29 @@ void set_face_type(FaceTypeMap &face_type_map,
|
||||
const Project &project,
|
||||
const CutMesh &shape_mesh);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Check orientation(normal direction) of face on mesh
|
||||
/// </summary>
|
||||
/// <param name="fi">Face index to inspect</param>
|
||||
/// <param name="mesh">Mesh contained fi</param>
|
||||
/// <param name="projection">Define direction of projection</param>
|
||||
/// <returns>TRUE for cutted face otherwise FALSE</returns>
|
||||
bool is_toward_projection(FI fi,
|
||||
const CutMesh &mesh,
|
||||
const Project &projection);
|
||||
|
||||
/// <summary>
|
||||
/// Change FaceType from not_constrained to inside
|
||||
/// For neighbor(or neighbor of neighbor of ...) of inside triangles.
|
||||
/// Process only not_constrained triangles
|
||||
/// </summary>
|
||||
/// <param name="mesh">Corefined mesh</param>
|
||||
/// <param name="projection">Projection from 2d to 3d</param>
|
||||
/// <param name="face_type_map">In/Out map with faces type</param>
|
||||
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<VI, VI>;
|
||||
/// <summary>
|
||||
@ -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<FI>, std::vector<HI>>;
|
||||
using CutAOIs = std::vector<CutAOI>;
|
||||
|
||||
/// <summary>
|
||||
/// Create areas from mesh surface
|
||||
/// </summary>
|
||||
/// <param name="mesh">Model</param>
|
||||
/// <param name="shapes">Cutted shapes</param>
|
||||
/// <param name="face_type_map">Define Triangles of interest.
|
||||
/// Edge between inside / outside.
|
||||
/// NOTE: Not const because it need to flag proccessed faces</param>
|
||||
/// <returns>Areas of interest from mesh</returns>
|
||||
CutAOIs create_cut_area_of_interests(const CutMesh &mesh,
|
||||
const ExPolygons &shapes,
|
||||
FaceTypeMap &face_type_map);
|
||||
|
||||
/// <summary>
|
||||
/// Filter out cuts which are behind another.
|
||||
/// Prevent overlapping embossed shape in space.
|
||||
/// </summary>
|
||||
/// <param name="cuts">AOIs</param>
|
||||
/// <param name="mesh">triangle model</param>
|
||||
/// <param name="shapes">2d cutted shapes</param>
|
||||
/// <param name="projection">Projection from 2d to 3d</param>
|
||||
/// <param name="vert_shape_map">Identify source of intersection</param>
|
||||
void filter_cuts(CutAOIs &cuts,
|
||||
const CutMesh &mesh,
|
||||
const ExPolygons &shapes,
|
||||
const Project &projection,
|
||||
const VertexShapeMap &vert_shape_map);
|
||||
|
||||
using ConvertMap = CutMesh::Property_map<VI, SurfaceCut::Index>;
|
||||
/// <summary>
|
||||
/// Create surface cuts from mesh model
|
||||
@ -303,12 +343,12 @@ using ConvertMap = CutMesh::Property_map<VI, SurfaceCut::Index>;
|
||||
/// <param name="convert_map">Used only inside function.
|
||||
/// Store conversion from mesh to result.</param>
|
||||
/// <returns>Created surface cuts</returns>
|
||||
SurfaceCuts create_surface_cut(const CutMesh &mesh,
|
||||
const ExPolygons &shapes,
|
||||
SurfaceCuts create_surface_cuts(const CutAOIs &cutAOIs,
|
||||
const CutMesh &mesh,
|
||||
const ReductionMap &reduction_map,
|
||||
FaceTypeMap &face_type_map,
|
||||
ConvertMap &convert_map);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Collect connected inside faces
|
||||
/// Collect outline half edges
|
||||
@ -349,7 +389,7 @@ SurfaceCut create_index_triangle_set(const std::vector<FI> &faces,
|
||||
/// <param name="reduction_map">Reduction of vertices</param>
|
||||
/// <param name="v2v">Map to convert CGAL vertex to its::vertex</param>
|
||||
/// <returns>Cuts - outlines of surface</returns>
|
||||
SurfaceCut::CutType create_cut(const std::vector<HI> &outlines,
|
||||
SurfaceCut::CutContour create_cut(const std::vector<HI> &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);
|
||||
CGAL::IO::write_OFF("C:/data/temp/shape.off", cgal_shape); // only debug
|
||||
|
||||
auto edge_shape_map = cgal_shape.property_map<priv::EI, priv::IntersectingElement>(edge_shape_map_name).first;
|
||||
auto face_shape_map = cgal_shape.property_map<priv::FI, priv::IntersectingElement>(face_shape_map_name).first;
|
||||
@ -383,8 +425,10 @@ SurfaceCuts Slic3r::cut_surface(const indexed_triangle_set &model,
|
||||
// pointer to edge or face shape_map
|
||||
priv::VertexShapeMap vert_shape_map = cgal_model.add_property_map<priv::VI, const priv::IntersectingElement*>(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<priv::FI, priv::FaceType>(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<priv::VI, SurfaceCut::Index>(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)
|
||||
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<uint32_t>::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<uint32_t>::max());
|
||||
// intersection was not filled in function intersection_point_detected
|
||||
//assert(intersection_ptr->point_index != std::numeric_limits<uint32_t>::max());
|
||||
vert_shape_map[v] = intersection_ptr;
|
||||
}
|
||||
|
||||
@ -895,14 +1052,14 @@ SurfaceCut priv::create_index_triangle_set(const std::vector<FI> &faces,
|
||||
}
|
||||
|
||||
|
||||
SurfaceCut::CutType priv::create_cut(const std::vector<HI> &outlines,
|
||||
SurfaceCut::CutContour priv::create_cut(const std::vector<HI> &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<HI> &outlines,
|
||||
return cut;
|
||||
}
|
||||
|
||||
SurfaceCuts priv::create_surface_cut(const CutMesh &mesh,
|
||||
priv::CutAOIs priv::create_cut_area_of_interests(const CutMesh &mesh,
|
||||
const ExPolygons &shapes,
|
||||
const ReductionMap &reduction_map,
|
||||
FaceTypeMap &face_type_map,
|
||||
ConvertMap &convert_map)
|
||||
FaceTypeMap &face_type_map)
|
||||
{
|
||||
// faces from one surface cut
|
||||
std::vector<FI> faces;
|
||||
// IMPROVE: Size can't be greater but it is too big.
|
||||
faces.reserve(mesh.faces().size());
|
||||
std::vector<HI> 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;
|
||||
|
||||
// initialize convert_map to MAX values
|
||||
for (VI vi : mesh.vertices())
|
||||
convert_map[vi] = std::numeric_limits<SurfaceCut::Index>::max();
|
||||
CutAOIs result;
|
||||
result.reserve(cuts_per_model);
|
||||
|
||||
// It is faster to use one queue for all cuts
|
||||
std::queue<FI> process;
|
||||
|
||||
SurfaceCuts result;
|
||||
for (FI fi: mesh.faces()) {
|
||||
for (FI fi : mesh.faces()) {
|
||||
if (face_type_map[fi] != FaceType::inside) continue;
|
||||
|
||||
faces.clear();
|
||||
outlines.clear();
|
||||
CutAOI cut;
|
||||
std::vector<FI> &faces = cut.first;
|
||||
std::vector<HI> &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<uint32_t>::max());
|
||||
assert(intersection.point_index != std::numeric_limits<uint32_t>::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<size_t>::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<CutIndex> indices(count);
|
||||
|
||||
// flags which cut is not first
|
||||
std::vector<bool> 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<uint32_t>::max());
|
||||
assert(i->vertex_base%2 == 0);
|
||||
assert(i->point_index != std::numeric_limits<uint32_t>::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<size_t>::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<HI> &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<SurfaceCut::Index>::max();
|
||||
|
||||
SurfaceCuts result;
|
||||
for (const CutAOI &cut : cuts) {
|
||||
const std::vector<FI>& faces = cut.first;
|
||||
const std::vector<HI> &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
|
||||
|
@ -11,29 +11,6 @@
|
||||
|
||||
namespace Slic3r{
|
||||
|
||||
/// <summary>
|
||||
/// Address of contour point in ExPolygon
|
||||
/// </summary>
|
||||
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;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Address of contour point in ExPolygons
|
||||
/// </summary>
|
||||
struct ExPolygonsPoint : public ExPolygonPoint
|
||||
{
|
||||
// Index of ExPolygon in ExPolygons
|
||||
size_t expoly_id;
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 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<std::vector<Index>>;
|
||||
using CutContour = std::vector<std::vector<Index>>;
|
||||
// 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);
|
||||
|
||||
/// <summary>
|
||||
/// Create model from surface cuts by projection
|
||||
/// </summary>
|
||||
/// <param name="cuts">Surfaces from model</param>
|
||||
/// <param name="projection">Way of emboss</param>
|
||||
/// <returns>Mesh</returns>
|
||||
indexed_triangle_set cuts2model(const SurfaceCuts &cuts,
|
||||
const Emboss::IProject &projection);
|
||||
|
||||
} // namespace Slic3r
|
||||
#endif // slic3r_CutSurface_hpp_
|
||||
|
@ -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::FontFile> Emboss::create_font_file(
|
||||
|
||||
infos.emplace_back(FontFile::Info{ascent, descent, linegap, units_per_em});
|
||||
}
|
||||
|
||||
return std::make_unique<Emboss::FontFile>(
|
||||
std::move(data), collection_size, std::move(infos));
|
||||
return std::make_unique<Emboss::FontFile>(std::move(data), std::move(infos));
|
||||
}
|
||||
|
||||
std::unique_ptr<Emboss::FontFile> 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<stbtt_fontinfo> 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<Vec3f, Vec3f> Emboss::OrthoProject::project(const Point &p) const {
|
||||
Vec3d front(p.x(), p.y(), 0.);
|
||||
Vec3f front_tr = (m_matrix * front).cast<float>();
|
||||
return std::make_pair(front_tr, project(front_tr));
|
||||
}
|
||||
|
||||
Vec3f Emboss::OrthoProject::project(const Vec3f &point) const
|
||||
{
|
||||
return point + m_direction;
|
||||
}
|
||||
|
@ -68,9 +68,6 @@ public:
|
||||
// data are stored inside unique_ptr
|
||||
std::unique_ptr<std::vector<unsigned char>> 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<Info> infos;
|
||||
|
||||
FontFile(std::unique_ptr<std::vector<unsigned char>> data,
|
||||
unsigned int count,
|
||||
std::vector<Info> &&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<Vec3f, Vec3f> project(const Point &p) const override;
|
||||
Vec3f project(const Vec3f &point) const override;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// choose valid source object for cut surface
|
||||
/// </summary>
|
||||
/// <param name="text_volume">Source model volume</param>
|
||||
/// <returns>triangle set OR nullptr</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Choose valid source Volume to project on(cut surface from).
|
||||
/// </summary>
|
||||
/// <param name="text_volume">Volume with text</param>
|
||||
/// <returns>ModelVolume to project on</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create cut_projection for cut surface
|
||||
/// </summary>
|
||||
/// <param name="tr">Volume transformation in object</param>
|
||||
/// <param name="tc">Configuration of embossig</param>
|
||||
/// <param name="ff">Font file for size --> unit per em</param>
|
||||
/// <param name="shape_bb">Bounding box of shape to center result volume</param>
|
||||
/// <returns>Orthogonal cut_projection</returns>
|
||||
std::unique_ptr<Emboss::IProject> 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<float>();
|
||||
|
||||
tr.scale(get_shape_scale(tc.font_item.prop, ff));
|
||||
|
||||
// Text aligmnemnt to center 2D
|
||||
Vec2d move = -(shape_bb.max + shape_bb.min).cast<double>() / 2.;
|
||||
tr.translate(Vec3d(move.x(), move.y(), -surface_cfg.min));
|
||||
|
||||
return std::make_unique<Emboss::OrthoProject>(tr, dir);
|
||||
}
|
||||
|
||||
#include "libslic3r/CutSurface.hpp"
|
||||
/// <summary>
|
||||
/// Create tranformation for emboss
|
||||
/// </summary>
|
||||
/// <param name="is_outside">True .. raise, False .. engrave</param>
|
||||
/// <param name="tc">Text configuration</param>
|
||||
/// <param name="tr">Text voliume transformation inside object</param>
|
||||
/// <param name="cuts">Cutted surfaces from model</param>
|
||||
/// <returns>Projection</returns>
|
||||
static std::unique_ptr<Emboss::IProject> 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<float>();
|
||||
|
||||
// move to front distance
|
||||
Vec3f move = (rot * Vec3d(0., 0., front_move)).cast<float>();
|
||||
for (SurfaceCut &cut : cuts)
|
||||
its_translate(cut, move);
|
||||
|
||||
// Transformation is not used
|
||||
return std::make_unique<Emboss::OrthoProject>(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<float>());
|
||||
|
||||
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)) {
|
||||
|
@ -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();
|
||||
|
||||
/// <summary>
|
||||
/// Reversible input float with option to restor default value
|
||||
/// TODO: make more general, static and move to ImGuiWrapper
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user