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);