Skip to content

Improve the halfedges API #116

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 21 additions & 19 deletions src/lib/geogram/mesh/mesh_halfedges.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,55 +41,57 @@

namespace GEO {

bool MeshHalfedges::move_to_next_around_vertex(Halfedge& H) const {
bool MeshHalfedges::move_to_next_around_vertex(Halfedge& H, bool ignore_borders) const {
geo_debug_assert(halfedge_is_valid(H));
index_t v = mesh_.facet_corners.vertex(H.corner);
index_t f = mesh_.facet_corners.adjacent_facet(H.corner);
index_t v = Geom::halfedge_vertex_index_from(mesh_,H); // get the vertex at the origin of H
index_t f = Geom::halfedge_facet_secondary(mesh_,H); // get the facet at the other side of H (relative to H.facet)
if(f == NO_FACET) {
return false;
return false; // cannot move
}
if(
ignore_borders == false &&
facet_region_.is_bound() &&
facet_region_[H.facet] != facet_region_[f]
) {
return false;
return false; // cannot move without crossing a border (end criteria when looping around a vertex, see move_to_*_around_border())
}
for(index_t c: mesh_.facets.corners(f)) {
index_t pc = mesh_.facets.prev_corner_around_facet(f, c);
for(index_t c: mesh_.facets.corners(f)) { // for each corner of the facet f
index_t pc = mesh_.facets.prev_corner_around_facet(f, c); // get the previous corner clockwise
if(
mesh_.facet_corners.vertex(c) == v &&
mesh_.facet_corners.adjacent_facet(pc) == H.facet
mesh_.facet_corners.vertex(c) == v && // same origin vertex
mesh_.facet_corners.adjacent_facet(pc) == H.facet // previous facet is adjacent
) {
H.corner = c;
H.facet = f;
return true;
return true; // successful move
}
}
geo_assert_not_reached;
}

bool MeshHalfedges::move_to_prev_around_vertex(Halfedge& H) const {
bool MeshHalfedges::move_to_prev_around_vertex(Halfedge& H, bool ignore_borders) const {
geo_debug_assert(halfedge_is_valid(H));
index_t v = mesh_.facet_corners.vertex(H.corner);
index_t pc = mesh_.facets.prev_corner_around_facet(H.facet, H.corner);
index_t f = mesh_.facet_corners.adjacent_facet(pc);
index_t v = Geom::halfedge_vertex_index_from(mesh_,H); // get the vertex at the origin of H
index_t pc = mesh_.facets.prev_corner_around_facet(H.facet, H.corner); // get the previous corner clockwise
index_t f = mesh_.facet_corners.adjacent_facet(pc); // the facet after H.facet clockwise
if(f == NO_FACET) {
return false;
return false; // cannot move
}
if(
ignore_borders == false &&
facet_region_.is_bound() &&
facet_region_[H.facet] != facet_region_[f]
) {
return false;
return false; // cannot move without crossing a border (end criteria when looping around a vertex, see move_to_*_around_border())
}
for(index_t c: mesh_.facets.corners(f)) {
if(
mesh_.facet_corners.vertex(c) == v &&
mesh_.facet_corners.adjacent_facet(c) == H.facet
mesh_.facet_corners.vertex(c) == v && // same origin vertex
mesh_.facet_corners.adjacent_facet(c) == H.facet // previous facet is adjacent
) {
H.corner = c;
H.facet = f;
return true;
return true; // successful move
}
}
geo_assert_not_reached;
Expand Down
206 changes: 193 additions & 13 deletions src/lib/geogram/mesh/mesh_halfedges.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,29 @@
/**
* \file geogram/mesh/mesh_halfedges.h
* \brief Classes and function for virtually seeing a mesh as a set of halfedges
* \details
* MeshHalfedges is the interface itself
* MeshHalfedges::Halfedge is an oriented edge, which stores a facet index and a facet corner index
* MeshHalfedges::Halfedge.corner is the corner of the facet that is along the halfedge origin
*
* Assuming counterclockwise vertices order for the facet (right hand rule -> outgoing facet normal),
* MeshHalfedges::Halfedge.facet is the facet at the left of the halfedge
*
* o
* / \
* / \
* / \
* / facet \
* / \
* /\corner \
* o ==========> o
* halfedge
*
* Still assuming counterclockwise vertices order, around a vertex:
* - move_to_next_around_vertex() moves clockwise
* - move_to_prev_around_vertex() moves counterclockwise
* so counterclockwise, the facet is ahead of the halfedge
* and clockwise, the halfedge is ahead of the facet
*/

namespace GEO {
Expand Down Expand Up @@ -121,8 +144,8 @@ namespace GEO {
return !(rhs == *this);
}

index_t facet;
index_t corner;
index_t facet; // the supporting facet. the position relative to the halfedge (left or right) depends on the facets normal orientation.
index_t corner; // the corner of `facet` along the halfedge origin vertex

};

Expand Down Expand Up @@ -190,7 +213,15 @@ namespace GEO {
set_use_facet_region(std::string(attribute_name));
}


/**
* \brief Get whether or not a facet attribute is set for facet regions.
* \return true if a facet attribute is used to define regions and borders
*/
bool is_using_facet_region() const {
return facet_region_.is_bound();
}


/**
* \brief Tests whether a Halfedge is valid.
* \param[in] H the Halfedge to be tested
Expand All @@ -202,11 +233,11 @@ namespace GEO {
*/
bool halfedge_is_valid(const Halfedge& H) const {
return
H.facet != Halfedge::NO_FACET &&
H.facet != Halfedge::NO_FACET &&
H.corner != Halfedge::NO_CORNER &&
H.facet < mesh_.facets.nb() &&
H.corner < mesh_.facet_corners.nb()
;
;
}

/**
Expand All @@ -231,6 +262,8 @@ namespace GEO {
return mesh_.facet_corners.adjacent_facet(H.corner) == NO_FACET;
}

/****** Moving around a facet **********/

/**
* \brief Replaces a Halfedge with the next one around the facet.
* \param[in,out] H the Halfedge
Expand All @@ -249,21 +282,27 @@ namespace GEO {
H.corner = mesh_.facets.prev_corner_around_facet(H.facet, H.corner);
}

/****** Moving around a vertex **********/

/**
* \brief Replaces a Halfedge with the next one around the vertex.
* \param[in,out] H the Halfedge
* \param[in] ignore_borders if the borders should be ignored (see set_use_facet_region())
* \return true if the move was successful, false otherwise. On borders,
* the next halfedge around a vertex may not exist.
*/
bool move_to_next_around_vertex(Halfedge& H) const;
bool move_to_next_around_vertex(Halfedge& H, bool ignore_borders = false) const;

/**
* \brief Replaces a Halfedge with the previous one around the vertex.
* \param[in,out] H the Halfedge
* \param[in] ignore_borders if the borders should be ignored (see set_use_facet_region())
* \return true if the move was successful, false otherwise. On borders,
* the previous halfedge around a vertex may not exist.
*/
bool move_to_prev_around_vertex(Halfedge& H) const;
bool move_to_prev_around_vertex(Halfedge& H, bool ignore_borders = false) const;

/****** Moving around the border **********/

/**
* \brief Replaces a Halfedge with the next one around the border.
Expand All @@ -283,6 +322,8 @@ namespace GEO {
*/
void move_to_prev_around_border(Halfedge& H) const;

/****** Flip halfedge **********/

/**
* \brief Replaces a Halfedge with the opposite one in the
* adjacent facet.
Expand All @@ -291,9 +332,9 @@ namespace GEO {
*/
void move_to_opposite(Halfedge& H) const;

private:
Mesh& mesh_;
Attribute<index_t> facet_region_;
protected:
Mesh& mesh_;
Attribute<index_t> facet_region_;
};

/**
Expand All @@ -310,6 +351,20 @@ namespace GEO {

namespace Geom {

/****** Get origin/extremity vertices **********/

/**
* \brief Gets the origin vertex of a Halfedge
* \param[in] M the mesh
* \param[in] H the Halfedge
* \return the vertex index of the origin of \p H
*/
inline index_t halfedge_vertex_index_from(
const Mesh& M, const MeshHalfedges::Halfedge& H
) {
return M.facet_corners.vertex(H.corner);
}

/**
* \brief Gets the origin point of a Halfedge
* \param[in] M the mesh
Expand All @@ -319,7 +374,20 @@ namespace GEO {
inline const vec3& halfedge_vertex_from(
const Mesh& M, const MeshHalfedges::Halfedge& H
) {
return mesh_vertex(M, M.facet_corners.vertex(H.corner));
return mesh_vertex(M, halfedge_vertex_index_from(M,H));
}

/**
* \brief Gets the arrow extremity vertex of a Halfedge
* \param[in] M the mesh
* \param[in] H the Halfedge
* \return the vertex index of the arrow extremity of \p H
*/
inline index_t halfedge_vertex_index_to(
const Mesh& M, const MeshHalfedges::Halfedge& H
) {
index_t c = M.facets.next_corner_around_facet(H.facet, H.corner);
return M.facet_corners.vertex(c);
}

/**
Expand All @@ -331,10 +399,11 @@ namespace GEO {
inline const vec3& halfedge_vertex_to(
const Mesh& M, const MeshHalfedges::Halfedge& H
) {
index_t c = M.facets.next_corner_around_facet(H.facet, H.corner);
return mesh_vertex(M, M.facet_corners.vertex(c));
return mesh_vertex(M, halfedge_vertex_index_to(M,H));
}

/****** Get halfedge as vector **********/

/**
* \brief Gets a 3d vector that connects the origin with the arrow
* extremity of a Halfedge.
Expand All @@ -360,6 +429,117 @@ namespace GEO {
) {
return length(halfedge_vector(M, H));
}

/****** Get main/secondary facets **********/

/**
* \brief Gets the main/supporting facet of a Halfedge
* \param[in] M the Mesh
* \param[in] H the Halfedge
* \return The facet index (can be NO_FACET)
*/
inline index_t halfedge_facet_main(
const Mesh& M, const MeshHalfedges::Halfedge& H
) {
geo_argused(M);
return H.facet;
}

/**
* \brief Gets the facet at the other side of the halfedge in relation to the main facet
* \param[in] M the Mesh
* \param[in] H the Halfedge
* \return The facet index (can be NO_FACET)
*/
inline index_t halfedge_facet_secondary(
const Mesh& M, const MeshHalfedges::Halfedge& H
) {
return M.facet_corners.adjacent_facet(H.corner); // see H.facet new value in move_to_opposite()
}

/****** Get facet corners **********/

/**
* \brief Gets the facet corner of the main facet which is on the origin vertex
* \param[in] M the Mesh
* \param[in] H the Halfedge
* \return The facet corner index (can be NO_CORNER)
*/
inline index_t halfedge_bottom_main_corner(
const Mesh& M, const MeshHalfedges::Halfedge& H
) {
geo_argused(M);
return H.corner;
}

/**
* \brief Gets the facet corner of the secondary facet which is on the origin vertex
* \param[in] M the Mesh
* \param[in] H the Halfedge
* \return The facet corner index (can be NO_CORNER)
*/
inline index_t halfedge_bottom_secondary_corner(
const Mesh& M, const MeshHalfedges::Halfedge& H
) {
index_t right_facet = halfedge_facet_secondary(M,H);
index_t corner = NO_CORNER;
if(right_facet == NO_FACET) {
return NO_CORNER;
}
FOR(lv,3) {
corner = M.facets.corner(right_facet,lv);
if(M.facet_corners.vertex(corner) == halfedge_vertex_index_from(M,H)) {
return corner;
}
}
geo_assert_not_reached;
}

/**
* \brief Gets the facet corner of the main facet which is on the extremity vertex
* \param[in] M the Mesh
* \param[in] H the Halfedge
* \return The facet corner index (can be NO_CORNER)
*/
inline index_t halfedge_top_main_corner(
const Mesh& M, const MeshHalfedges::Halfedge& H
) {
index_t left_facet = halfedge_facet_main(M,H);
index_t corner = NO_CORNER;
if(left_facet == NO_FACET) {
return NO_CORNER;
}
FOR(lv,3) {
corner = M.facets.corner(left_facet,lv);
if(M.facet_corners.vertex(corner) == halfedge_vertex_index_to(M,H)) {
return corner;
}
}
geo_assert_not_reached;
}

/**
* \brief Gets the facet corner of the secondary facet which is on the extremity vertex
* \param[in] M the Mesh
* \param[in] H the Halfedge
* \return The facet corner index (can be NO_CORNER)
*/
inline index_t halfedge_top_secondary_corner(
const Mesh& M, const MeshHalfedges::Halfedge& H
) {
index_t right_facet = halfedge_facet_secondary(M,H);
index_t corner = NO_CORNER;
if(right_facet == NO_FACET) {
return NO_CORNER;
}
FOR(lv,3) {
corner = M.facets.corner(right_facet,lv);
if(M.facet_corners.vertex(corner) == halfedge_vertex_index_to(M,H)) {
return corner;
}
}
geo_assert_not_reached;
}
}
}

Expand Down