Fix of flood fill on edge
This commit is contained in:
parent
d0dd074937
commit
51b103885c
3 changed files with 51 additions and 49 deletions
|
@ -692,10 +692,10 @@ priv::CutMesh priv::to_cgal(const ExPolygons &shapes,
|
||||||
bool is_last = size_t(i + 2) >= indices.size();
|
bool is_last = size_t(i + 2) >= indices.size();
|
||||||
int32_t j = is_last ? 0 : (i + 2);
|
int32_t j = is_last ? 0 : (i + 2);
|
||||||
|
|
||||||
auto fi1 = result.add_face(indices[i], indices[i + 1], indices[j]);
|
auto fi1 = result.add_face(indices[i], indices[j], indices[i + 1]);
|
||||||
auto ei1 = find_edge(fi1, indices[i], indices[i + 1]);
|
auto ei1 = find_edge(fi1, indices[i + 1], indices[i]);
|
||||||
auto ei2 = find_edge(fi1, indices[i + 1], indices[j]);
|
auto ei2 = find_edge(fi1, indices[j], indices[i + 1]);
|
||||||
auto fi2 = result.add_face(indices[j], indices[i + 1], indices[j + 1]);
|
auto fi2 = result.add_face(indices[j], indices[j + 1], indices[i + 1]);
|
||||||
uint32_t vertex_base = static_cast<uint32_t>(num_vertices_old);
|
uint32_t vertex_base = static_cast<uint32_t>(num_vertices_old);
|
||||||
IntersectingElement element {vertex_base, contour_index, (unsigned char)IntersectingElement::Type::undefined};
|
IntersectingElement element {vertex_base, contour_index, (unsigned char)IntersectingElement::Type::undefined};
|
||||||
if (is_first) element.set_is_first();
|
if (is_first) element.set_is_first();
|
||||||
|
@ -786,7 +786,7 @@ void priv::set_face_type(FaceTypeMap &face_type_map,
|
||||||
shape_mesh.point(VI(j)),
|
shape_mesh.point(VI(j)),
|
||||||
shape_mesh.point(VI(i + 1)),
|
shape_mesh.point(VI(i + 1)),
|
||||||
shape_mesh.point(VI(j + 1)), p);
|
shape_mesh.point(VI(j + 1)), p);
|
||||||
is_inside = abcp == CGAL::NEGATIVE;
|
is_inside = abcp == CGAL::POSITIVE;
|
||||||
} else if (i_from < i_to || (i_from == i_to && type_from < type_to)) {
|
} else if (i_from < i_to || (i_from == i_to && type_from < type_to)) {
|
||||||
// TODO: check that it is continous indices of contour
|
// TODO: check that it is continous indices of contour
|
||||||
bool is_last = shape_from.is_first() && shape_to.is_last() &&
|
bool is_last = shape_from.is_first() && shape_to.is_last() &&
|
||||||
|
@ -819,17 +819,18 @@ bool priv::is_toward_projection(FI fi,
|
||||||
const CutMesh &mesh,
|
const CutMesh &mesh,
|
||||||
const Project &projection)
|
const Project &projection)
|
||||||
{
|
{
|
||||||
|
using P3 = CGAL::Epick::Point_3;
|
||||||
HI hi = mesh.halfedge(fi);
|
HI hi = mesh.halfedge(fi);
|
||||||
const auto &a = mesh.point(mesh.source(hi));
|
const P3 &a = mesh.point(mesh.source(hi));
|
||||||
const auto &b = mesh.point(mesh.target(hi));
|
const P3 &b = mesh.point(mesh.target(hi));
|
||||||
const auto &c = mesh.point(mesh.target(mesh.next(hi)));
|
const P3 &c = mesh.point(mesh.target(mesh.next(hi)));
|
||||||
|
|
||||||
Vec3f a_(a.x(), a.y(), a.z());
|
Vec3f a_(a.x(), a.y(), a.z());
|
||||||
Vec3f p_ = projection.project(a_);
|
Vec3f p_ = projection.project(a_);
|
||||||
|
|
||||||
CGAL::Epick::Point_3 p{p_.x(), p_.y(), p_.z()};
|
P3 p{p_.x(), p_.y(), p_.z()};
|
||||||
|
|
||||||
return CGAL::orientation(a, b, c, p) == CGAL::NEGATIVE;
|
return CGAL::orientation(a, b, c, p) == CGAL::POSITIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -837,15 +838,16 @@ void priv::flood_fill_inner(const CutMesh &mesh,
|
||||||
const Project &projection,
|
const Project &projection,
|
||||||
FaceTypeMap &face_type_map)
|
FaceTypeMap &face_type_map)
|
||||||
{
|
{
|
||||||
for (FI fi : mesh.faces()) {
|
std::vector<FI> process;
|
||||||
if (face_type_map[fi] != FaceType::not_constrained) continue;
|
// guess count of connected not constrained triangles
|
||||||
|
size_t guess_size = 128;
|
||||||
|
process.reserve(guess_size);
|
||||||
|
|
||||||
// check if neighbor(one of three in triangle) has type inside
|
// check if neighbor(one of three in triangle) has type inside
|
||||||
bool has_inside_neighbor = false;
|
auto has_inside_neighbor = [&mesh, &face_type_map](FI fi) {
|
||||||
HI hi = mesh.halfedge(fi);
|
HI hi = mesh.halfedge(fi);
|
||||||
HI hi_end = hi;
|
HI hi_end = hi;
|
||||||
// list of other not constrained neighbors
|
// loop over 3 half edges of face
|
||||||
std::queue<FI> queue;
|
|
||||||
do {
|
do {
|
||||||
HI hi_opposite = mesh.opposite(hi);
|
HI hi_opposite = mesh.opposite(hi);
|
||||||
// open edge doesn't have opposit half edge
|
// open edge doesn't have opposit half edge
|
||||||
|
@ -854,22 +856,25 @@ void priv::flood_fill_inner(const CutMesh &mesh,
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
FI fi_opposite = mesh.face(hi_opposite);
|
FI fi_opposite = mesh.face(hi_opposite);
|
||||||
FaceType side = face_type_map[fi_opposite];
|
if (face_type_map[fi_opposite] == FaceType::inside) return true;
|
||||||
if (side == FaceType::inside) {
|
|
||||||
has_inside_neighbor = true;
|
|
||||||
} else if (side == FaceType::not_constrained) {
|
|
||||||
queue.emplace(fi_opposite);
|
|
||||||
}
|
|
||||||
hi = mesh.next(hi);
|
hi = mesh.next(hi);
|
||||||
} while (hi != hi_end);
|
} while (hi != hi_end);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (FI fi : mesh.faces()) {
|
||||||
|
if (face_type_map[fi] != FaceType::not_constrained) continue;
|
||||||
|
if (!has_inside_neighbor(fi)) continue;
|
||||||
|
if (!is_toward_projection(fi, mesh, projection)) {
|
||||||
|
face_type_map[fi] = FaceType::outside;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
assert(process.empty());
|
||||||
|
process.push_back(fi);
|
||||||
|
|
||||||
if (!has_inside_neighbor) continue;
|
while (!process.empty()) {
|
||||||
|
FI fi = process.back();
|
||||||
face_type_map[fi] = FaceType::inside;
|
process.pop_back();
|
||||||
while (!queue.empty()) {
|
|
||||||
FI fi = queue.front();
|
|
||||||
queue.pop();
|
|
||||||
// Do not fill twice
|
// Do not fill twice
|
||||||
if (face_type_map[fi] == FaceType::inside) continue;
|
if (face_type_map[fi] == FaceType::inside) continue;
|
||||||
face_type_map[fi] = FaceType::inside;
|
face_type_map[fi] = FaceType::inside;
|
||||||
|
@ -888,7 +893,7 @@ void priv::flood_fill_inner(const CutMesh &mesh,
|
||||||
FaceType &side = face_type_map[fi_opposite];
|
FaceType &side = face_type_map[fi_opposite];
|
||||||
if (side == FaceType::not_constrained) {
|
if (side == FaceType::not_constrained) {
|
||||||
if (is_toward_projection(fi_opposite, mesh, projection)) {
|
if (is_toward_projection(fi_opposite, mesh, projection)) {
|
||||||
queue.emplace(fi_opposite);
|
process.push_back(fi_opposite);
|
||||||
} else {
|
} else {
|
||||||
// Is in opposit direction
|
// Is in opposit direction
|
||||||
side = FaceType::outside;
|
side = FaceType::outside;
|
||||||
|
@ -1006,13 +1011,13 @@ void priv::create_reduce_map(ReductionMap &reduction_map,
|
||||||
const FaceTypeMap &face_type_map,
|
const FaceTypeMap &face_type_map,
|
||||||
const VertexShapeMap &vert_shape_map)
|
const VertexShapeMap &vert_shape_map)
|
||||||
{
|
{
|
||||||
// IMPROVE: find better way to initialize or try use std::map
|
// IMPROVE: find better way to initialize
|
||||||
// initialize reduction map
|
// initialize reduction map
|
||||||
for (VI reduction_from : mesh.vertices())
|
for (VI reduction_from : mesh.vertices())
|
||||||
reduction_map[reduction_from] = reduction_from;
|
reduction_map[reduction_from] = reduction_from;
|
||||||
|
|
||||||
// check if vertex was made by edge_2 which is diagonal of quad
|
// check if vertex was made by edge_2 which is diagonal of quad
|
||||||
auto is_reducible_vertex = [&vert_shape_map, &mesh](VI reduction_from) -> bool {
|
auto is_reducible_vertex = [&vert_shape_map](VI reduction_from) -> bool {
|
||||||
const IntersectingElement *ie = vert_shape_map[reduction_from];
|
const IntersectingElement *ie = vert_shape_map[reduction_from];
|
||||||
if (ie == nullptr) return false;
|
if (ie == nullptr) return false;
|
||||||
IntersectingElement::Type type = ie->get_type();
|
IntersectingElement::Type type = ie->get_type();
|
||||||
|
@ -1023,9 +1028,9 @@ void priv::create_reduce_map(ReductionMap &reduction_map,
|
||||||
/// Append reduction or change existing one.
|
/// Append reduction or change existing one.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="hi">HalEdge between outside and inside face.
|
/// <param name="hi">HalEdge between outside and inside face.
|
||||||
/// Target vertex will be reduced
|
/// Target vertex will be reduced, source vertex left</param>
|
||||||
/// Source vertex left</param>
|
/// [[maybe_unused]] &face_type_map, &is_reducible_vertex are need only in debug
|
||||||
auto add_reduction = [&reduction_map, &mesh, &is_reducible_vertex, &face_type_map]
|
auto add_reduction = [&] //&reduction_map, &mesh, &face_type_map, &is_reducible_vertex
|
||||||
(HI hi) {
|
(HI hi) {
|
||||||
VI erase = mesh.target(hi);
|
VI erase = mesh.target(hi);
|
||||||
VI left = mesh.source(hi);
|
VI left = mesh.source(hi);
|
||||||
|
@ -1055,9 +1060,6 @@ void priv::create_reduce_map(ReductionMap &reduction_map,
|
||||||
do {
|
do {
|
||||||
VI reduction_from = mesh.target(hi);
|
VI reduction_from = mesh.target(hi);
|
||||||
if (is_reducible_vertex(reduction_from)) {
|
if (is_reducible_vertex(reduction_from)) {
|
||||||
// reducible vertex
|
|
||||||
VI vi_from = mesh.target(hi);
|
|
||||||
|
|
||||||
// halfedges connected with reduction_from
|
// halfedges connected with reduction_from
|
||||||
HI hi1 = hi;
|
HI hi1 = hi;
|
||||||
HI hi2 = mesh.next(hi);
|
HI hi2 = mesh.next(hi);
|
||||||
|
@ -1100,7 +1102,6 @@ SurfaceCut priv::create_index_triangle_set(const std::vector<FI> &faces,
|
||||||
bool exist_reduction = false;
|
bool exist_reduction = false;
|
||||||
do {
|
do {
|
||||||
VI vi = mesh.source(hi);
|
VI vi = mesh.source(hi);
|
||||||
|
|
||||||
VI vi_r = reduction_map[vi];
|
VI vi_r = reduction_map[vi];
|
||||||
if (vi_r != vi) {
|
if (vi_r != vi) {
|
||||||
exist_reduction = true;
|
exist_reduction = true;
|
||||||
|
|
|
@ -826,6 +826,7 @@ ModelVolume *GLGizmoEmboss::get_selected_volume(const Selection &selection,
|
||||||
return get_model_volume(vol_gl, objects);
|
return get_model_volume(vol_gl, objects);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run Job on main thread (blocking) - ONLY DEBUG
|
||||||
static inline void execute_job(std::shared_ptr<Job> j)
|
static inline void execute_job(std::shared_ptr<Job> j)
|
||||||
{
|
{
|
||||||
struct MyCtl : public Job::Ctl
|
struct MyCtl : public Job::Ctl
|
||||||
|
@ -1005,12 +1006,12 @@ std::unique_ptr<Emboss::IProject> create_projection_for_cut(
|
||||||
// X .. from left to right
|
// X .. from left to right
|
||||||
// Y .. from bottom to top
|
// Y .. from bottom to top
|
||||||
// Z .. from text to eye
|
// Z .. from text to eye
|
||||||
Vec3d untransformed_direction(0., 0., -projection_size);
|
Vec3d untransformed_direction(0., 0., projection_size);
|
||||||
Vec3f project_direction =
|
Vec3f project_direction =
|
||||||
(transformation_for_vector * untransformed_direction).cast<float>();
|
(transformation_for_vector * untransformed_direction).cast<float>();
|
||||||
|
|
||||||
// Projection is in direction from far plane
|
// Projection is in direction from far plane
|
||||||
tr.translate(Vec3d(0., 0., max_z));
|
tr.translate(Vec3d(0., 0., min_z));
|
||||||
|
|
||||||
tr.scale(get_shape_scale(tc.font_item.prop, ff));
|
tr.scale(get_shape_scale(tc.font_item.prop, ff));
|
||||||
// Text alignemnt to center 2D
|
// Text alignemnt to center 2D
|
||||||
|
@ -1035,7 +1036,7 @@ static std::unique_ptr<Emboss::IProject> create_emboss_projection(
|
||||||
SurfaceCut &cut)
|
SurfaceCut &cut)
|
||||||
{
|
{
|
||||||
// Offset of clossed side to model
|
// Offset of clossed side to model
|
||||||
const float surface_offset = 1e-3; // [in mm]
|
const float surface_offset = 1e-3f; // [in mm]
|
||||||
|
|
||||||
const FontProp &fp = tc.font_item.prop;
|
const FontProp &fp = tc.font_item.prop;
|
||||||
float front_move, back_move;
|
float front_move, back_move;
|
||||||
|
|
|
@ -567,7 +567,7 @@ indexed_triangle_set cut_shape(const indexed_triangle_set &source,
|
||||||
const ExPolygon &shape,
|
const ExPolygon &shape,
|
||||||
const Emboss::IProject &projection)
|
const Emboss::IProject &projection)
|
||||||
{
|
{
|
||||||
throw std::exception("NOT implemented yet");
|
// NOT implemented yet
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,8 +645,8 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
||||||
// identify glyph for intersected vertex
|
// identify glyph for intersected vertex
|
||||||
std::string vert_shape_map_name = "v:glyph_id";
|
std::string vert_shape_map_name = "v:glyph_id";
|
||||||
MyMesh cgal_object = MeshBoolean::cgal2::to_cgal(cube, face_map_name);
|
MyMesh cgal_object = MeshBoolean::cgal2::to_cgal(cube, face_map_name);
|
||||||
auto& face_map = cgal_object.property_map<MyMesh::Face_index, int32_t>(face_map_name).first;
|
auto face_map = cgal_object.property_map<MyMesh::Face_index, int32_t>(face_map_name).first;
|
||||||
auto& vert_shape_map = cgal_object.add_property_map<MyMesh::Vertex_index, IntersectingElemnt>(vert_shape_map_name).first;
|
auto vert_shape_map = cgal_object.add_property_map<MyMesh::Vertex_index, IntersectingElemnt>(vert_shape_map_name).first;
|
||||||
|
|
||||||
std::string edge_shape_map_name = "e:glyph_id";
|
std::string edge_shape_map_name = "e:glyph_id";
|
||||||
std::string face_shape_map_name = "f:glyph_id";
|
std::string face_shape_map_name = "f:glyph_id";
|
||||||
|
@ -654,8 +654,8 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
||||||
|
|
||||||
MyMesh cgal_shape = MeshBoolean::cgal2::to_cgal(shape, projection, 0, edge_shape_map_name, face_shape_map_name, glyph_contours);
|
MyMesh cgal_shape = MeshBoolean::cgal2::to_cgal(shape, projection, 0, edge_shape_map_name, face_shape_map_name, glyph_contours);
|
||||||
|
|
||||||
auto& edge_shape_map = cgal_shape.property_map<MyMesh::Edge_index, IntersectingElemnt>(edge_shape_map_name).first;
|
auto edge_shape_map = cgal_shape.property_map<MyMesh::Edge_index, IntersectingElemnt>(edge_shape_map_name).first;
|
||||||
auto& face_shape_map = cgal_shape.property_map<MyMesh::Face_index, IntersectingElemnt>(face_shape_map_name).first;
|
auto face_shape_map = cgal_shape.property_map<MyMesh::Face_index, IntersectingElemnt>(face_shape_map_name).first;
|
||||||
|
|
||||||
// bool map for affected edge
|
// bool map for affected edge
|
||||||
using d_prop_bool = CGAL::dynamic_edge_property_t<bool>;
|
using d_prop_bool = CGAL::dynamic_edge_property_t<bool>;
|
||||||
|
@ -835,10 +835,10 @@ TEST_CASE("Emboss extrude cut", "[Emboss-Cut]")
|
||||||
p);
|
p);
|
||||||
is_inside = abcp == CGAL::POSITIVE;
|
is_inside = abcp == CGAL::POSITIVE;
|
||||||
} else if (i_from < i_to || (i_from == i_to && shape_from.type < shape_to.type)) {
|
} else if (i_from < i_to || (i_from == i_to && shape_from.type < shape_to.type)) {
|
||||||
bool is_last = i_from == 0 && (i_to + 1) == contour.size();
|
bool is_last = i_from == 0 && static_cast<size_t>(i_to + 1) == contour.size();
|
||||||
if (!is_last) is_inside = true;
|
if (!is_last) is_inside = true;
|
||||||
} else { // i_from > i_to || (i_from == i_to && shape_from.type > shape_to.type)
|
} else { // i_from > i_to || (i_from == i_to && shape_from.type > shape_to.type)
|
||||||
bool is_last = i_to == 0 && (i_from + 1) == contour.size();
|
bool is_last = i_to == 0 && static_cast<size_t>(i_from + 1) == contour.size();
|
||||||
if (is_last) is_inside = true;
|
if (is_last) is_inside = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue