How the AutoRemesher Progress Callback Tracks Remeshing Progress
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 (lines 39-41). This function pointer defines the contract between the library and consuming applications:
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 completionstatus– 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 (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:
// 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 (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):
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 (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:
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 (lines 25-35).
Summary
- AutoRemesherProgressHandler defines a three-parameter callback signature (tag, progress, status) that consumers must implement
- Registration requires calling both
setTagfor the user data pointer andsetProgressHandlerfor 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 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →