star-cad

Geometric operators for computer-aided design
git clone git://git.meso-star.fr/star-cad.git
Log | Files | Refs | README | LICENSE

scad_device.c (21326B)


      1 /* Copyright (C) 2022-2024 |Méso|Star> (contact@meso-star.com)
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 3 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     15 
     16 #include "rsys/str.h"
     17 #include "scad.h"
     18 #include "scad_c.h"
     19 #include "scad_device.h"
     20 #include "scad_geometry.h"
     21 
     22 #include <rsys/logger.h>
     23 #include <rsys/mem_allocator.h>
     24 #include <rsys/ref_count.h>
     25 #include <rsys/cstr.h>
     26 
     27 #include <gmsh/gmshc.h>
     28 #include <rsys/rsys.h>
     29 
     30 /*******************************************************************************
     31  * The unique device in scad-cad
     32  ******************************************************************************/
     33 static struct scad_device* g_device = NULL;
     34 
     35 /*******************************************************************************
     36  * Local functions
     37  ******************************************************************************/
     38 static res_T
     39 device_release(void)
     40 {
     41   res_T res = RES_OK;
     42   int log, empty;
     43   enum scad_log_refcounting option_ref;
     44   int option_dec;
     45   enum log_type log_type;
     46   struct mem_allocator* allocator;
     47 
     48   ASSERT(g_device);
     49 
     50   allocator = g_device->allocator;
     51   option_ref = g_device->options.Misc.LogRefCounting;
     52   option_dec = g_device->options.Misc.DebugEmptyContext;
     53   empty = htable_geometries_is_empty(&g_device->allgeom);
     54   log_type = empty ? LOG_OUTPUT : LOG_WARNING;
     55   log = (option_ref & SCAD_LOG_DIMTAGS_ALL)
     56     || (!empty && (option_ref & SCAD_LOG_DIMTAGS_ONLY_UNDELETED));
     57   g_device->log = log;
     58   g_device->log_type = log_type;
     59 
     60   if(empty) {
     61     if(log) logger_print(g_device->logger, log_type, "No scad geometry.\n");
     62   } else {
     63     struct htable_geometries tmp;
     64     struct htable_geometries_iterator it, end;
     65     /* Duplicate the htable we iterate on as dev->allgeom will be altered during
     66      * the process (through calls to geometry_release) */
     67     htable_geometries_init(allocator, &tmp);
     68     CHK(RES_OK == htable_geometries_copy(&tmp, &g_device->allgeom));
     69     htable_geometries_begin(&tmp, &it);
     70     htable_geometries_end(&tmp, &end);
     71     while(!htable_geometries_iterator_eq(&it, &end)) {
     72       struct scad_geometry* geom = *htable_geometries_iterator_key_get(&it);
     73       long cpt = geom->ref;
     74       while(cpt-- > 0) {
     75         SCAD(geometry_ref_put(geom));
     76       }
     77       htable_geometries_iterator_next(&it);
     78     }
     79     htable_geometries_release(&tmp);
     80   }
     81   htable_names_release(&g_device->geometry_names);
     82   htable_geometries_release(&g_device->allgeom);
     83   htable_tags2desc_release(g_device->tags2desc);
     84   htable_tags2desc_release(g_device->tags2desc+1);
     85   htable_size_modifiers_release(&g_device->size_modifiers_by_dim[0]);
     86   htable_size_modifiers_release(&g_device->size_modifiers_by_dim[1]);
     87   htable_size_modifiers_release(&g_device->size_modifiers_by_dim[2]);
     88   htable_size_modifiers_release(&g_device->size_modifiers_by_dim[3]);
     89   if(log) {
     90     logger_print(g_device->logger, log_type, "End finalizing scad.\n");
     91   }
     92 
     93   if(option_dec) {
     94     /* After releasing all star-cad stuff, gmsh and OCC contexts must be empty */
     95     res = check_empty_gmsh_occ(g_device);
     96   }
     97 
     98   MEM_RM(allocator, g_device);
     99   g_device = NULL;
    100 
    101   return res;
    102 }
    103 
    104 void
    105 log_error(struct scad_device* dev, const char* msg, ...)
    106 {
    107   va_list vargs_list;
    108   ASSERT(dev && msg);
    109   if(dev->verbose < 1) return;
    110   va_start(vargs_list, msg);
    111   log_msg(dev, LOG_ERROR, msg, vargs_list);
    112   va_end(vargs_list);
    113 }
    114 
    115 void
    116 log_warning(struct scad_device* dev, const char* msg, ...)
    117 {
    118   va_list vargs_list;
    119   ASSERT(dev && msg);
    120   if(dev->verbose < 2) return;
    121   va_start(vargs_list, msg);
    122   log_msg(dev, LOG_WARNING, msg, vargs_list);
    123   va_end(vargs_list);
    124 }
    125 
    126 
    127 void
    128 log_message(struct scad_device* dev, const char* msg, ...)
    129 {
    130   va_list vargs_list;
    131   ASSERT(dev && msg);
    132   if(dev->verbose < 3) return;
    133   va_start(vargs_list, msg);
    134   log_msg(dev, LOG_OUTPUT, msg, vargs_list);
    135   va_end(vargs_list);
    136 }
    137 
    138 /*******************************************************************************
    139  * Exported scad_device functions
    140  ******************************************************************************/
    141 res_T
    142 check_device
    143   (const char* function_name)
    144 {
    145   res_T res = RES_OK;
    146   ASSERT(function_name);
    147 
    148   if(!g_device) {
    149     /* No logger available for a message */
    150     fprintf(stderr,
    151        "%s: cannot call API functions if star-cad is not initialized.\n",
    152        function_name);
    153     res = RES_BAD_ARG;
    154     goto error;
    155   }
    156 
    157   if(g_device->options.Misc.RunUIAtEachStep) {
    158     ERR(scad_run_ui());
    159   }
    160 
    161   if(g_device->options.Misc.DebugEmptyContext
    162       && htable_geometries_size_get(&g_device->allgeom) == 0)
    163   {
    164     ERR(check_empty_gmsh_occ(g_device));
    165   }
    166 
    167 exit:
    168   return res;
    169 error:
    170   goto exit;
    171 }
    172 
    173 res_T
    174 sync_device(void)
    175 {
    176   res_T res = RES_OK;
    177 
    178   if(g_device->need_synchro) {
    179     ERR(scad_synchronize()); /* Reset need_synchro according to options */
    180   }
    181 
    182 exit:
    183   return res;
    184 error:
    185   goto exit;
    186 }
    187 
    188 struct scad_device*
    189 get_device
    190   (void)
    191 {
    192   return g_device;
    193 }
    194 
    195 res_T
    196 device_register_tags
    197   (struct scad_geometry* geom)
    198 {
    199   res_T res = RES_OK;
    200   struct scad_device* dev = get_device();
    201   int log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL);
    202 
    203   ASSERT(geom);
    204 
    205   if(geom->gmsh_dimTags_n) {
    206     if(log) {
    207       if(str_is_empty(&geom->name)) {
    208         log_message(dev, "Registering tags for unnamed geometry %p.\n",
    209           (void*)geom);
    210       } else {
    211         log_message(dev, "Registering tags for geometry '%s'.\n",
    212           str_cget(&geom->name));
    213       }
    214     }
    215     ERR(do_device_tags_ref_get(geom->gmsh_dimTags, geom->gmsh_dimTags_n));
    216   }
    217 
    218 exit:
    219   return res;
    220 error:
    221   goto exit;
    222 }
    223 
    224 struct tag_desc*
    225 device_get_description
    226   (const int dim,
    227    const int tag)
    228 {
    229   struct scad_device* dev = get_device();
    230   struct htable_tags2desc* t2d;
    231   struct tag_desc* desc;
    232   CHK(dim == 2 || dim == 3); /* other dims not managed yet */
    233 
    234   t2d = g_device->tags2desc + (dim-2);
    235   desc = htable_tags2desc_find(t2d, &tag);
    236   if(!desc || desc->refcount == 0) {
    237     logger_print(dev->logger, LOG_ERROR,
    238         "SCAD internal error: tag %d.%d not registered.\n", dim, tag);
    239   }
    240   return desc;
    241 }
    242 
    243 res_T
    244 scad_dump_geometry
    245   (const struct scad_geometry* geom)
    246 {
    247   res_T res = RES_OK;
    248   size_t i;
    249 
    250   if(!geom) {
    251     res = RES_BAD_ARG;
    252     goto error;
    253   }
    254 
    255   ERR(check_device(FUNC_NAME));
    256   if(str_is_empty(&geom->name)) {
    257     printf("Unnamed geometry %p (count is %lu), tags: ",
    258         (void*)geom, (long unsigned)geom->ref);
    259   } else {
    260     printf("Geometry '%s' (%p, count is %lu), tags: ",
    261         str_cget(&geom->name), (void*)geom, (long unsigned)geom->ref);
    262   }
    263   for(i = 0; i < geom->gmsh_dimTags_n; i += 2) {
    264     int dim = geom->gmsh_dimTags[i];
    265     int tag = geom->gmsh_dimTags[i+1];
    266     printf((i ? ", %d.%d" : "%d.%d"), dim, tag);
    267   }
    268   printf(".\n");
    269 
    270 exit:
    271   return res;
    272 error:
    273   goto exit;
    274 }
    275 
    276 res_T
    277 scad_dump_geometries
    278   (void)
    279 {
    280   res_T res = RES_OK;
    281   struct scad_device* dev = get_device();
    282   struct htable_geometries_iterator it, end;
    283   size_t cpt = 0;
    284 
    285   ERR(check_device(FUNC_NAME));
    286 
    287   if(htable_geometries_is_empty(&dev->allgeom)) {
    288     printf("No geometry defined.\n");
    289     goto exit; /* Not an error */
    290   }
    291   htable_geometries_begin(&dev->allgeom, &it);
    292   htable_geometries_end(&dev->allgeom, &end);
    293   while(!htable_geometries_iterator_eq(&it, &end)) {
    294     struct scad_geometry* geom = *htable_geometries_iterator_key_get(&it);
    295     ERR(scad_dump_geometry(geom));
    296     cpt++;
    297     htable_geometries_iterator_next(&it);
    298   }
    299   printf("Counted %ld geometries.\n", cpt);
    300 
    301 exit:
    302   return res;
    303 error:
    304   goto exit;
    305 }
    306 
    307 static void
    308 device_remove_description
    309   (const int dim,
    310    const int tag)
    311 {
    312   struct scad_device* dev = get_device();
    313   struct htable_tags2desc* t2d;
    314   struct tag_desc* desc;
    315   CHK(dim == 2 || dim == 3); /* other dims not managed yet */
    316 
    317   t2d = g_device->tags2desc + (dim-2);
    318   desc = htable_tags2desc_find(t2d, &tag);
    319   if(!desc) {
    320     logger_print(dev->logger, LOG_ERROR,
    321         "SCAD internal error: tag %d.%d not registered.\n", dim, tag);
    322   }
    323   if(desc->refcount != 0) {
    324     logger_print(dev->logger, LOG_ERROR,
    325         "SCAD internal error: erasing tag %d.%d that still has references.\n",
    326         dim, tag);
    327   }
    328   CHK(1 == htable_tags2desc_erase(t2d, &tag));
    329 }
    330 
    331 res_T
    332 do_device_tags_ref_get
    333   (const int* dimTags,
    334    const size_t count)
    335 {
    336   res_T res = RES_OK;
    337   size_t i;
    338   struct scad_device* dev = get_device();
    339   int log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL);
    340   enum log_type log_type = dev->log_type;
    341 
    342   ASSERT(dimTags || count == 0);
    343 
    344   for(i = 0; i < count; i += 2) {
    345     int dim = dimTags[i];
    346     int tag = dimTags[i+1];
    347     struct htable_tags2desc* t2d;
    348     struct tag_desc* desc;
    349     CHK(dim == 2 || dim == 3); /* other dims not managed yet */
    350 
    351     t2d = dev->tags2desc + (dim-2);
    352     desc = htable_tags2desc_find(t2d, &tag);
    353     if(!desc) {
    354       /* First ref to dim.tag: create the description */
    355       struct tag_desc d;
    356       tag_desc_init(dev->allocator, &d);
    357       ERR(htable_tags2desc_set(t2d, &tag, &d));
    358       dev->need_synchro = 1;
    359       if(log) {
    360         logger_print(dev->logger, log_type, "New tag %d.%d (count set to 1).\n",
    361             dim, tag);
    362       }
    363     } else {
    364       desc->refcount++;
    365       if(log) {
    366         if(desc->refcount > 1) {
    367           logger_print(dev->logger, log_type, "Tag %d.%d (count increased to %lu).\n",
    368               dim, tag, desc->refcount);
    369         } else {
    370           logger_print(dev->logger, log_type, "Reuse tag %d.%d (count set to 1).\n",
    371               dim, tag);
    372         }
    373       }
    374     }
    375   }
    376 
    377 exit:
    378   return res;
    379 error:
    380   goto exit;
    381 }
    382 
    383 res_T
    384 do_device_tags_ref_put
    385   (const int log,
    386    const enum log_type log_type,
    387    int* dimTags,
    388    size_t count)
    389 {
    390   res_T res = RES_OK;
    391   size_t i;
    392   struct scad_device* dev = get_device();
    393   struct str msg;
    394 
    395   ASSERT(dimTags || count == 0);
    396 
    397   if(log) str_init(dev->allocator, &msg);
    398   for(i = 0; i < count; i += 2) {
    399     int dim = dimTags[i];
    400     int tag = dimTags[i+1];
    401     int ierr;
    402     struct tag_desc* desc = device_get_description(dim, tag);
    403 
    404     if(!desc) {
    405       res = RES_BAD_OP;
    406       goto error;
    407     }
    408 
    409     /* Check if still in use after this unregistration */
    410     desc->refcount--;
    411     if(desc->refcount > 0) {
    412       if(log) {
    413         logger_print(dev->logger, log_type,
    414             "Tag %d.%d (count decreased to %lu).\n",
    415             dim, tag, (unsigned long)desc->refcount);
    416       }
    417       continue;
    418     }
    419 
    420     /* The gmsh geometry with tag 'tag' is not in use anymore: release it */
    421     switch(desc->delete_policy) {
    422       case Scad_do_not_delete:
    423         if(log) {
    424           logger_print(dev->logger, log_type,
    425               "Tag %d.%d not deleted due to policy.\n",
    426               dim, tag);
    427         }
    428         break;
    429       case Scad_delete_non_recursive:
    430         if(log) {
    431           logger_print(dev->logger, log_type,
    432               "Tag %d.%d non-recursively deleted due to policy.\n",
    433               dim, tag);
    434         }
    435         gmshModelOccRemove(dimTags+i, 2, 0, &ierr);
    436         dev->need_synchro = 1;
    437         ERR(gmsh_err_to_res_T(ierr));
    438         break;
    439       case Scad_delete_recursive:
    440         if(log) {
    441           logger_print(dev->logger, log_type, "Tag %d.%d recursively deleted.\n",
    442               dim, tag);
    443         }
    444         gmshModelOccRemove(dimTags+i, 2, 1, &ierr);
    445         dev->need_synchro = 1;
    446         ERR(gmsh_err_to_res_T(ierr));
    447         break;
    448       default: FATAL("Invalid enum value");
    449     }
    450     /* Release associated tags in tags_to_refput */
    451     if(darray_int_size_get(&desc->tags_to_refput)) {
    452       size_t j;
    453       const int* dt = darray_int_cdata_get(&desc->tags_to_refput);
    454       if(log) {
    455         ERR(str_set(&msg, "Putting a reference to tags: "));
    456         for(j = 0; j < darray_int_size_get(&desc->tags_to_refput); j += 2) {
    457           int d = dt[j];
    458           int t = dt[j+1];
    459           ERR(str_append_printf(&msg, (j ? ", %d.%d" : "%d.%d"), d, t));
    460         }
    461         logger_print(dev->logger, log_type, "%s\n", str_cget(&msg));
    462       }
    463       ERR(do_device_tags_ref_put(log, log_type,
    464             darray_int_data_get(&desc->tags_to_refput),
    465             darray_int_size_get(&desc->tags_to_refput)));
    466     }
    467     device_remove_description(dim, tag);
    468   }
    469 
    470 exit:
    471   if(log) str_release(&msg);
    472   return res;
    473 error:
    474   goto exit;
    475 }
    476 
    477 res_T
    478 device_register_ref_to_tags
    479   (const int dim,
    480    const int tag,
    481    const int* dimTags,
    482    const size_t count)
    483 {
    484   res_T res = RES_OK;
    485   struct tag_desc* desc = device_get_description(dim, tag);
    486   size_t i, prev_count, c = 0;
    487   const int* dt;
    488   ASSERT(dimTags);
    489 
    490   if(!desc) {
    491     res = RES_BAD_OP;
    492     goto error;
    493   }
    494 
    495   prev_count = darray_int_size_get(&desc->tags_to_refput);
    496   ERR(darray_int_reserve(&desc->tags_to_refput, count + prev_count));
    497   for(i = 0; i < count; i += 2) {
    498     int d = dimTags[i];
    499     int t = dimTags[i+1];
    500     if(d == dim && t == tag)
    501       continue;
    502     ERR(darray_int_push_back(&desc->tags_to_refput, &d));
    503     ERR(darray_int_push_back(&desc->tags_to_refput, &t));
    504     c += 2;
    505   }
    506   /* As refences will be put, need to get them now */
    507   dt = darray_int_cdata_get(&desc->tags_to_refput);
    508   ERR(do_device_tags_ref_get(dt + prev_count, c));
    509 
    510 exit:
    511   return res;
    512 error:
    513   goto exit;
    514 }
    515 
    516 res_T
    517 device_unregister_tags
    518   (const int log,
    519    const enum log_type log_type,
    520    struct scad_geometry* geom)
    521 {
    522   res_T res = RES_OK;
    523   struct scad_device* dev = get_device();
    524   ASSERT(geom);
    525 
    526   if(log) {
    527     if(str_is_empty(&geom->name)) {
    528       logger_print(dev->logger, log_type,
    529           "Unregistering tags for unnamed geometry %p.\n", (void*)geom);
    530     } else {
    531       logger_print(dev->logger, log_type,
    532           "Unregistering tags for geometry '%s'.\n", str_cget(&geom->name));
    533     }
    534   }
    535 
    536   ERR(do_device_tags_ref_put(log, log_type, geom->gmsh_dimTags,
    537         geom->gmsh_dimTags_n));
    538 
    539 exit:
    540   return res;
    541 error:
    542   goto exit;
    543 }
    544 
    545 res_T
    546 check_empty_gmsh_occ(struct scad_device* dev)
    547 {
    548   int* dimTags = NULL;
    549   size_t dimTags_n, i;
    550   int ierr, found, d;
    551   struct str msg;
    552   int msg_initialized = 0;
    553   res_T res = RES_OK;
    554 
    555   found = 0;
    556   for(d = 3; d >= 0; d--) {
    557     gmshFree(dimTags);
    558     dimTags = NULL;
    559     gmshModelOccGetEntities(&dimTags, &dimTags_n, d, &ierr);
    560     ASSERT(dimTags_n % 2 == 0);
    561     if(dimTags_n == 0)
    562       continue;
    563     found = 1;
    564     log_error(dev,
    565         "There are %ld unreferenced Open-Cascade entities of dim %d from an empty star-cad context%c\n",
    566         dimTags_n / 2, d, (d > 1 ? ':' : '.'));
    567     if(d < 2) continue;
    568     if(!msg_initialized) str_init(dev->allocator, &msg);
    569     for(i = 0; i < dimTags_n; i += 2) {
    570       const int dim = dimTags[i];
    571       const int tag = dimTags[i+1];
    572       ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
    573     }
    574     log_error(dev,"  tags [%s]\n", str_cget(&msg));
    575   }
    576   if(found) {
    577     res = RES_BAD_ARG;
    578     goto error;
    579   }
    580 
    581   ERR(sync_device());
    582 
    583   found = 0;
    584   for(d = 3; d >= 0; d--) {
    585     gmshFree(dimTags);
    586     dimTags = NULL;
    587     gmshModelGetEntities(&dimTags, &dimTags_n, d, &ierr);
    588     ASSERT(dimTags_n % 2 == 0);
    589     if(dimTags_n == 0)
    590       continue;
    591     found = 1;
    592     log_error(dev,
    593         "There are %ld unreferenced gmsh entities of dim %d from an empty star-cad context%c\n",
    594         dimTags_n / 2, d, (d > 1 ? ':' : '.'));
    595     if(d < 2) continue;
    596     if(!msg_initialized) str_init(dev->allocator, &msg);
    597     for(i = 0; i < dimTags_n; i += 2) {
    598       const int dim = dimTags[i];
    599       const int tag = dimTags[i+1];
    600       ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
    601     }
    602     log_error(dev,"  tags [%s]\n", str_cget(&msg));
    603   }
    604   if(found) {
    605     res = RES_BAD_ARG;
    606     goto error;
    607   }
    608 
    609 exit:
    610   gmshFree(dimTags);
    611   if(msg_initialized) str_release(&msg);
    612   return res;
    613 error:
    614   goto exit;
    615 }
    616 
    617 /*******************************************************************************
    618  * API scad_device functions
    619  ******************************************************************************/
    620 res_T
    621 scad_initialize
    622   (struct logger* logger,
    623    struct mem_allocator* mem_allocator,
    624    const int verbose)
    625 {
    626   struct mem_allocator* allocator;
    627   res_T res = RES_OK;
    628   int ierr;
    629 
    630   if(g_device != NULL) {
    631     log_error(g_device, "scad-star is already initialized.\n");
    632     res = RES_BAD_ARG;
    633     goto error;
    634   }
    635 
    636   if(0 > verbose || verbose > 3) {
    637     res = RES_BAD_ARG;
    638     goto error;
    639   }
    640 
    641   gmshInitialize(0, NULL, 1, 0, &ierr);
    642   ERR(gmsh_err_to_res_T(ierr));
    643 
    644   allocator = mem_allocator ? mem_allocator : &mem_default_allocator;
    645   g_device
    646     = (struct scad_device*)MEM_CALLOC(allocator, 1, sizeof(struct scad_device));
    647   if(!g_device) {
    648     res = RES_MEM_ERR;
    649     goto error;
    650   }
    651   g_device->logger = logger ? logger : LOGGER_DEFAULT;
    652   g_device->allocator = allocator;
    653   g_device->need_synchro = g_device->options.Misc.DebugAutoSync;
    654   g_device->verbose = verbose;
    655   g_device->log_type = LOG_OUTPUT;
    656   g_device->log = (g_device->options.Misc.LogRefCounting != SCAD_LOG_NONE);
    657   htable_names_init(allocator, &g_device->geometry_names);
    658   htable_geometries_init(allocator, &g_device->allgeom);
    659   htable_tags2desc_init(allocator, &g_device->tags2desc[0]);
    660   htable_tags2desc_init(allocator, &g_device->tags2desc[1]);
    661   htable_size_modifiers_init(allocator, &g_device->size_modifiers_by_dim[0]);
    662   htable_size_modifiers_init(allocator, &g_device->size_modifiers_by_dim[1]);
    663   htable_size_modifiers_init(allocator, &g_device->size_modifiers_by_dim[2]);
    664   htable_size_modifiers_init(allocator, &g_device->size_modifiers_by_dim[3]);
    665   /* Init to default */
    666   scad_set_options(NULL);
    667 
    668 exit:
    669   return res;
    670 error:
    671   if(g_device) {
    672     device_release();
    673   }
    674   goto exit;
    675 }
    676 
    677 res_T
    678 scad_finalize
    679   (void)
    680 {
    681   res_T tmp_res = RES_OK, res = RES_OK;
    682   int ierr;
    683   struct scad_device* dev = get_device();
    684   int log, empty;
    685   enum scad_log_refcounting option;
    686   enum log_type log_type;
    687 
    688   ERR(check_device(FUNC_NAME));
    689   option = dev->options.Misc.LogRefCounting;
    690 
    691   empty = htable_geometries_is_empty(&dev->allgeom);
    692   log_type = empty ? LOG_OUTPUT : LOG_WARNING;
    693   log = (option & SCAD_LOG_DIMTAGS_ALL)
    694     || (!empty && (option & SCAD_LOG_DIMTAGS_ONLY_UNDELETED));
    695   if(log) {
    696     logger_print(dev->logger, log_type,
    697         "Finalizing scad; undeleted tags will be automatically unregistered.\n");
    698   }
    699 
    700   tmp_res = device_release();
    701   gmshFinalize(&ierr);
    702   ERR(gmsh_err_to_res_T(ierr));
    703 
    704 exit:
    705   if(tmp_res != RES_OK) res = tmp_res;
    706   return res;
    707 error:
    708   goto exit;
    709 }
    710 
    711 #define SET_GMSH_OPTION_INT(Option) \
    712   gmshOptionSetNumber((#Option), (actual_options->Option), &ierr);\
    713   if(ierr) {\
    714     log_error(dev, "Could not set option %s to %d.\n",\
    715       (#Option), (actual_options->Option));\
    716     res = RES_BAD_ARG;\
    717     goto error;\
    718   }
    719 
    720 #define SET_GMSH_OPTION_DOUBLE(Option) \
    721   gmshOptionSetNumber((#Option), (actual_options->Option), &ierr);\
    722   if(ierr) {\
    723     log_error(dev, "Could not set option %s to %g.\n",\
    724       (#Option), (actual_options->Option));\
    725     res = RES_BAD_ARG;\
    726     goto error;\
    727   }
    728 
    729 res_T
    730 scad_set_options
    731   (const struct scad_options* options)
    732 {
    733   res_T res = RES_OK;
    734   const struct scad_options* actual_options
    735     = options ? options : &SCAD_DEFAULT_OPTIONS;
    736   int ierr = 0;
    737   struct scad_options keep;
    738   struct scad_device* dev = NULL;
    739 
    740   ERR(check_device(FUNC_NAME));
    741 
    742   dev = get_device();
    743   keep = dev->options;
    744 
    745   SET_GMSH_OPTION_DOUBLE(Mesh.MeshSizeFactor);
    746   SET_GMSH_OPTION_DOUBLE(Mesh.MeshSizeFromCurvature);
    747   SET_GMSH_OPTION_DOUBLE(Mesh.MeshSizeMax);
    748   SET_GMSH_OPTION_DOUBLE(Mesh.MeshSizeMin);
    749   SET_GMSH_OPTION_DOUBLE(Mesh.Smoothing);
    750   SET_GMSH_OPTION_INT(Mesh.StlOneSolidPerSurface);
    751   SET_GMSH_OPTION_INT(Mesh.MeshOnlyVisible);
    752   SET_GMSH_OPTION_INT(Mesh.Algorithm);
    753   SET_GMSH_OPTION_INT(Mesh.MeshSizeExtendFromBoundary);
    754   SET_GMSH_OPTION_INT(Mesh.MeshSizeFromPoints);
    755 
    756   SET_GMSH_OPTION_INT(General.Verbosity);
    757   SET_GMSH_OPTION_INT(General.ExpertMode);
    758 
    759   SET_GMSH_OPTION_INT(Geometry.OCCParallel);
    760 
    761   if(options) {
    762     /* Check non-gmsh option validity if user-provided */
    763     (void)actual_options->Misc.RunUIAtEachStep; /* int boolean: always OK */
    764     (void)actual_options->Misc.SynchronizeOnRunUI; /* int boolean: always OK */
    765     (void)actual_options->Misc.LogRefCounting; /* int boolean: always OK */
    766     (void)actual_options->Misc.DebugAutoSync; /* int boolean: always OK */
    767     (void)actual_options->Misc.DebugEmptyContext; /* int boolean: always OK */
    768   }
    769 
    770   dev->options = *actual_options;
    771 
    772   /* Update logging policy */
    773   dev->log
    774     = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL);
    775 
    776 exit:
    777   return res;
    778 error:
    779   if(dev) dev->options = keep;
    780   goto exit;
    781 }
    782 
    783 #undef SET_GMSH_OPTION_INT
    784 #undef SET_GMSH_OPTION_DOUBLE
    785 
    786 res_T
    787 scad_get_options
    788   (struct scad_options* options)
    789 {
    790   res_T res = RES_OK;
    791   struct scad_device* dev = NULL;
    792 
    793   if(! options) {
    794     res = RES_BAD_ARG;
    795     goto error;
    796   }
    797 
    798   ERR(check_device(FUNC_NAME));
    799 
    800   dev = get_device();
    801 
    802   *options = dev->options;
    803 
    804 exit:
    805   return res;
    806 error:
    807   goto exit;
    808 }