Skip to content
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
41 changes: 41 additions & 0 deletions src/YgorMath.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6325,6 +6325,47 @@ fv_surface_mesh<T,I>::recreate_involved_face_index(void){
#endif


// Apply a surgical update to this->involved_faces.
template <class T, class I>
void
fv_surface_mesh<T,I>::apply_involved_face_index_diff(const involved_face_index_diff<I> &diff){
// Extend involved_faces for new vertices.
for(I i = 0; i < diff.new_vertex_count; ++i){
this->involved_faces.emplace_back();
}

// Apply removals.
for(const auto &entry : diff.entries_to_remove){
const auto v_idx = entry.first;
const auto f_idx = entry.second;
if(static_cast<size_t>(v_idx) >= this->involved_faces.size()) continue;
auto &face_list = this->involved_faces[v_idx];
auto it = std::find(face_list.begin(), face_list.end(), f_idx);
if(it != face_list.end()){
// Swap-and-pop for O(1) erasure (order of entries is not significant).
std::swap(*it, face_list.back());
face_list.pop_back();
}
}

// Apply additions.
for(const auto &entry : diff.entries_to_add){
const auto v_idx = entry.first;
const auto f_idx = entry.second;
if(static_cast<size_t>(v_idx) >= this->involved_faces.size()) continue;
this->involved_faces[v_idx].push_back(f_idx);
}
return;
}
#ifndef YGORMATH_DISABLE_ALL_SPECIALIZATIONS
template void fv_surface_mesh<float , uint32_t >::apply_involved_face_index_diff(const involved_face_index_diff<uint32_t> &);
template void fv_surface_mesh<float , uint64_t >::apply_involved_face_index_diff(const involved_face_index_diff<uint64_t> &);

template void fv_surface_mesh<double, uint32_t >::apply_involved_face_index_diff(const involved_face_index_diff<uint32_t> &);
template void fv_surface_mesh<double, uint64_t >::apply_involved_face_index_diff(const involved_face_index_diff<uint64_t> &);
#endif


// Re-compute this->vertex_normals from current face orientations.
template <class T, class I>
void
Expand Down
38 changes: 38 additions & 0 deletions src/YgorMath.h
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,34 @@ Estimate_Contour_Separation(const std::list<std::reference_wrapper<contour_colle
//---------------------------------------------------------------------------------------------------------------------------
//--------------- fv_surface_mesh: a 2D surface mesh embedded in 3D with a straightforward data structure -------------------
//---------------------------------------------------------------------------------------------------------------------------
//---------------------------------------------------------------------------------------------------------------------------
// A lightweight description of surgical changes to an involved_faces index.
//
// Instead of rebuilding the entire involved_faces index after every mesh modification,
// callers can construct an involved_face_index_diff describing only the changes and
// pass it to fv_surface_mesh::apply_involved_face_index_diff().
//
// Application order: new vertices are appended first, then all removals are applied,
// and finally all additions are applied. The result is identical to what
Copy link

Copilot AI Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment claims applying a diff “is identical to what recreate_involved_face_index() would produce”, but apply_involved_face_index_diff() does not validate preconditions (e.g., involved_faces currently matches the pre-change mesh, diff is complete, and new_vertex_count brings involved_faces to vertices.size()). Consider clarifying these preconditions in the doc comment (or add defensive checks) to avoid implying it self-heals a stale/corrupted index.

Suggested change
// and finally all additions are applied. The result is identical to what
// and finally all additions are applied.
//
// Preconditions for correctness:
// * involved_faces must currently match the pre-change mesh connectivity;
// * the diff must completely describe all changes performed on the mesh
// since involved_faces was last rebuilt (or last kept in sync);
// * new_vertex_count must be chosen so that, after applying the diff,
// involved_faces has one entry per vertex in the mesh (e.g., matches vertices.size()).
//
// fv_surface_mesh::apply_involved_face_index_diff() does not validate these
// preconditions or repair a stale/corrupted index; it simply applies the
// described edits. When the above preconditions hold, the resulting
// involved_faces index is intended to be identical to what

Copilot uses AI. Check for mistakes.
// recreate_involved_face_index() would produce.
//
template <class I>
struct involved_face_index_diff {
// Number of new vertices added to the mesh.
// The involved_faces vector will be extended with this many empty entries.
I new_vertex_count = 0;

// Specific (vertex_index, face_index) associations to remove.
// Each pair means: remove face_index from involved_faces[vertex_index].
std::vector<std::pair<I, I>> entries_to_remove;

// Specific (vertex_index, face_index) associations to add.
// Each pair means: add face_index to involved_faces[vertex_index].
std::vector<std::pair<I, I>> entries_to_add;
};

//---------------------------------------------------------------------------------------------------------------------------

//Simple, direct face-vertex list data structure representing a 3D surface mesh. Few constraints are imposed by this
// data structure, and the meshes are not guaranteed to have many specific qualities. For example, multiple disconnected
// meshes can be combined together without specifying whether they represent internal/external surfaces or are nested
Expand Down Expand Up @@ -597,6 +625,16 @@ template <class T, class I> class fv_surface_mesh {
// Regenerates this->involved_faces using this->vertices and this->faces.
void recreate_involved_face_index(void);

// Apply a surgical update to this->involved_faces.
//
// This is an efficient alternative to recreate_involved_face_index() when only
// a small number of faces or vertices have changed. The diff describes which
// (vertex, face) associations to remove and which to add. New vertex entries are
// appended first, then all removals are applied, then all additions.
// The resulting index is identical to what recreate_involved_face_index() would
// produce given the same mesh state.
void apply_involved_face_index_diff(const involved_face_index_diff<I> &diff);

// Re-compute this->vertex_normals using the current face orientations.
//
// Each vertex normal is the area-weighted average of the face normals of all
Expand Down
Loading
Loading