# How Vertex Constraint Handling Works in the AutoRemesher Isotropic Remesher

> Learn how AutoRemesher's isotropic remesher handles vertex constraints by setting per-vertex target edge lengths. Discover dynamic threshold adjustments for optimal remeshing.

- Repository: [Jeremy HU/autoremesher](https://github.com/huxingyi/autoremesher)
- Tags: internals
- Published: 2026-07-11

---

**The AutoRemesher isotropic remesher accepts per-vertex target edge lengths via `setVertexTargetEdgeLengths()`, copies them into half-edge mesh vertices, and uses local averages to dynamically adjust edge split and collapse thresholds during remeshing.**

Vertex constraint handling in the AutoRemesher isotropic remesher allows users to prescribe spatially varying mesh densities by assigning a specific target edge length to each vertex of the input mesh. This mechanism drives adaptive refinement, enabling finer tessellation in detailed regions and coarser elements in flat or simple areas. The implementation stores these constraints in the `IsotropicHalfedgeMesh::Vertex` structure and applies them during the split and collapse phases of the remeshing algorithm.

## Setting Per-Vertex Target Edge Lengths

The public API exposes vertex constraints through the `setVertexTargetEdgeLengths` method declared in [`thirdparty/isotropicremesher/isotropicremesher.h`](https://github.com/huxingyi/autoremesher/blob/main/thirdparty/isotropicremesher/isotropicremesher.h). Users supply a `std::vector<double>` where each index corresponds to a vertex index in the input mesh.

```cpp
remesher.setVertexTargetEdgeLengths(const std::vector<double> *targetLengths);

```

The implementation stores this pointer in `IsotropicRemesher::m_vertexTargetEdgeLengths` according to the source in [`thirdparty/isotropicremesher/isotropicremesher.cpp`](https://github.com/huxingyi/autoremesher/blob/main/thirdparty/isotropicremesher/isotropicremesher.cpp)【source L61-L64】. If the pointer is non-null, the remesher assumes adaptive density control is active.

## Propagating Constraints to the Half-Edge Mesh

Before the remeshing iterations begin, the system copies the per-vertex values into the half-edge data structure. The remesher iterates through all vertices in `m_halfedgeMesh` and assigns the `targetEdgeLength` field of each `IsotropicHalfedgeMesh::Vertex` object【source L20-L27】:

```cpp
if (m_vertexTargetEdgeLengths != nullptr) {
    IsotropicHalfedgeMesh::Vertex *vertex = m_halfedgeMesh->moveToNextVertex(nullptr);
    for (size_t vi = 0; vertex != nullptr && vi < m_vertexTargetEdgeLengths->size();
         vertex = m_halfedgeMesh->moveToNextVertex(vertex), ++vi) {
        vertex->targetEdgeLength = (*m_vertexTargetEdgeLengths)[vi];
    }
}

```

This propagation ensures that every vertex in the internal `IsotropicHalfedgeMesh` carries its local constraint value for the duration of the remeshing process.

## Local Edge Length Thresholds

During remeshing, the algorithm evaluates each edge against locally computed thresholds rather than global constants. The `splitLongEdges` and `collapseShortEdges` functions calculate an edge-specific target by averaging the `targetEdgeLength` values of the two endpoint vertices.

### Splitting Long Edges

The maximum allowed edge length is normally computed as `4/3 · targetLength`. When per-vertex constraints are present, the implementation recomputes the limit using the average of the two vertex targets【source L73-L80】:

```cpp
double edgeTarget = (t0 + t1) * 0.5;
edgeMaxLenSq = std::pow(4.0 / 3.0 * edgeTarget, 2);

```

An edge is split when its squared length exceeds `edgeMaxLenSq`. This means vertices with smaller target values trigger earlier splitting, producing denser local triangulation.

### Collapsing Short Edges

Similarly, the minimum allowed length uses a `4/5` factor applied to the averaged target. The `collapseShortEdges` logic sets both minimum and maximum bounds based on the local average【source L13-L18】:

```cpp
double edgeTarget = (t0 + t1) * 0.5;
edgeMinLenSq = std::pow(4.0 / 5.0 * edgeTarget, 2);
edgeMaxLenSq = std::pow(4.0 / 3.0 * edgeTarget, 2);

```

An edge is collapsed only when its squared length falls below `edgeMinLenSq` and the operation does not violate the maximum length constraint.

## Practical Example: Adaptive Mesh Density

The following example demonstrates how to use vertex constraint handling to create finer mesh density near the X-axis while keeping coarser resolution elsewhere:

```cpp
// Load input mesh
std::vector<Vector3> vertices = loadMeshVertices();
std::vector<std::vector<size_t>> triangles = loadMeshTriangles();

// Create per-vertex target lengths: 0.02 near X-axis, 0.05 elsewhere
std::vector<double> perVertexTarget(vertices.size(), 0.0);
for (size_t i = 0; i < vertices.size(); ++i) {
    perVertexTarget[i] = (std::abs(vertices[i].x) < 0.5) ? 0.02 : 0.05;
}

// Initialize and configure remesher
IsotropicRemesher remesher(&vertices, &triangles);
remesher.setVertexTargetEdgeLengths(&perVertexTarget);
remesher.setTargetEdgeLength(0.04);   // Fallback global target
remesher.remesh(5);                   // Run 5 iterations

```

After remeshing, you can inspect the effective target lengths stored in the half-edge mesh:

```cpp
auto *mesh = remesher.remeshedHalfedgeMesh();
for (auto *v = mesh->moveToNextVertex(nullptr); v != nullptr;
     v = mesh->moveToNextVertex(v)) {
    std::cout << "Vertex " << v->debugIndex
              << " target length = " << v->targetEdgeLength << "\n";
}

```

## Key Implementation Files

- **[`thirdparty/isotropicremesher/isotropicremesher.h`](https://github.com/huxingyi/autoremesher/blob/main/thirdparty/isotropicremesher/isotropicremesher.h)** – Declares the public API including `setVertexTargetEdgeLengths`.
- **[`thirdparty/isotropicremesher/isotropicremesher.cpp`](https://github.com/huxingyi/autoremesher/blob/main/thirdparty/isotropicremesher/isotropicremesher.cpp)** – Implements constraint storage, propagation to vertices, and the split/collapse logic using local averages.
- **[`thirdparty/isotropicremesher/isotropichalfedgemesh.cpp`](https://github.com/huxingyi/autoremesher/blob/main/thirdparty/isotropicremesher/isotropichalfedgemesh.cpp)** – Defines `IsotropicHalfedgeMesh::Vertex` with the `targetEdgeLength` field.
- **[`src/AutoRemesher/autoremesher.cpp`](https://github.com/huxingyi/autoremesher/blob/main/src/AutoRemesher/autoremesher.cpp)** – Higher-level wrapper that forwards user constraints from the application layer to the isotropic remesher instance.

## Summary

- **Vertex constraints** are supplied as a `std::vector<double>` via `setVertexTargetEdgeLengths()` and stored in `m_vertexTargetEdgeLengths`.
- The remesher copies these values into `IsotropicHalfedgeMesh::Vertex::targetEdgeLength` before processing begins.
- **Edge thresholds** are computed locally by averaging the `targetEdgeLength` values of both endpoints.
- The **split threshold** uses a factor of `4/3` applied to the local average, while the **collapse threshold** uses `4/5`.
- This system enables **adaptive remeshing** where local density is controlled independently per vertex while maintaining the isotropic quality of the output mesh.

## Frequently Asked Questions

### How do I set different target edge lengths for specific vertices?

Pass a `std::vector<double>` pointer to `setVertexTargetEdgeLengths()` where the index of each element corresponds to the vertex index in your input mesh. The remesher reads this vector during initialization and assigns each value to the respective vertex in the internal half-edge structure.

### What happens if I only set a global target edge length?

If you do not call `setVertexTargetEdgeLengths()` (or pass `nullptr`), the remesher uses the global value set via `setTargetEdgeLength()` for all edges. The vertex constraint system is optional and only activates when `m_vertexTargetEdgeLengths` is non-null.

### How does the remesher handle edges between vertices with different target lengths?

The algorithm computes an **edge-specific target** by averaging the `targetEdgeLength` values of the two endpoint vertices. This averaged value then feeds into the standard `4/3` split factor and `4/5` collapse factor, producing a smooth transition in mesh density rather than abrupt changes at vertex boundaries.

### Can I inspect the target edge lengths after remeshing?

Yes. After calling `remesh()`, access the remeshed half-edge mesh via `remeshedHalfedgeMesh()` and iterate through vertices using `moveToNextVertex()`. Each vertex object exposes its effective `targetEdgeLength` field, allowing you to verify how constraints propagated through the remeshing operations.