diff --git a/libexamples/mmg3d/progress_callback_example0/README.md b/libexamples/mmg3d/progress_callback_example0/README.md
new file mode 100644
index 000000000..5dcecb49f
--- /dev/null
+++ b/libexamples/mmg3d/progress_callback_example0/README.md
@@ -0,0 +1,57 @@
+# Progress callback example
+
+## I/ Implementation
+This example shows how to register a progress callback with the **mmg3d**
+library before calling **MMG3D_mmg3dlib**.
+
+The callback receives the current remeshing phase, the iteration count and the
+number of mesh operations performed during the iteration. It returns 1 to keep
+remeshing; returning 0 asks Mmg to stop.
+
+A phase may stop before reaching its maximum iteration count when the mesh has
+converged. Mmg emits a final completion notification so progress bars can close
+at 100%.
+
+The same pattern can be used with **MMG2D_Set_progressCallback** and
+**MMGS_Set_progressCallback** for the 2D and surface libraries.
+
+## II/ Compilation
+ 1. Build and install the **mmg3d** shared and static library. We suppose in
+ the following that you have installed the **mmg3d** library in the
+ **_$CMAKE_INSTALL_PREFIX_** directory;
+ 2. compile the main.c file specifying:
+ * the **mmg3d** include directory with the **-I** option;
+ * the **mmg3d** library location with the **-L** option;
+ * the **mmg3d** library name with the **-l** option;
+ * for the static library you must also link the executable with, if used for
+ the **mmg3d** library compilation, the scotch and scotcherr libraries and
+ with the math library.
+
+> Example 1
+> Command line to link the application with the **mmg3d** static library:
+> ```Shell
+> gcc -I$CMAKE_INSTALL_PREFIX/include main.c -L$CMAKE_INSTALL_PREFIX/lib -L$SCOTCH_PATH -lmmg3d -lscotch -lscotcherr -lm
+> ```
+
+> Example 2
+> Command line to link the application with the **mmg3d** shared library:
+> ```Shell
+> gcc -I$CMAKE_INSTALL_PREFIX/include main.c -L$CMAKE_INSTALL_PREFIX/lib -lmmg3d
+> export LD_LIBRARY_PATH=$CMAKE_INSTALL_PREFIX/lib:$LD_LIBRARY_PATH
+> ```
+
+## III/ Run
+```Shell
+./a.out cube.mesh cube-progress.mesh
+```
+
+For a larger input that makes progress reporting visible, run the command-line
+tool on the 2 spheres example:
+
+```Shell
+mmg3d -progress ../adaptation_example2/2spheres.mesh -out 2spheres-progress.mesh
+```
+
+The **-progress** option keeps the usual Mmg output and adds progress reporting.
+With verbose output, progress is printed as regular rows to avoid overwriting
+diagnostic messages.
diff --git a/libexamples/mmg3d/progress_callback_example0/cube.mesh b/libexamples/mmg3d/progress_callback_example0/cube.mesh
new file mode 100644
index 000000000..837498db1
--- /dev/null
+++ b/libexamples/mmg3d/progress_callback_example0/cube.mesh
@@ -0,0 +1,58 @@
+MeshVersionFormatted 2
+
+Dimension 3
+
+Vertices
+12
+0 0 0 0
+0.5 0 0 0
+0.5 0 1 0
+0 0 1 0
+0 1 0 0
+0.5 1 0 0
+0.5 1 1 0
+0 1 1 0
+1 0 0 0
+1 1 0 0
+1 0 1 0
+1 1 1 0
+
+Tetrahedra
+12
+ 1 4 2 8 1
+ 8 3 2 7 1
+ 5 2 6 8 1
+ 5 8 1 2 1
+ 7 2 8 6 1
+ 2 4 3 8 1
+ 9 2 3 7 2
+ 7 11 9 12 2
+ 6 9 10 7 2
+ 6 7 2 9 2
+ 12 9 7 10 2
+ 9 3 11 7 2
+
+Triangles
+20
+ 1 4 8 3
+ 1 2 4 3
+ 8 3 7 3
+ 5 8 6 3
+ 5 6 2 3
+ 5 2 1 3
+ 5 1 8 3
+ 7 6 8 3
+ 4 3 8 3
+ 2 3 4 3
+ 9 3 2 4
+ 11 9 12 4
+ 7 11 12 4
+ 6 7 10 4
+ 6 10 9 4
+ 6 9 2 4
+ 12 10 7 4
+ 12 9 10 4
+ 3 11 7 4
+ 9 11 3 4
+
+End
diff --git a/libexamples/mmg3d/progress_callback_example0/cube.sol b/libexamples/mmg3d/progress_callback_example0/cube.sol
new file mode 100644
index 000000000..86bcfdb5f
--- /dev/null
+++ b/libexamples/mmg3d/progress_callback_example0/cube.sol
@@ -0,0 +1,21 @@
+MeshVersionFormatted 2
+
+Dimension 3
+
+SolAtVertices
+12
+1 1
+0.5
+0.5
+0.5
+0.5
+0.5
+0.5
+0.5
+0.5
+0.5
+0.5
+0.5
+0.5
+
+End
diff --git a/libexamples/mmg3d/progress_callback_example0/main.c b/libexamples/mmg3d/progress_callback_example0/main.c
new file mode 100644
index 000000000..ea6ee24bb
--- /dev/null
+++ b/libexamples/mmg3d/progress_callback_example0/main.c
@@ -0,0 +1,321 @@
+/* =============================================================================
+** This file is part of the mmg software package for the tetrahedral
+** mesh modification.
+** Copyright (c) Bx INP/Inria/UBordeaux/UPMC, 2004- .
+**
+** mmg is free software: you can redistribute it and/or modify it
+** under the terms of the GNU Lesser General Public License as published
+** by the Free Software Foundation, either version 3 of the License, or
+** (at your option) any later version.
+**
+** mmg is distributed in the hope that it will be useful, but WITHOUT
+** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+** FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+** License for more details.
+**
+** You should have received a copy of the GNU Lesser General Public
+** License and of the GNU General Public License along with mmg (in
+** files COPYING.LESSER and COPYING). If not, see
+** . Please read their terms carefully and
+** use this copy of the mmg distribution only if you accept them.
+** =============================================================================
+*/
+
+/**
+ * Example of use of the mmg3d library progress callback.
+ *
+ * \version 5
+ * \copyright GNU Lesser General Public License.
+ */
+
+#include /** BEGIN_EXAMPLE (this line is used by Doxygen) */
+#include
+#include
+
+#if defined(_WIN32)
+#include
+#define ISATTY(stream) _isatty(_fileno(stream))
+#else
+#include
+#define ISATTY(stream) isatty(fileno(stream))
+#endif
+
+/** Include the mmg3d library header file */
+// if the header file is in the "include" directory
+// #include "libmmg3d.h"
+// if the header file is in "include/mmg/mmg3d"
+#include "mmg/mmg3d/libmmg3d.h"
+
+#define PROGRESS_PHASE_COUNT 4
+
+typedef struct {
+ int lastPhase;
+ int barIsOpen;
+ int isInteractive;
+ int seen[PROGRESS_PHASE_COUNT];
+ int iteration[PROGRESS_PHASE_COUNT];
+ int maxIterations[PROGRESS_PHASE_COUNT];
+ int percent[PROGRESS_PHASE_COUNT];
+ int64_t nSplit[PROGRESS_PHASE_COUNT];
+ int64_t nCollapse[PROGRESS_PHASE_COUNT];
+ int64_t nSwap[PROGRESS_PHASE_COUNT];
+ int64_t nMove[PROGRESS_PHASE_COUNT];
+} ProgressData;
+
+static const char *phaseName(int phase) {
+ switch ( phase ) {
+ case MMG5_PHASE_GEOMETRIC_MESH:
+ return "Geometric Mesh";
+ case MMG5_PHASE_COMPUTATIONAL_MESH:
+ return "Computational Mesh";
+ case MMG5_PHASE_ADAPTATION:
+ return "Adaptation";
+ case MMG5_PHASE_OPTIMIZATION:
+ return "Optimization";
+ default:
+ return "Unknown";
+ }
+}
+
+static int phaseIndex(int phase) {
+ switch ( phase ) {
+ case MMG5_PHASE_GEOMETRIC_MESH:
+ case MMG5_PHASE_COMPUTATIONAL_MESH:
+ case MMG5_PHASE_ADAPTATION:
+ case MMG5_PHASE_OPTIMIZATION:
+ return phase;
+ default:
+ return -1;
+ }
+}
+
+static void printProgressBarLine(const char *phaseName,int iteration,
+ int maxIterations,int percent,
+ int64_t nSplit,int64_t nCollapse,
+ int64_t nSwap,int64_t nMove) {
+ const int width = 30;
+ int currentIteration, filled, i;
+
+ currentIteration = iteration + 1;
+ filled = width * percent / 100;
+
+ fprintf(stdout," %-18s [",phaseName);
+ for ( i=0; iseen[idx] = 1;
+ data->iteration[idx] = iteration;
+ data->maxIterations[idx] = maxIterations;
+ data->percent[idx] = percent;
+ data->nSplit[idx] = nSplit;
+ data->nCollapse[idx] = nCollapse;
+ data->nSwap[idx] = nSwap;
+ data->nMove[idx] = nMove;
+}
+
+static void printProgressSummary(const ProgressData *data) {
+ int phase;
+
+ for ( phase=0; phaseseen[phase] ) {
+ continue;
+ }
+ printProgressBarLine(phaseName(phase),
+ data->iteration[phase],
+ data->maxIterations[phase],
+ 100,
+ data->nSplit[phase],
+ data->nCollapse[phase],
+ data->nSwap[phase],
+ data->nMove[phase]);
+ fprintf(stdout,"\n");
+ }
+}
+
+static int progressCallback(void *mesh,
+ int phase,
+ int iteration,
+ int maxIterations,
+ int64_t nSplit,
+ int64_t nCollapse,
+ int64_t nSwap,
+ int64_t nMove,
+ void *userData) {
+ ProgressData *data;
+ int percent;
+
+ (void)mesh;
+
+ data = (ProgressData *)userData;
+ if ( maxIterations > 0 ) {
+ percent = 100 * (iteration + 1) / maxIterations;
+ if ( percent > 100 ) {
+ percent = 100;
+ }
+ }
+ else {
+ maxIterations = iteration + 1;
+ percent = 100;
+ }
+
+ if ( !data ) {
+ return 1;
+ }
+
+ saveProgress(data,phase,iteration,maxIterations,percent,
+ nSplit,nCollapse,nSwap,nMove);
+
+ if ( !data->isInteractive ) {
+ return 1;
+ }
+
+ if ( data->barIsOpen && data->lastPhase != phase ) {
+ int lastPhaseIndex;
+
+ lastPhaseIndex = phaseIndex(data->lastPhase);
+ if ( lastPhaseIndex >= 0 ) {
+ clearProgressLine();
+ printProgressBarLine(phaseName(data->lastPhase),
+ data->iteration[lastPhaseIndex],
+ data->maxIterations[lastPhaseIndex],
+ 100,
+ data->nSplit[lastPhaseIndex],
+ data->nCollapse[lastPhaseIndex],
+ data->nSwap[lastPhaseIndex],
+ data->nMove[lastPhaseIndex]);
+ }
+ fprintf(stdout,"\n");
+ data->barIsOpen = 0;
+ }
+
+ data->lastPhase = phase;
+ data->barIsOpen = 1;
+
+ clearProgressLine();
+ printProgressBarLine(phaseName(phase),iteration,maxIterations,percent,
+ nSplit,nCollapse,nSwap,nMove);
+ fflush(stdout);
+
+ if ( percent == 100 ) {
+ fprintf(stdout,"\n");
+ data->lastPhase = -1;
+ data->barIsOpen = 0;
+ }
+
+ return 1;
+}
+
+int main(int argc,char *argv[]) {
+ MMG5_pMesh mmgMesh;
+ MMG5_pSol mmgSol;
+ ProgressData progressData;
+ int ier;
+ char *filename, *fileout;
+
+ fprintf(stdout," -- TEST MMG3DLIB PROGRESS CALLBACK\n");
+
+ if ( argc != 3 ) {
+ printf(" Usage: %s filein fileout \n",argv[0]);
+ return(1);
+ }
+
+ filename = (char *) calloc(strlen(argv[1]) + 1, sizeof(char));
+ if ( filename == NULL ) {
+ perror(" ## Memory problem: calloc");
+ exit(EXIT_FAILURE);
+ }
+ strcpy(filename,argv[1]);
+
+ fileout = (char *) calloc(strlen(argv[2]) + 1, sizeof(char));
+ if ( fileout == NULL ) {
+ perror(" ## Memory problem: calloc");
+ exit(EXIT_FAILURE);
+ }
+ strcpy(fileout,argv[2]);
+
+ mmgMesh = NULL;
+ mmgSol = NULL;
+
+ MMG3D_Init_mesh(MMG5_ARG_start,
+ MMG5_ARG_ppMesh,&mmgMesh,MMG5_ARG_ppMet,&mmgSol,
+ MMG5_ARG_end);
+
+ if ( MMG3D_Set_iparameter(mmgMesh,mmgSol,MMG3D_IPARAM_verbose,-1) != 1 )
+ exit(EXIT_FAILURE);
+
+ if ( MMG3D_loadMesh(mmgMesh,filename) != 1 ) exit(EXIT_FAILURE);
+
+ if ( MMG3D_loadSol(mmgMesh,mmgSol,filename) != 1 )
+ exit(EXIT_FAILURE);
+
+ if ( MMG3D_Chk_meshData(mmgMesh,mmgSol) != 1 ) exit(EXIT_FAILURE);
+
+ memset(&progressData,0,sizeof(ProgressData));
+ progressData.lastPhase = -1;
+ progressData.isInteractive = ISATTY(stdout);
+ if ( MMG3D_Set_progressCallback(mmgMesh,progressCallback,&progressData) != 1 )
+ exit(EXIT_FAILURE);
+
+ ier = MMG3D_mmg3dlib(mmgMesh,mmgSol);
+
+ if ( progressData.isInteractive && progressData.barIsOpen ) {
+ fprintf(stdout,"\n");
+ }
+ else if ( !progressData.isInteractive ) {
+ printProgressSummary(&progressData);
+ }
+
+ if ( ier == MMG5_STRONGFAILURE ) {
+ fprintf(stdout,"BAD ENDING OF MMG3DLIB: UNABLE TO SAVE MESH\n");
+ return(ier);
+ } else if ( ier == MMG5_LOWFAILURE )
+ fprintf(stdout,"BAD ENDING OF MMG3DLIB\n");
+
+ if ( MMG3D_saveMesh(mmgMesh,fileout) != 1 ) {
+ fprintf(stdout,"UNABLE TO SAVE MESH\n");
+ return(MMG5_STRONGFAILURE);
+ }
+
+ if ( MMG3D_saveSol(mmgMesh,mmgSol,fileout) != 1 ) {
+ fprintf(stdout,"UNABLE TO SAVE SOL\n");
+ return(MMG5_LOWFAILURE);
+ }
+
+ MMG3D_Free_all(MMG5_ARG_start,
+ MMG5_ARG_ppMesh,&mmgMesh,MMG5_ARG_ppMet,&mmgSol,
+ MMG5_ARG_end);
+
+ free(filename);
+ filename = NULL;
+
+ free(fileout);
+ fileout = NULL;
+
+ return(ier);
+} /** END_EXAMPLE (this line is used by Doxygen) */
diff --git a/src/common/libmmgtypes.h b/src/common/libmmgtypes.h
index fe75d6193..9a393e3c2 100644
--- a/src/common/libmmgtypes.h
+++ b/src/common/libmmgtypes.h
@@ -40,6 +40,52 @@
#ifndef _LIBMMGTYPES_H
#define _LIBMMGTYPES_H
+#define MMG5_HAS_PROGRESS_CALLBACK 1
+
+/**
+ * \enum MMG5_progressPhase
+ * \brief Phases reported by the progress callback.
+ */
+enum MMG5_progressPhase {
+ MMG5_PHASE_GEOMETRIC_MESH, /*!< Geometric mesh analysis and splitting */
+ MMG5_PHASE_COMPUTATIONAL_MESH, /*!< Metric definition and gradation */
+ MMG5_PHASE_ADAPTATION, /*!< Main adaptation loop
+ (split/collapse/swap/move) */
+ MMG5_PHASE_OPTIMIZATION /*!< Final quality optimization (swap/move) */
+};
+
+/**
+ * \typedef MMG5_progressCallback
+ * \brief Callback function type for progress reporting.
+ *
+ * Called at each iteration within a phase to report progress. If a phase
+ * converges before its maximum iteration count, a final completion callback may
+ * be emitted with iteration set to max_iterations-1.
+ *
+ * \param mesh pointer to the mesh structure (opaque to caller).
+ * \param phase current phase (\ref MMG5_progressPhase).
+ * \param iteration current iteration within the phase (0-based).
+ * \param max_iterations maximum iterations for this phase.
+ * \param n_split number of edge splits performed in this iteration.
+ * \param n_collapse number of edge collapses performed in this iteration.
+ * \param n_swap number of edge swaps performed in this iteration.
+ * \param n_move number of vertex moves performed in this iteration.
+ * \param user_data opaque pointer passed by the user.
+ *
+ * \return 1 to continue, 0 to request cancellation.
+ *
+ * \remark The callback is only invoked when set (non-NULL).
+ */
+typedef int (*MMG5_progressCallback)(void *mesh,
+ int phase,
+ int iteration,
+ int max_iterations,
+ int64_t n_split,
+ int64_t n_collapse,
+ int64_t n_swap,
+ int64_t n_move,
+ void *user_data);
+
/**
* \def MMG5_SUCCESS
*
@@ -561,6 +607,12 @@ typedef struct {
MMG5_pMat mat;
MMG5_InvMat invmat;
+
+ /* Progress callback (optional, NULL by default) */
+ MMG5_progressCallback progressCb; /**< Progress callback (NULL =
+ disabled, zero overhead) */
+ void *progressData; /**< Opaque user data forwarded
+ to progressCb */
} MMG5_Info;
/**
diff --git a/src/common/libtools.c b/src/common/libtools.c
index 2ef4c23be..0db83acdd 100644
--- a/src/common/libtools.c
+++ b/src/common/libtools.c
@@ -34,6 +34,15 @@
#include "mmgcommon_private.h"
+#if defined(_WIN32)
+#include
+#include
+#define MMG5_ISATTY(stream) _isatty(_fileno(stream))
+#else
+#include
+#define MMG5_ISATTY(stream) isatty(fileno(stream))
+#endif
+
/**
* \param mesh pointer to the mesh
* \param dim string dontaining the dimension (3D,2D or S)
@@ -219,6 +228,7 @@ void MMG5_mmgUsage(char *prog) {
fprintf(stdout,"-v [n] Tune level of verbosity, [-1..10]\n");
fprintf(stdout,"-m [n] Set maximal memory size to n Mbytes\n");
fprintf(stdout,"-d Turn on debug mode\n");
+ fprintf(stdout,"-progress Display a progress bar during remeshing\n");
fprintf(stdout,"-val Print the default parameters values\n");
fprintf(stdout,"-default Save a local parameters file for default parameters"
" values\n");
@@ -236,6 +246,211 @@ void MMG5_mmgUsage(char *prog) {
}
+static const char *MMG5_progressPhaseName(int phase) {
+ switch ( phase ) {
+ case MMG5_PHASE_GEOMETRIC_MESH:
+ return "Geometric Mesh";
+ case MMG5_PHASE_COMPUTATIONAL_MESH:
+ return "Computational Mesh";
+ case MMG5_PHASE_ADAPTATION:
+ return "Adaptation";
+ case MMG5_PHASE_OPTIMIZATION:
+ return "Optimization";
+ default:
+ return "Unknown";
+ }
+}
+
+static void MMG5_clearProgressBarLine(void) {
+ int i;
+
+ fprintf(stdout,"\r");
+ for ( i=0; i<120; ++i ) {
+ fputc(' ',stdout);
+ }
+ fprintf(stdout,"\r");
+}
+
+static int MMG5_enableAnsiProgressBar(void) {
+#if defined(_WIN32)
+ static int cached = -1;
+ HANDLE console;
+ DWORD mode;
+
+ if ( cached >= 0 ) {
+ return cached;
+ }
+
+ cached = 0;
+ console = GetStdHandle(STD_OUTPUT_HANDLE);
+ if ( console == INVALID_HANDLE_VALUE ) {
+ return cached;
+ }
+
+ if ( !GetConsoleMode(console,&mode) ) {
+ return cached;
+ }
+
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
+#endif
+
+ if ( SetConsoleMode(console,mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING) ) {
+ cached = 1;
+ }
+
+ return cached;
+#else
+ return 1;
+#endif
+}
+
+static int MMG5_useAnsiProgressBar(int isTTY) {
+ char *noColor, *term;
+
+ if ( !isTTY ) {
+ return 0;
+ }
+
+ noColor = getenv("NO_COLOR");
+ if ( noColor && noColor[0] ) {
+ return 0;
+ }
+
+ term = getenv("TERM");
+ if ( term && !strcmp(term,"dumb") ) {
+ return 0;
+ }
+
+ return MMG5_enableAnsiProgressBar();
+}
+
+static void MMG5_printProgressBarLine(const char *phaseName,int iteration,
+ int max_iterations,int percent,
+ int64_t n_split,int64_t n_collapse,
+ int64_t n_swap,int64_t n_move,
+ int useAnsi) {
+ const int width = 30;
+ int current_iteration, filled, i;
+
+ current_iteration = iteration + 1;
+ filled = (width * percent) / 100;
+
+ fprintf(stdout," %-18s ",phaseName);
+ if ( useAnsi ) {
+ fprintf(stdout,"\033[2m[\033[0m");
+ for ( i=0; i= 0);
+
+ if ( max_iterations > 0 ) {
+ percent = (100 * (iteration + 1)) / max_iterations;
+ }
+ else {
+ max_iterations = iteration + 1;
+ percent = 100;
+ }
+ percent = MG_MIN(percent,100);
+ phaseName = MMG5_progressPhaseName(phase);
+ mmgMesh = (MMG5_pMesh)mesh;
+ isTTY = MMG5_ISATTY(stdout);
+ lineByLine = isTTY && mmgMesh &&
+ ( abs(mmgMesh->info.imprim) >= 5 || mmgMesh->info.ddebug );
+ live = isTTY && !lineByLine;
+ useAnsi = live && MMG5_useAnsiProgressBar(isTTY);
+
+ if ( lineByLine ) {
+ MMG5_printProgressBarLine(phaseName,iteration,max_iterations,percent,
+ n_split,n_collapse,n_swap,n_move,0);
+ fprintf(stdout,"\n");
+ return 1;
+ }
+
+ if ( previousPhase != MMG5_UNSET && previousPhase != phase ) {
+ if ( live ) {
+ MMG5_clearProgressBarLine();
+ }
+ MMG5_printProgressBarLine(MMG5_progressPhaseName(previousPhase),
+ previousIteration,previousMaxIterations,
+ 100,previousNSplit,
+ previousNCollapse,previousNSwap,
+ previousNMove,useAnsi);
+ fprintf(stdout,"\n");
+ previousPhase = MMG5_UNSET;
+ }
+
+ if ( percent == 100 ) {
+ if ( live ) {
+ MMG5_clearProgressBarLine();
+ }
+ MMG5_printProgressBarLine(phaseName,iteration,max_iterations,percent,
+ n_split,n_collapse,n_swap,n_move,useAnsi);
+ fprintf(stdout,"\n");
+ previousPhase = MMG5_UNSET;
+ }
+ else {
+ previousPhase = phase;
+ previousIteration = iteration;
+ previousMaxIterations = max_iterations;
+ previousNSplit = n_split;
+ previousNCollapse = n_collapse;
+ previousNSwap = n_swap;
+ previousNMove = n_move;
+
+ if ( live ) {
+ MMG5_clearProgressBarLine();
+ MMG5_printProgressBarLine(phaseName,iteration,max_iterations,percent,
+ n_split,n_collapse,n_swap,n_move,useAnsi);
+ fflush(stdout);
+ }
+ }
+
+ return 1;
+}
+
/**
*
* Print help for common parameters options of the 3 codes (first section).
diff --git a/src/common/mmgcommon_private.h b/src/common/mmgcommon_private.h
index 6c73ef4ee..058838328 100644
--- a/src/common/mmgcommon_private.h
+++ b/src/common/mmgcommon_private.h
@@ -668,6 +668,7 @@ typedef struct MMG5_iNode_s {
int MMG5_mmgHashTria(MMG5_pMesh mesh, MMG5_int *adja, MMG5_Hash*, int chkISO);
void MMG5_mmgInit_parameters(MMG5_pMesh mesh);
void MMG5_mmgUsage(char *prog);
+ int MMG5_cliProgressBar(void*,int,int,int,int64_t,int64_t,int64_t,int64_t,void*);
void MMG5_paramUsage1(void);
void MMG5_paramUsage2(void);
void MMG5_2d3dUsage(void);
diff --git a/src/mmg2d/API_functions_2d.c b/src/mmg2d/API_functions_2d.c
index 3205094b3..503dd48fe 100644
--- a/src/mmg2d/API_functions_2d.c
+++ b/src/mmg2d/API_functions_2d.c
@@ -1950,3 +1950,11 @@ int MMG2D_Free_names(const int starter,...)
return ier;
}
+
+int MMG2D_Set_progressCallback(MMG5_pMesh mesh,
+ MMG5_progressCallback callback,
+ void *user_data) {
+ mesh->info.progressCb = callback;
+ mesh->info.progressData = user_data;
+ return 1;
+}
diff --git a/src/mmg2d/libmmg2d.h b/src/mmg2d/libmmg2d.h
index f0097d566..501d27a62 100644
--- a/src/mmg2d/libmmg2d.h
+++ b/src/mmg2d/libmmg2d.h
@@ -2769,6 +2769,21 @@ LIBMMG2D_EXPORT int MMG2D_Free_all(const int starter,...);
*/
LIBMMG2D_EXPORT int MMG2D_scaleMesh(MMG5_pMesh mesh,MMG5_pSol met,MMG5_pSol ls);
+/**
+ * \brief Set a progress callback for the remeshing process.
+ *
+ * \param mesh pointer to the mesh structure.
+ * \param callback progress callback function (NULL to disable).
+ * \param user_data opaque pointer forwarded to every callback invocation.
+ *
+ * \return 1 on success.
+ *
+ * \sa MMG5_progressCallback, MMG3D_Set_progressCallback.
+ */
+ LIBMMG2D_EXPORT int MMG2D_Set_progressCallback(MMG5_pMesh mesh,
+ MMG5_progressCallback callback,
+ void *user_data);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/mmg2d/libmmg2d_tools.c b/src/mmg2d/libmmg2d_tools.c
index 0ebf7e9ab..618bff973 100644
--- a/src/mmg2d/libmmg2d_tools.c
+++ b/src/mmg2d/libmmg2d_tools.c
@@ -373,6 +373,17 @@ int MMG2D_parsar(int argc,char *argv[],MMG5_pMesh mesh,MMG5_pSol met,MMG5_pSol s
return 0;
}
break;
+ case 'p':
+ if ( !strcmp(argv[i],"-progress") ) {
+ if ( !MMG2D_Set_progressCallback(mesh,MMG5_cliProgressBar,NULL) )
+ return 0;
+ }
+ else {
+ fprintf(stderr,"\nUnrecognized option %s\n",argv[i]);
+ MMG2D_usage(argv[0]);
+ return 0;
+ }
+ break;
case 'r':
if ( !strcmp(argv[i],"-rmc") ) {
if ( !MMG2D_Set_dparameter(mesh,met,MMG2D_DPARAM_rmc,0) )
diff --git a/src/mmg2d/mmg2d1.c b/src/mmg2d/mmg2d1.c
index 4b4f7704e..bf270619e 100644
--- a/src/mmg2d/mmg2d1.c
+++ b/src/mmg2d/mmg2d1.c
@@ -96,10 +96,46 @@ int MMG2D_anatri(MMG5_pMesh mesh,MMG5_pSol met,int8_t typchk) {
if ( (abs(mesh->info.imprim) > 4 || mesh->info.ddebug) && ns+nc > 0 )
fprintf(stdout," %8" MMG5_PRId " splitted, %8" MMG5_PRId " collapsed, %8" MMG5_PRId " swapped\n",ns,nc,nsw);
+
+ /* Progress callback */
+ if ( mesh->info.progressCb ) {
+ int phase = (typchk == 1) ?
+ MMG5_PHASE_GEOMETRIC_MESH :
+ MMG5_PHASE_COMPUTATIONAL_MESH;
+ if ( !mesh->info.progressCb(
+ mesh, phase, it, maxit,
+ (int64_t)ns, (int64_t)nc,
+ (int64_t)nsw, 0,
+ mesh->info.progressData) ) {
+ fprintf(stderr,
+ "\n ## %s cancelled by user callback.\n",
+ (typchk == 1) ?
+ "Geometric mesh" : "Computational mesh");
+ return 0;
+ }
+ }
+
if ( it > 3 && MMG5_abs(nc-ns) < 0.1 * MG_MAX(nc,ns) ) break;
}
while ( ++it < maxit && ns+nc+nsw >0 );
+ if ( mesh->info.progressCb && it+1 < maxit ) {
+ int phase = (typchk == 1) ?
+ MMG5_PHASE_GEOMETRIC_MESH :
+ MMG5_PHASE_COMPUTATIONAL_MESH;
+ if ( !mesh->info.progressCb(
+ mesh, phase, maxit-1, maxit,
+ (int64_t)ns, (int64_t)nc,
+ (int64_t)nsw, 0,
+ mesh->info.progressData) ) {
+ fprintf(stderr,
+ "\n ## %s cancelled by user callback.\n",
+ (typchk == 1) ?
+ "Geometric mesh" : "Computational mesh");
+ return 0;
+ }
+ }
+
if ( mesh->info.imprim > 0 ) {
if ( (abs(mesh->info.imprim) < 5 || mesh->info.ddebug ) && nns+nnc > 0 )
fprintf(stdout," %8" MMG5_PRId " splitted, %8" MMG5_PRId " collapsed, %8" MMG5_PRId " swapped, %d iter.\n",nns,nnc,nnsw,it);
@@ -606,11 +642,31 @@ int MMG2D_adptri(MMG5_pMesh mesh,MMG5_pSol met) {
if ( (abs(mesh->info.imprim) > 4 || mesh->info.ddebug) && ns+nc+nsw+nm > 0 )
fprintf(stdout," %8" MMG5_PRId " splitted, %8" MMG5_PRId " collapsed, %8" MMG5_PRId " swapped, %8" MMG5_PRId " moved\n",ns,nc,nsw,nm);
+
+ /* Progress callback */
+ if ( mesh->info.progressCb ) {
+ if ( !mesh->info.progressCb(mesh, MMG5_PHASE_ADAPTATION, it, maxit,
+ (int64_t)ns, (int64_t)nc, (int64_t)nsw,
+ (int64_t)nm, mesh->info.progressData) ) {
+ fprintf(stderr,"\n ## Remeshing cancelled by user callback.\n");
+ return 0;
+ }
+ }
+
if ( ns < 10 && MMG5_abs(nc-ns) < 3 ) break;
else if ( it > 3 && MMG5_abs(nc-ns) < 0.3 * MG_MAX(nc,ns) ) break;
}
while( ++it < maxit && (nc+ns+nsw+nm > 0) );
+ if ( mesh->info.progressCb && it+1 < maxit ) {
+ if ( !mesh->info.progressCb(mesh, MMG5_PHASE_ADAPTATION, maxit-1, maxit,
+ (int64_t)ns, (int64_t)nc, (int64_t)nsw,
+ (int64_t)nm, mesh->info.progressData) ) {
+ fprintf(stderr,"\n ## Remeshing cancelled by user callback.\n");
+ return 0;
+ }
+ }
+
/* Last iterations of vertex relocation only */
if ( !mesh->info.nomove ) {
nm = MMG2D_movtri(mesh,met,5,1);
diff --git a/src/mmg3d/API_functions_3d.c b/src/mmg3d/API_functions_3d.c
index d86290777..9d2b4c8fc 100644
--- a/src/mmg3d/API_functions_3d.c
+++ b/src/mmg3d/API_functions_3d.c
@@ -2585,3 +2585,11 @@ int MMG3D_Free_names(const int starter,...)
return ier;
}
+
+int MMG3D_Set_progressCallback(MMG5_pMesh mesh,
+ MMG5_progressCallback callback,
+ void *user_data) {
+ mesh->info.progressCb = callback;
+ mesh->info.progressData = user_data;
+ return 1;
+}
diff --git a/src/mmg3d/libmmg3d.h b/src/mmg3d/libmmg3d.h
index 26d3c265c..e0d5054dc 100644
--- a/src/mmg3d/libmmg3d.h
+++ b/src/mmg3d/libmmg3d.h
@@ -3458,6 +3458,31 @@ LIBMMG3D_EXPORT int MMG3D_loadVtuMesh_and_allData(MMG5_pMesh mesh,MMG5_pSol *sol
* functions.
*/
LIBMMG3D_EXPORT void MMG3D_Set_commonFunc(void);
+
+/**
+ * \brief Set a progress callback for the remeshing process.
+ *
+ * \param mesh pointer to the mesh structure.
+ * \param callback progress callback function (NULL to disable).
+ * \param user_data opaque pointer forwarded to every callback invocation.
+ *
+ * \return 1 on success.
+ *
+ * The callback is invoked at each iteration of every remeshing phase:
+ * geometric mesh, computational mesh, adaptation loop, and optimization.
+ * Returning 0 from the callback requests graceful cancellation.
+ *
+ * When \a callback is NULL (the default), no function-pointer check is
+ * performed in the hot loops, so there is zero overhead.
+ *
+ * \remark No Fortran interface.
+ *
+ * \sa MMG5_progressCallback for the callback signature.
+ */
+ LIBMMG3D_EXPORT int MMG3D_Set_progressCallback(MMG5_pMesh mesh,
+ MMG5_progressCallback callback,
+ void *user_data);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/mmg3d/libmmg3d_tools.c b/src/mmg3d/libmmg3d_tools.c
index 43fa2b7d0..8ed2ee0f4 100644
--- a/src/mmg3d/libmmg3d_tools.c
+++ b/src/mmg3d/libmmg3d_tools.c
@@ -240,7 +240,7 @@ int MMG3D_storeknownar(int argc,char *argv[],MMG5_pMesh mesh,MMG5_pSol met,
MMG5_pSol tmp = NULL;
double val;
int i;
- char namein[MMG5_FILESTR_LGTH],*endptr;
+ char *endptr;
int param;
i = 1;
@@ -540,6 +540,16 @@ int MMG3D_storeknownar(int argc,char *argv[],MMG5_pMesh mesh,MMG5_pSol met,
MMG_ARGV_APPEND(argv, mmgArgv, i, *mmgArgc,return 0);
}
break;
+ case 'p':
+ if ( !strcmp(argv[i],"-progress") ) {
+ if ( !MMG3D_Set_progressCallback(mesh,MMG5_cliProgressBar,NULL) )
+ return 0;
+ }
+ else {
+ /* Arg unknown by Mmg: arg starts with -p but is not known */
+ MMG_ARGV_APPEND(argv, mmgArgv, i, *mmgArgc,return 0);
+ }
+ break;
case 'r':
if ( !strcmp(argv[i],"-rmc") ) {
if ( !MMG3D_Set_dparameter(mesh,met,MMG3D_DPARAM_rmc,0) )
diff --git a/src/mmg3d/mmg3d1.c b/src/mmg3d/mmg3d1.c
index dc005e3c7..e99e9d975 100644
--- a/src/mmg3d/mmg3d1.c
+++ b/src/mmg3d/mmg3d1.c
@@ -3263,6 +3263,24 @@ int MMG5_anatet(MMG5_pMesh mesh,MMG5_pSol met,int8_t typchk, int patternMode) {
fprintf(stdout," %8" MMG5_PRId " splitted, %8" MMG5_PRId " collapsed, %8" MMG5_PRId " swapped\n",ns,nc,nf);
}
+ /* Progress callback */
+ if ( mesh->info.progressCb ) {
+ int phase = (typchk == 1) ?
+ MMG5_PHASE_GEOMETRIC_MESH :
+ MMG5_PHASE_COMPUTATIONAL_MESH;
+ if ( !mesh->info.progressCb(
+ mesh, phase, it, maxit,
+ (int64_t)ns, (int64_t)nc,
+ (int64_t)nf, 0,
+ mesh->info.progressData) ) {
+ fprintf(stderr,
+ "\n ## %s cancelled by user callback.\n",
+ (typchk == 1) ?
+ "Geometric mesh" : "Computational mesh");
+ return 0;
+ }
+ }
+
if ( it > minit-1 && ( !(ns+nc) || (MMG5_abs(nc-ns) < 0.1 * MG_MAX(nc,ns)) ) ) {
++lastit;
if ( it > minit && lastit>2 ) break;
@@ -3280,6 +3298,23 @@ int MMG5_anatet(MMG5_pMesh mesh,MMG5_pSol met,int8_t typchk, int patternMode) {
}
while ( ++it < maxit && (ns+nc+nf > 0 || lastit<3) );
+ if ( mesh->info.progressCb && it+1 < maxit ) {
+ int phase = (typchk == 1) ?
+ MMG5_PHASE_GEOMETRIC_MESH :
+ MMG5_PHASE_COMPUTATIONAL_MESH;
+ if ( !mesh->info.progressCb(
+ mesh, phase, maxit-1, maxit,
+ (int64_t)ns, (int64_t)nc,
+ (int64_t)nf, 0,
+ mesh->info.progressData) ) {
+ fprintf(stderr,
+ "\n ## %s cancelled by user callback.\n",
+ (typchk == 1) ?
+ "Geometric mesh" : "Computational mesh");
+ return 0;
+ }
+ }
+
if ( mesh->info.imprim > 0 ) {
if ( (abs(mesh->info.imprim) < 5 || mesh->info.ddebug ) && nns+nnc > 0 ) {
#ifndef MMG_PATTERN
diff --git a/src/mmg3d/mmg3d1_delone.c b/src/mmg3d/mmg3d1_delone.c
index 62911114b..a434adf4f 100644
--- a/src/mmg3d/mmg3d1_delone.c
+++ b/src/mmg3d/mmg3d1_delone.c
@@ -614,9 +614,28 @@ int MMG5_optbad(MMG5_pMesh mesh, MMG5_pSol met,MMG3D_pPROctree PROctree) {
fprintf(stdout," ");
fprintf(stdout," %8" MMG5_PRId " improved, %8" MMG5_PRId " swapped, %8" MMG5_PRId " moved\n",nw,nf,nm);
}
+
+ /* Progress callback */
+ if ( mesh->info.progressCb ) {
+ if ( !mesh->info.progressCb(mesh, MMG5_PHASE_OPTIMIZATION, it, maxit,
+ 0, 0, (int64_t)nf, (int64_t)nm,
+ mesh->info.progressData) ) {
+ fprintf(stderr,"\n ## Optimization cancelled by user callback.\n");
+ return 0;
+ }
+ }
}
while( ++it < maxit && nw+nm+nf > 0 );
+ if ( mesh->info.progressCb && it+1 < maxit ) {
+ if ( !mesh->info.progressCb(mesh, MMG5_PHASE_OPTIMIZATION, maxit-1, maxit,
+ 0, 0, (int64_t)nf, (int64_t)nm,
+ mesh->info.progressData) ) {
+ fprintf(stderr,"\n ## Optimization cancelled by user callback.\n");
+ return 0;
+ }
+ }
+
if ( mesh->info.imprim > 0 ) {
if ( abs(mesh->info.imprim) < 5 && (nnf > 0 || nnm > 0) )
fprintf(stdout," "
@@ -716,10 +735,22 @@ int MMG5_adpdel(MMG5_pMesh mesh,MMG5_pSol met,MMG3D_pPROctree *PROctree, int* wa
fprintf(stdout," %8"MMG5_PRId" filtered, %8" MMG5_PRId " splitted, %8" MMG5_PRId " collapsed,"
" %8" MMG5_PRId " swapped, %8" MMG5_PRId " moved\n",ifilt,ns,nc,nf,nm);
+ /* Progress callback */
+ if ( mesh->info.progressCb ) {
+ if ( !mesh->info.progressCb(mesh, MMG5_PHASE_ADAPTATION, it, maxit,
+ (int64_t)ns, (int64_t)nc, (int64_t)nf,
+ (int64_t)nm, mesh->info.progressData) ) {
+ fprintf(stderr,"\n ## Remeshing cancelled by user callback.\n");
+ return 0;
+ }
+ }
+
/*optimization*/
dd = MMG5_abs(nc-ns);
if ( !noptim && (it==5 || ((dd < 5) || (dd < 0.05*MG_MAX(nc,ns)) || !(ns+nc))) ) {
- MMG5_optbad(mesh,met,*PROctree);
+ if ( !MMG5_optbad(mesh,met,*PROctree) ) {
+ return 0;
+ }
noptim = 1;
}
@@ -733,6 +764,15 @@ int MMG5_adpdel(MMG5_pMesh mesh,MMG5_pSol met,MMG3D_pPROctree *PROctree, int* wa
}
while( ++it < maxit && (noptim || nc+ns > 0) );
+ if ( mesh->info.progressCb && it+1 < maxit ) {
+ if ( !mesh->info.progressCb(mesh, MMG5_PHASE_ADAPTATION, maxit-1, maxit,
+ (int64_t)ns, (int64_t)nc, (int64_t)nf,
+ (int64_t)nm, mesh->info.progressData) ) {
+ fprintf(stderr,"\n ## Remeshing cancelled by user callback.\n");
+ return 0;
+ }
+ }
+
if ( mesh->info.imprim > 0 ) {
if ( (abs(mesh->info.imprim) < 5) && ( nnc || nns ) ) {
fprintf(stdout," %8"MMG5_PRId" filtered, %8" MMG5_PRId " splitted, %8" MMG5_PRId " collapsed,"
@@ -806,9 +846,28 @@ int MMG5_optetLES(MMG5_pMesh mesh, MMG5_pSol met,MMG3D_pPROctree PROctree) {
fprintf(stdout," ");
fprintf(stdout," %8" MMG5_PRId " improved, %8" MMG5_PRId " swapped, %8" MMG5_PRId " moved\n",nw,nf,nm);
}
+
+ /* Progress callback */
+ if ( mesh->info.progressCb ) {
+ if ( !mesh->info.progressCb(mesh, MMG5_PHASE_OPTIMIZATION, it, maxit,
+ 0, 0, (int64_t)nf, (int64_t)nm,
+ mesh->info.progressData) ) {
+ fprintf(stderr,"\n ## Optimization cancelled by user callback.\n");
+ return 0;
+ }
+ }
}
while( ++it < maxit && nw+nm+nf > 0 );
+ if ( mesh->info.progressCb && it+1 < maxit ) {
+ if ( !mesh->info.progressCb(mesh, MMG5_PHASE_OPTIMIZATION, maxit-1, maxit,
+ 0, 0, (int64_t)nf, (int64_t)nm,
+ mesh->info.progressData) ) {
+ fprintf(stderr,"\n ## Optimization cancelled by user callback.\n");
+ return 0;
+ }
+ }
+
if ( !mesh->info.nomove ) {
/* move for tria with qualinfo.progressCb ) {
+ if ( !mesh->info.progressCb(mesh, MMG5_PHASE_OPTIMIZATION, it, maxit,
+ 0, 0, (int64_t)nf, (int64_t)nm,
+ mesh->info.progressData) ) {
+ fprintf(stderr,"\n ## Optimization cancelled by user callback.\n");
+ return 0;
+ }
+ }
+
if ( it > 3 ) {
if ( !nw && (!nm || !nf) ) break;
}
}
while( ++it < maxit && nw+nm+nf > 0 );
+ if ( mesh->info.progressCb && it+1 < maxit ) {
+ if ( !mesh->info.progressCb(mesh, MMG5_PHASE_OPTIMIZATION, maxit-1, maxit,
+ 0, 0, (int64_t)nf, (int64_t)nm,
+ mesh->info.progressData) ) {
+ fprintf(stderr,"\n ## Optimization cancelled by user callback.\n");
+ return 0;
+ }
+ }
+
if ( !mesh->info.nomove ) {
/* move for tria with qual<1., tetra with qual<1., internal move allowed,
* surface degradation forbidden, volume degradation during the surface and
diff --git a/src/mmg3d/mmg3d1_pattern.c b/src/mmg3d/mmg3d1_pattern.c
index 643271929..642f7ac31 100644
--- a/src/mmg3d/mmg3d1_pattern.c
+++ b/src/mmg3d/mmg3d1_pattern.c
@@ -323,11 +323,31 @@ static int MMG5_adptet(MMG5_pMesh mesh,MMG5_pSol met,MMG5_int *permNodGlob) {
if ( (abs(mesh->info.imprim) > 4 || mesh->info.ddebug) && ns+nc > 0 )
fprintf(stdout," %8" MMG5_PRId " splitted, %8" MMG5_PRId " collapsed, %8" MMG5_PRId " swapped, %8" MMG5_PRId " moved\n",ns,nc,nf,nm);
+
+ /* Progress callback */
+ if ( mesh->info.progressCb ) {
+ if ( !mesh->info.progressCb(mesh, MMG5_PHASE_ADAPTATION, it, maxit,
+ (int64_t)ns, (int64_t)nc, (int64_t)nf,
+ (int64_t)nm, mesh->info.progressData) ) {
+ fprintf(stderr,"\n ## Remeshing cancelled by user callback.\n");
+ return 0;
+ }
+ }
+
if ( ns < 10 && MMG5_abs(nc-ns) < 3 ) break;
else if ( it > 3 && MMG5_abs(nc-ns) < 0.3 * MG_MAX(nc,ns) ) break;
}
while( ++it < maxit && nc+ns > 0 );
+ if ( mesh->info.progressCb && it+1 < maxit ) {
+ if ( !mesh->info.progressCb(mesh, MMG5_PHASE_ADAPTATION, maxit-1, maxit,
+ (int64_t)ns, (int64_t)nc, (int64_t)nf,
+ (int64_t)nm, mesh->info.progressData) ) {
+ fprintf(stderr,"\n ## Remeshing cancelled by user callback.\n");
+ return 0;
+ }
+ }
+
if ( warn ) {
fprintf(stderr,"\n ## Error: %s: unable to allocate a new point in last"
" call of MMG5_adpspl.\n",__func__);
@@ -387,9 +407,28 @@ static int MMG5_adptet(MMG5_pMesh mesh,MMG5_pSol met,MMG5_int *permNodGlob) {
fprintf(stdout," ");
fprintf(stdout,"%8" MMG5_PRId " swapped, %8" MMG5_PRId " moved\n",nf,nm);
}
+
+ /* Progress callback */
+ if ( mesh->info.progressCb ) {
+ if ( !mesh->info.progressCb(mesh, MMG5_PHASE_OPTIMIZATION, it, maxit,
+ 0, 0, (int64_t)nf, (int64_t)nm,
+ mesh->info.progressData) ) {
+ fprintf(stderr,"\n ## Optimization cancelled by user callback.\n");
+ return 0;
+ }
+ }
}
while( ++it < maxit && /*nw+*/nm+nf > 0 );
+ if ( mesh->info.progressCb && it+1 < maxit ) {
+ if ( !mesh->info.progressCb(mesh, MMG5_PHASE_OPTIMIZATION, maxit-1, maxit,
+ 0, 0, (int64_t)nf, (int64_t)nm,
+ mesh->info.progressData) ) {
+ fprintf(stderr,"\n ## Optimization cancelled by user callback.\n");
+ return 0;
+ }
+ }
+
if ( !mesh->info.nomove ) {
nm = MMG5_movtet(mesh,met,NULL,MMG3D_MAXKAL,MMG3D_MAXKAL,1,1,1,1,3,mesh->mark-2);
if ( nm < 0 ) {
diff --git a/src/mmgs/API_functions_s.c b/src/mmgs/API_functions_s.c
index 71cefa643..c99873cb3 100644
--- a/src/mmgs/API_functions_s.c
+++ b/src/mmgs/API_functions_s.c
@@ -1728,3 +1728,11 @@ int MMGS_Free_names(const int starter,...)
return ier;
}
+
+int MMGS_Set_progressCallback(MMG5_pMesh mesh,
+ MMG5_progressCallback callback,
+ void *user_data) {
+ mesh->info.progressCb = callback;
+ mesh->info.progressData = user_data;
+ return 1;
+}
diff --git a/src/mmgs/libmmgs.h b/src/mmgs/libmmgs.h
index c9de5efd5..4c0361617 100644
--- a/src/mmgs/libmmgs.h
+++ b/src/mmgs/libmmgs.h
@@ -2576,6 +2576,21 @@ LIBMMGS_EXPORT void MMGS_Free_solutions(MMG5_pMesh mesh,MMG5_pSol sol);
*/
LIBMMGS_EXPORT void MMGS_Set_commonFunc(void);
+/**
+ * \brief Set a progress callback for the remeshing process.
+ *
+ * \param mesh pointer to the mesh structure.
+ * \param callback progress callback function (NULL to disable).
+ * \param user_data opaque pointer forwarded to every callback invocation.
+ *
+ * \return 1 on success.
+ *
+ * \sa MMG5_progressCallback, MMG3D_Set_progressCallback.
+ */
+LIBMMGS_EXPORT int MMGS_Set_progressCallback(MMG5_pMesh mesh,
+ MMG5_progressCallback callback,
+ void *user_data);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/mmgs/libmmgs_tools.c b/src/mmgs/libmmgs_tools.c
index 89f3e884e..729ce6354 100644
--- a/src/mmgs/libmmgs_tools.c
+++ b/src/mmgs/libmmgs_tools.c
@@ -386,6 +386,17 @@ int MMGS_parsar(int argc,char *argv[],MMG5_pMesh mesh,MMG5_pSol met,MMG5_pSol so
return 0;
}
break;
+ case 'p':
+ if ( !strcmp(argv[i],"-progress") ) {
+ if ( !MMGS_Set_progressCallback(mesh,MMG5_cliProgressBar,NULL) )
+ return 0;
+ }
+ else {
+ fprintf(stderr,"\nUnrecognized option %s\n",argv[i]);
+ MMGS_usage(argv[0]);
+ return 0;
+ }
+ break;
case 'r':
if ( !strcmp(argv[i],"-rmc") ) {
if ( !MMGS_Set_dparameter(mesh,met,MMGS_DPARAM_rmc,0) )
diff --git a/src/mmgs/mmgs1.c b/src/mmgs/mmgs1.c
index e619e8527..f3cbaea4e 100644
--- a/src/mmgs/mmgs1.c
+++ b/src/mmgs/mmgs1.c
@@ -1324,11 +1324,31 @@ static int adptri(MMG5_pMesh mesh,MMG5_pSol met,MMG5_int* permNodGlob) {
nnm += nm;
if ( (abs(mesh->info.imprim) > 4 || mesh->info.ddebug) && ns+nc+nf+nm > 0 )
fprintf(stdout," %8" MMG5_PRId " splitted, %8" MMG5_PRId " collapsed, %8" MMG5_PRId " swapped, %8" MMG5_PRId " moved\n",ns,nc,nf,nm);
+
+ /* Progress callback */
+ if ( mesh->info.progressCb ) {
+ if ( !mesh->info.progressCb(mesh, MMG5_PHASE_ADAPTATION, it, maxit,
+ (int64_t)ns, (int64_t)nc, (int64_t)nf,
+ (int64_t)nm, mesh->info.progressData) ) {
+ fprintf(stderr,"\n ## Remeshing cancelled by user callback.\n");
+ return 0;
+ }
+ }
+
if ( ns < 10 && MMG5_abs(nc-ns) < 3 ) break;
else if ( it > 3 && MMG5_abs(nc-ns) < 0.3 * MG_MAX(nc,ns) ) break;
}
while( ++it < maxit && nc+ns > 0 );
+ if ( mesh->info.progressCb && it+1 < maxit ) {
+ if ( !mesh->info.progressCb(mesh, MMG5_PHASE_ADAPTATION, maxit-1, maxit,
+ (int64_t)ns, (int64_t)nc, (int64_t)nf,
+ (int64_t)nm, mesh->info.progressData) ) {
+ fprintf(stderr,"\n ## Remeshing cancelled by user callback.\n");
+ return 0;
+ }
+ }
+
/* renumbering if available */
if ( !MMG5_scotchCall(mesh,met,NULL,permNodGlob) )
return 0;
@@ -1362,9 +1382,28 @@ static int adptri(MMG5_pMesh mesh,MMG5_pSol met,MMG5_int* permNodGlob) {
fprintf(stdout," ");
fprintf(stdout,"%8" MMG5_PRId " swapped, %8" MMG5_PRId " moved\n",nf,nm);
}
+
+ /* Progress callback */
+ if ( mesh->info.progressCb ) {
+ if ( !mesh->info.progressCb(mesh, MMG5_PHASE_OPTIMIZATION, it, maxit,
+ 0, 0, (int64_t)nf, (int64_t)nm,
+ mesh->info.progressData) ) {
+ fprintf(stderr,"\n ## Optimization cancelled by user callback.\n");
+ return 0;
+ }
+ }
}
while( ++it < maxit && nm+nf > 0 );
+ if ( mesh->info.progressCb && it+1 < maxit ) {
+ if ( !mesh->info.progressCb(mesh, MMG5_PHASE_OPTIMIZATION, maxit-1, maxit,
+ 0, 0, (int64_t)nf, (int64_t)nm,
+ mesh->info.progressData) ) {
+ fprintf(stderr,"\n ## Optimization cancelled by user callback.\n");
+ return 0;
+ }
+ }
+
if ( !mesh->info.nomove ) {
nm = movtri(mesh,met,5);
if ( nm < 0 ) {
@@ -1438,10 +1477,46 @@ static int anatri(MMG5_pMesh mesh,MMG5_pSol met,int8_t typchk) {
nnf += nf;
if ( (abs(mesh->info.imprim) > 4 || mesh->info.ddebug) && ns+nc > 0 )
fprintf(stdout," %8" MMG5_PRId " splitted, %8" MMG5_PRId " collapsed, %8" MMG5_PRId " swapped\n",ns,nc,nf);
+
+ /* Progress callback */
+ if ( mesh->info.progressCb ) {
+ int phase = (typchk == 1) ?
+ MMG5_PHASE_GEOMETRIC_MESH :
+ MMG5_PHASE_COMPUTATIONAL_MESH;
+ if ( !mesh->info.progressCb(
+ mesh, phase, it, maxit,
+ (int64_t)ns, (int64_t)nc,
+ (int64_t)nf, 0,
+ mesh->info.progressData) ) {
+ fprintf(stderr,
+ "\n ## %s cancelled by user callback.\n",
+ (typchk == 1) ?
+ "Geometric mesh" : "Computational mesh");
+ return 0;
+ }
+ }
+
if ( it > 3 && MMG5_abs(nc-ns) < 0.1 * MG_MAX(nc,ns) ) break;
}
while ( ++it < maxit && ns+nc+nf > 0 );
+ if ( mesh->info.progressCb && it+1 < maxit ) {
+ int phase = (typchk == 1) ?
+ MMG5_PHASE_GEOMETRIC_MESH :
+ MMG5_PHASE_COMPUTATIONAL_MESH;
+ if ( !mesh->info.progressCb(
+ mesh, phase, maxit-1, maxit,
+ (int64_t)ns, (int64_t)nc,
+ (int64_t)nf, 0,
+ mesh->info.progressData) ) {
+ fprintf(stderr,
+ "\n ## %s cancelled by user callback.\n",
+ (typchk == 1) ?
+ "Geometric mesh" : "Computational mesh");
+ return 0;
+ }
+ }
+
if ( mesh->info.imprim > 0 ) {
if ( (abs(mesh->info.imprim) < 5 || mesh->info.ddebug ) && nns+nnc > 0 )
fprintf(stdout," %8" MMG5_PRId " splitted, %8" MMG5_PRId " collapsed, %8" MMG5_PRId " swapped, %d iter.\n",nns,nnc,nnf,it);