# How the AutoRemesher Progress Callback Tracks Remeshing Progress

> Discover how the AutoRemesher progress callback system aggregates per-island updates from multiple threads into a weighted average for precise remeshing progress tracking.

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

---

**AutoRemesher reports remeshing progress through a three-part callback system that aggregates per-island updates from multiple threads into a weighted average delivered to a consumer-registered handler.**

The AutoRemesher library provides real-time feedback during the computationally intensive remeshing process through a thread-safe progress callback mechanism. By implementing the `AutoRemesherProgressHandler` function pointer and registering it via `setProgressHandler`, applications can track both coarse initialization phases and fine-grained Geogram solver progress across multiple mesh islands.

## The Callback Function Signature

At the core of the system is the `AutoRemesherProgressHandler` type definition found in [`src/AutoRemesher/autoremesher.h`](https://github.com/huxingyi/autoremesher/blob/main/src/AutoRemesher/autoremesher.h) (lines 39-41). This function pointer defines the contract between the library and consuming applications:

```cpp
typedef void (*AutoRemesherProgressHandler)(void* tag, float progress, const char* status);

```

The three parameters provide complete context for each progress update:
- **`tag`** – A user-supplied opaque pointer (typically the consumer object instance)
- **`progress`** – A normalized float value between **0.0** and **1.0** representing overall completion
- **`status`** – A human-readable C-string describing the current operation (e.g., "Island 2: solver passes 0-1")

## Registering a Progress Handler

Consumers register their callback using two complementary methods in [`src/quadmeshgenerator.cpp`](https://github.com/huxingyi/autoremesher/blob/main/src/quadmeshgenerator.cpp) (lines 68-70). First, `setTag` stores the user data pointer that will be passed back to the handler. Second, `setProgressHandler` registers the static callback function:

```cpp
// Consumer side implementation (QuadMeshGenerator)
static void reportProgressHandler(void* tag, float progress, const char* status)
{
    QuadMeshGenerator* gen = static_cast<QuadMeshGenerator*>(tag);
    gen->emitProgress(progress, QString::fromUtf8(status));
}

// Registration before remeshing begins
m_autoRemesher->setTag(this);
m_autoRemesher->setProgressHandler(reportProgressHandler);

```

This pattern allows the static C-style callback to forward messages to member functions of the consumer class.

## Internal Progress Aggregation

The `AutoRemesher::updateProgress` method in [`src/AutoRemesher/autoremesher.cpp`](https://github.com/huxingyi/autoremesher/blob/main/src/AutoRemesher/autoremesher.cpp) (lines 101-118) implements thread-safe progress aggregation across multiple mesh islands. When processing islands in parallel, the system maintains a per-thread progress vector (`m_threadProgress`) and computes a weighted average using island-specific weights (`m_threadProgressWeights`):

```cpp
void AutoRemesher::updateProgress(size_t threadIndex, float progress)
{
    if (!m_progressHandler) return;
    
    if (progress > m_threadProgress[threadIndex])
        m_threadProgress[threadIndex] = progress;

    // Calculate weighted average across all islands
    float islandWeightedAvg = 0.0f;
    for (size_t i = 0; i < m_threadProgress.size(); ++i)
        islandWeightedAvg += m_threadProgress[i] * m_threadProgressWeights[i];

    // Thread-safe status copying
    std::string statusCopy;
    {
        std::lock_guard<std::mutex> lock(m_currentStatusMutex);
        statusCopy = m_currentStatus;
    }

    // Invoke user-provided callback
    m_progressHandler(m_tag, islandWeightedAvg, statusCopy.c_str());
}

```

The function uses a **mutex-protected status string** to prevent race conditions during multi-threaded updates, ensuring that the callback receives consistent data even when islands complete at different rates.

## Geogram Integration and Stage Mapping

AutoRemesher integrates with the Geogram library's internal progress system through the `ReportProgress` static function in [`src/AutoRemesher/autoremesher.cpp`](https://github.com/huxingyi/autoremesher/blob/main/src/AutoRemesher/autoremesher.cpp) (lines 43-64). Georama reports progress in 8 distinct rounds (0-7), which AutoRemesher maps to specific processing stages while translating the progress into a normalized 0.3-0.9 range:

```cpp
static void ReportProgress(void* tag, float progress)
{
    ReportProgressContext* ctx = static_cast<ReportProgressContext*>(tag);
    int round = geogram_report_progress_round;

    // Map Geogram rounds to human-readable stages
    static const char* stages[] = {
        "brush + cross-field alignment",
        "singular vertex detection", 
        "cut graph construction",
        "constraint building",
        "solver passes 0-1",
        "solver passes 2-3",
        "mixed-integer solve",
        "result extraction"
    };
    
    ctx->autoRemesher->setCurrentStatus(
        "Island " + std::to_string(ctx->islandIndex + 1) + ": " + stages[round]);

    // Map round to progress range (0.3 to 0.9 total)
    float base, span;
    switch (round) {
        case 0:  base = 0.0f;  span = 0.015f; break;
        case 1:  base = 0.015f; span = 0.01f;  break;
        case 2:  base = 0.025f; span = 0.015f; break;
        case 3:  base = 0.04f;  span = 0.02f;  break;
        default: base = 0.06f;  span = 0.94f;  break;
    }
    
    float totalProgress = 0.3f + 0.6f * (base + span * progress);
    ctx->autoRemesher->updateProgress(ctx->islandIndex, totalProgress);
}

```

The mapping reserves the **0.0-0.3** range for AutoRemesher's internal initialization phases (voxel sizing, island splitting) and the **0.9-1.0** range for final result assembly, as seen in [`src/AutoRemesher/autoremesher.cpp`](https://github.com/huxingyi/autoremesher/blob/main/src/AutoRemesher/autoremesher.cpp) (lines 25-35).

## Summary

- **AutoRemesherProgressHandler** defines a three-parameter callback signature (tag, progress, status) that consumers must implement
- **Registration** requires calling both `setTag` for the user data pointer and `setProgressHandler` for the callback function
- **Thread aggregation** computes weighted averages across parallel island processing using per-thread progress tracking and mutex-protected status updates
- **Geogram bridge** maps the library's 8 processing rounds (0-7) to standardized progress ranges (0.3-0.9) with descriptive stage names
- **Progress ranges** reserve 0.0-0.3 for initialization, 0.3-0.9 for Geogram processing, and 0.9-1.0 for finalization

## Frequently Asked Questions

### How do I register a custom progress handler in AutoRemesher?

Register your handler by calling `setProgressHandler` with a static function matching the `AutoRemesherProgressHandler` signature, and use `setTag` to pass your object instance as the first parameter. The QuadMeshGenerator class in [`src/quadmeshgenerator.cpp`](https://github.com/huxingyi/autoremesher/blob/main/src/quadmeshgenerator.cpp) demonstrates this pattern by casting the tag pointer back to the generator instance within the static callback.

### Why does AutoRemesher use weighted averages for progress reporting?

The weighted average calculation in `updateProgress` accounts for islands of different sizes, ensuring that progress reflects actual computational work rather than treating each island equally. The `m_threadProgressWeights` vector stores normalized area weights, so larger islands contribute proportionally more to the overall progress percentage.

### What are the 8 Geogram processing stages mapped by AutoRemesher?

AutoRemesher translates Geogram's internal rounds into descriptive status messages: brush and cross-field alignment, singular vertex detection, cut graph construction, constraint building, solver passes 0-1, solver passes 2-3, mixed-integer solve, and result extraction. Each stage occupies a specific sub-range of the 0.3-0.9 progress band as defined in the `ReportProgress` switch statement.

### Is the AutoRemesher progress callback thread-safe?

Yes. The `updateProgress` method uses a `std::lock_guard` on `m_currentStatusMutex` when copying the status string to prevent race conditions during parallel island processing. However, consumers should ensure their callback implementation is reentrant if they access shared state, as the handler may be invoked from multiple threads simultaneously.