star-cad

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

scad_geometry.c (94751B)


      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 "scad.h"
     17 #include "scad_c.h"
     18 #include "scad_device.h"
     19 #include "scad_geometry.h"
     20 
     21 #include <rsys/rsys.h>
     22 #include <rsys/mem_allocator.h>
     23 #include <rsys/str.h>
     24 #include <rsys/math.h>
     25 #include <rsys/double2.h>
     26 #include <rsys/double3.h>
     27 #include <rsys/logger.h>
     28 #include <rsys/hash_table.h>
     29 
     30 #include <stdlib.h>
     31 #include <string.h>
     32 #include <gmsh/gmshc.h>
     33 
     34 /* A type used to deduplicate tags */
     35 #define HTABLE_NAME tags
     36 #define HTABLE_KEY int
     37 #define HTABLE_DATA char
     38 #include <rsys/hash_table.h>
     39 
     40 /* A type used for mappings */
     41 #define HTABLE_NAME mappings
     42 #define HTABLE_KEY int
     43 #define HTABLE_DATA size_t
     44 #include <rsys/hash_table.h>
     45 
     46 #include <limits.h>
     47 
     48 /*******************************************************************************
     49  * Utility functions
     50  ******************************************************************************/
     51 struct coord_pair {
     52   const double* x;
     53   const double* y;
     54 };
     55 
     56 static res_T
     57 geom_set_name
     58   (struct scad_geometry* geom,
     59    const struct str* name) /* Can be NULL but not "" */
     60 {
     61   struct scad_device* dev = get_device();
     62   int same_name, name_isnt_null, previous_name_is_null;
     63   res_T res = RES_OK;
     64 
     65   ASSERT(geom);
     66   ASSERT(!name || !str_is_empty(name));
     67 
     68   previous_name_is_null = str_is_empty(&geom->name);
     69   if(name) {
     70     name_isnt_null = 1;
     71     same_name = (!previous_name_is_null) && (0 == str_cmp(name, &geom->name));
     72   } else {
     73     name_isnt_null = 0;
     74     same_name = previous_name_is_null;
     75   }
     76 
     77   if(same_name) /* No change needed */
     78     goto exit;
     79 
     80   if(name_isnt_null) {
     81     struct scad_geometry** g
     82       = htable_names_find(&dev->geometry_names, name);
     83     if(g) { /* if defined, names must be unique */
     84       res = RES_BAD_ARG;
     85       log_error(get_device(), "Geometry name '%s' is allready in use.\n",
     86         str_cget(name));
     87       goto error;
     88     }
     89   }
     90 
     91   if(!previous_name_is_null) {
     92     size_t n = htable_names_erase(&dev->geometry_names, &geom->name);
     93     ASSERT((n == 1)); (void)n;
     94   }
     95   if(name_isnt_null) {
     96     ERR(str_copy(&geom->name, name));
     97     ERR(htable_names_set(&dev->geometry_names, name, &geom));
     98   } else {
     99     str_clear(&geom->name);
    100   }
    101 
    102 exit:
    103   return res;
    104 error:
    105   goto exit;
    106 }
    107 
    108 static int
    109 mixed_dim_err_msg
    110   (const char* op,
    111    struct scad_geometry** geometries,
    112    const size_t geometries_count,
    113    int* dim)
    114 {
    115   size_t i, j;
    116   struct scad_device* dev = get_device();
    117   ASSERT(op && (geometries || geometries_count == 0) && dim);
    118   for(i = 0; i < geometries_count; i++) {
    119     for(j = 0; j < geometries[i]->gmsh_dimTags_n; j += 2) {
    120       int d = geometries[i]->gmsh_dimTags[j];
    121       if(*dim == INT_MAX) *dim = d;
    122       else if (*dim != d) {
    123         log_error(dev,
    124             "Dimension mismatch in %s operation creating geometry.\n", op);
    125         return 1;
    126       }
    127     }
    128   }
    129   return 0;
    130 }
    131 
    132 static res_T
    133 geometry_create
    134   (struct scad_geometry** out_geometry)
    135 {
    136   res_T res = RES_OK;
    137   struct scad_geometry* geom = NULL;
    138   struct scad_device* dev = get_device();
    139   struct mem_allocator* allocator = dev->allocator;
    140   char one = 1;
    141 
    142   ASSERT(out_geometry);
    143 
    144   geom = (struct scad_geometry*)MEM_CALLOC(allocator, 1, sizeof(*geom));
    145   if(!geom) {
    146     res = RES_MEM_ERR;
    147     goto error;
    148   }
    149 
    150   ref_init(&geom->ref);
    151   str_init(allocator, &geom->name);
    152   ERR(htable_geometries_set(&dev->allgeom, &geom, &one));
    153 
    154   if(dev->options.Misc.LogRefCounting & SCAD_LOG_GEOMETRY) {
    155     if(str_is_empty(&geom->name)) {
    156       logger_print(dev->logger, dev->log_type,
    157           "Creating unnamed geometry %p (count set to 1).\n", (void*)geom);
    158     } else {
    159       logger_print(dev->logger, dev->log_type,
    160           "Creating geometry '%s' (%p, count set to 1).\n",
    161           str_cget(&geom->name), (void*)geom);
    162     }
    163   }
    164 
    165 end:
    166   *out_geometry = geom;
    167   return res;
    168 error:
    169   if(geom) {
    170     SCAD(geometry_ref_put(geom));
    171     geom = NULL;
    172   }
    173   goto end;
    174 }
    175 
    176 enum tag_operation {
    177   COMMON_TAGS,
    178   UNIQUE_TAGS
    179 };
    180 
    181 static res_T
    182 process_tag_list
    183   (const int* dt1,
    184    const size_t c1,
    185    const int* dt2,
    186    const size_t c2,
    187    const enum tag_operation op,
    188    int** out_dimTags,
    189    size_t* out_dimTags_n)
    190 {
    191   res_T res = RES_OK;
    192   size_t i, d, c = 0, sz = 0;
    193   struct scad_device* dev = get_device();
    194   struct mem_allocator* allocator = dev->allocator;
    195   struct htable_tags t2, t3;
    196   struct htable_tags_iterator it, end;
    197   int* dimTags = NULL;
    198   const char set1 = BIT(0), set2 = BIT(1), both = set1 | set2;
    199 
    200   ASSERT((dt1 || c1 == 0) && (dt2 || c2 == 0) && out_dimTags && out_dimTags_n);
    201 
    202   htable_tags_init(allocator, &t2);
    203   htable_tags_init(allocator, &t3);
    204 
    205   /* list tags and flags them whith set(s) they are from */
    206   for(i = 0; i < c1; i += 2) {
    207     int dim = dt1[i];
    208     int tag = dt1[i+1];
    209     struct htable_tags* tn = (dim == 2) ? &t2 : &t3;
    210     ASSERT(dim == 2 || dim == 3);
    211     ERR(htable_tags_set(tn, &tag, &set1));
    212   }
    213   for(i = 0; i < c2; i += 2) {
    214     char *fl, tmp;
    215     int dim = dt2[i];
    216     int tag = dt2[i+1];
    217     struct htable_tags* tn = (dim == 2) ? &t2 : &t3;
    218     ASSERT(dim == 2 || dim == 3);
    219     fl = htable_tags_find(tn, &tag);
    220     if(fl == NULL) {
    221       tmp = set2;
    222       fl = &tmp;
    223     } else {
    224       *fl |= set2;
    225     }
    226     ERR(htable_tags_set(tn, &tag, fl));
    227   }
    228 
    229   switch(op) {
    230     case COMMON_TAGS:
    231       for(d = 2; d <= 3; d++) {
    232         struct htable_tags* tn = (d == 2) ? &t2 : &t3;
    233         htable_tags_begin(tn, &it);
    234         htable_tags_end(tn, &end);
    235         while(!htable_tags_iterator_eq(&it, &end)) {
    236           if(*htable_tags_iterator_data_get(&it) == both) sz++;
    237           htable_tags_iterator_next(&it);
    238         }
    239       }
    240       break;
    241     case UNIQUE_TAGS:
    242       sz = htable_tags_size_get(&t2) + htable_tags_size_get(&t3);
    243       break;
    244     default: FATAL("Invalid enum value");
    245   }
    246 
    247   /* Build result */
    248   if(sz > 0) {
    249     dimTags = MEM_ALLOC(allocator, sz * 2 * sizeof(*dimTags));
    250     if(!dimTags) {
    251       res = RES_MEM_ERR;
    252       goto error;
    253     }
    254 
    255     for(d = 2; d <= 3; d++) {
    256       struct htable_tags* tn = (d == 2) ? &t2 : &t3;
    257       htable_tags_begin(tn, &it);
    258       htable_tags_end(tn, &end);
    259       while(!htable_tags_iterator_eq(&it, &end)) {
    260         int add;
    261         switch(op) {
    262           case COMMON_TAGS:
    263             add = (*htable_tags_iterator_data_get(&it) == both);
    264             break;
    265           case UNIQUE_TAGS:
    266             add = 1;
    267             break;
    268           default: FATAL("Invalid enum value");
    269         }
    270         if(add) {
    271           dimTags[c++] = 2;
    272           dimTags[c++] = *htable_tags_iterator_key_get(&it);
    273         }
    274         htable_tags_iterator_next(&it);
    275       }
    276     }
    277     ASSERT(sz*2 == c);
    278   }
    279 
    280 exit:
    281   *out_dimTags_n = c;
    282   *out_dimTags = dimTags;
    283   htable_tags_release(&t2);
    284   htable_tags_release(&t3);
    285   return res;
    286 error:
    287   MEM_RM(allocator, dimTags);
    288   dimTags = NULL;
    289   c = 0;
    290   goto exit;
    291 }
    292 
    293 static res_T
    294 gather_tags
    295   (struct scad_geometry** geometries,
    296    const size_t geometries_count,
    297    int** out_dimTags,
    298    size_t* out_dimTags_n)
    299 {
    300   res_T res = RES_OK;
    301   int* dimTags = NULL;
    302   size_t i, j, c, sz;
    303   struct scad_device* dev = get_device();
    304   struct mem_allocator* allocator = dev->allocator;
    305   struct htable_tags t2, t3;
    306   struct htable_tags_iterator it, end;
    307 
    308   ASSERT((geometries || !geometries_count) && out_dimTags && out_dimTags_n);
    309 
    310   htable_tags_init(allocator, &t2);
    311   htable_tags_init(allocator, &t3);
    312 
    313   /* list tags and remove duplicates */
    314   for(i = 0; i < geometries_count; i++) {
    315     if(!geometries[i]) {
    316       res = RES_BAD_ARG;
    317       goto error;
    318     }
    319     for(j = 0; j < geometries[i]->gmsh_dimTags_n; j += 2) {
    320       char one = 1;
    321       int dim = geometries[i]->gmsh_dimTags[j];
    322       int tag = geometries[i]->gmsh_dimTags[j+1];
    323       struct htable_tags* tn = (dim == 2) ? &t2 : &t3;
    324       ASSERT(dim == 2 || dim == 3);
    325       ERR(htable_tags_set(tn, &tag, &one));
    326     }
    327   }
    328 
    329   /* Build result */
    330   sz = htable_tags_size_get(&t2) + htable_tags_size_get(&t3);
    331   dimTags = MEM_ALLOC(allocator, sz * 2 * sizeof(*dimTags));
    332   if(!dimTags) {
    333     res = RES_MEM_ERR;
    334     goto error;
    335   }
    336 
    337   c = 0;
    338   htable_tags_begin(&t2, &it);
    339   htable_tags_end(&t2, &end);
    340   while(!htable_tags_iterator_eq(&it, &end)) {
    341     dimTags[c++] = 2;
    342     dimTags[c++] = *htable_tags_iterator_key_get(&it);
    343     htable_tags_iterator_next(&it);
    344   }
    345   htable_tags_begin(&t3, &it);
    346   htable_tags_end(&t3, &end);
    347   while(!htable_tags_iterator_eq(&it, &end)) {
    348     dimTags[c++] = 3;
    349     dimTags[c++] = *htable_tags_iterator_key_get(&it);
    350     htable_tags_iterator_next(&it);
    351   }
    352   ASSERT(sz*2 == c);
    353 
    354   *out_dimTags_n = c;
    355   *out_dimTags = dimTags;
    356 
    357 exit:
    358   htable_tags_release(&t2);
    359   htable_tags_release(&t3);
    360   return res;
    361 error:
    362   MEM_RM(allocator, dimTags);
    363   goto exit;
    364 }
    365 
    366 static res_T
    367 store_tags
    368   (int* dimTags,
    369    size_t count,
    370    const int down_to_dim,
    371    struct htable_tags t[4])
    372 {
    373   res_T res = RES_OK;
    374   char one = 1;
    375   size_t i;
    376   ASSERT((dimTags || count == 0) && t);
    377   for(i = 0; i < count; i += 2) {
    378     int dim = dimTags[i];
    379     int tag = dimTags[i+1];
    380     ASSERT(dim >= 0 && dim <= 3);
    381     if(down_to_dim > dim) continue;
    382     ERR(htable_tags_set(t+dim, &tag, &one));
    383   }
    384 
    385 exit:
    386   return res;
    387 error:
    388   goto exit;
    389 }
    390 
    391 /* Recursivelly get the tags of geometries, possibly down to dim 0.
    392  * Trigger a call to sync_device() */
    393 static res_T
    394 gather_tags_recursive
    395   (struct scad_geometry** geometries,
    396    const size_t geometries_count,
    397    const int down_to_dim,
    398    int* out_dimTags[4],
    399    size_t out_dimTags_n[4])
    400 {
    401   res_T res = RES_OK;
    402   int* dimTags[4] = { NULL, NULL, NULL, NULL }, *sub = NULL;
    403   size_t i, sz[4];
    404   struct scad_device* dev = get_device();
    405   struct mem_allocator* allocator = dev->allocator;
    406   struct htable_tags t[4];
    407   struct htable_tags_iterator it, end;
    408   int dim;
    409 
    410   ASSERT((geometries || geometries_count == 0) && out_dimTags && out_dimTags_n);
    411   ASSERT(0 <= down_to_dim && down_to_dim <= 3);
    412 
    413   for(i = (size_t)down_to_dim; i < 4; i++) {
    414     htable_tags_init(allocator, t+i);
    415   }
    416 
    417   /* list tags by dimension and remove duplicates */
    418   for(i = 0; i < geometries_count; i++) {
    419     if(!geometries[i]) {
    420       res = RES_BAD_ARG;
    421       goto error;
    422     }
    423     ERR(store_tags(geometries[i]->gmsh_dimTags, geometries[i]->gmsh_dimTags_n,
    424          down_to_dim, t));
    425   }
    426 
    427   /* Recursively build result by dimension and list constituents,
    428    * begining with dim==3 */
    429   ERR(sync_device());
    430   for(dim = 3; dim >= down_to_dim; dim--) {
    431     size_t c = 0;
    432     sz[dim] = 2 * htable_tags_size_get(t+dim);
    433     if(sz[dim] == 0)
    434       continue;
    435     dimTags[dim] = MEM_ALLOC(allocator, sz[dim] * sizeof(*dimTags));
    436     if(!dimTags[dim]) {
    437       res = RES_MEM_ERR;
    438       goto error;
    439     }
    440     htable_tags_begin(t+dim, &it);
    441     htable_tags_end(t+dim, &end);
    442     while(!htable_tags_iterator_eq(&it, &end)) {
    443       dimTags[dim][c++] = dim;
    444       dimTags[dim][c++] = *htable_tags_iterator_key_get(&it);
    445       htable_tags_iterator_next(&it);
    446     }
    447     if(dim > down_to_dim) {
    448       int ierr;
    449       size_t subn;
    450       gmshModelGetBoundary(dimTags[dim], sz[dim], &sub, &subn, 0, 0, 0, &ierr);
    451       ERR(gmsh_err_to_res_T(ierr));
    452       ERR(store_tags(sub, subn, down_to_dim, t));
    453       gmshFree(sub); sub = NULL;
    454     }
    455     ASSERT(sz[dim] == c);
    456   }
    457 
    458   for(i = (size_t)down_to_dim; i < 4; i++) {
    459     out_dimTags_n[i] = sz[i];
    460     out_dimTags[i] = dimTags[i];
    461   }
    462 
    463 exit:
    464   if(sub) gmshFree(sub);
    465   for(i = (size_t)down_to_dim; i < 4; i++) {
    466     htable_tags_release(t+i);
    467   }
    468   return res;
    469 error:
    470   for(i = (size_t)down_to_dim; i < 4; i++) {
    471     MEM_RM(allocator, dimTags[i]);
    472   }
    473   goto exit;
    474 }
    475 
    476 static double size_callback
    477   (int dim, int tag, double x, double y, double z, double lc, void* data_)
    478 {
    479   struct scad_device* dev = get_device();
    480   double *modifier =
    481     htable_size_modifiers_find(dev->size_modifiers_by_dim + dim, &tag);
    482   (void)x;(void)y;(void)z;(void)data_;
    483   if(!modifier)
    484     return lc; /* No modifier defined */
    485   if(*modifier < 0)
    486     return -(*modifier); /* Absolute size modifier */
    487   return (*modifier) * lc; /* Size factor modifier */
    488 }
    489 
    490 /* gmsh documentation states that memory allocated by gmsh should be freed using
    491  * gmshFree.
    492  * According to valgrind map results as allocated by gmsh are not fully freed if
    493  * simply freed using gmshFree.
    494  * A code review shows that maps content is recursivelly allocated.
    495  * We end writting this helper function that free maps content and makes
    496  * valgrind happy. */
    497 static void
    498 free_gmsh_map
    499   (int** map,
    500    size_t mapnn)
    501 {
    502   size_t i;
    503   if(!mapnn) return;
    504   for(i = 0; i < mapnn; i++) {
    505     gmshFree(map[i]);
    506   }
    507   gmshFree(map);
    508 }
    509 
    510 /*******************************************************************************
    511  * Local functions
    512  ******************************************************************************/
    513 static void
    514 geometry_release(ref_T* ref)
    515 {
    516   struct scad_geometry* geom;
    517   struct scad_device* dev = get_device();
    518   struct mem_allocator* allocator = dev->allocator;
    519   size_t n;
    520   ASSERT(ref);
    521 
    522   geom = CONTAINER_OF(ref, struct scad_geometry, ref);
    523   CHK(RES_OK == device_unregister_tags(dev->log, dev->log_type, geom));
    524   MEM_RM(allocator, geom->gmsh_dimTags);
    525   if(!str_is_empty(&geom->name)) {
    526     n = htable_names_erase(&dev->geometry_names, &geom->name);
    527     ASSERT(n == 1);
    528   }
    529   str_release(&geom->name);
    530   n = htable_geometries_erase(&dev->allgeom, &geom);
    531   ASSERT(n == 1); (void)n;
    532   if(geom->release && geom->custom) {
    533     (*geom->release)(geom->custom);
    534   }
    535   MEM_RM(allocator, geom);
    536 }
    537 
    538 /******************************************************************************
    539  * Exported functions
    540  *****************************************************************************/
    541 res_T
    542 scad_geometry_ref_get
    543   (struct scad_geometry* geom)
    544 {
    545   res_T res = RES_OK;
    546   struct scad_device* dev = get_device();
    547 
    548   if(!geom) return RES_BAD_ARG;
    549   ERR(check_device(FUNC_NAME));
    550 
    551   ref_get(&geom->ref);
    552   if(dev->options.Misc.LogRefCounting & SCAD_LOG_GEOMETRY) {
    553     if(str_is_empty(&geom->name)) {
    554       logger_print(dev->logger, dev->log_type,
    555           "Getting a reference on unnamed geometry %p (count set to %lu).\n",
    556           (void*)geom, (long unsigned)geom->ref);
    557     } else {
    558       logger_print(dev->logger, dev->log_type,
    559           "Getting a reference on geometry '%s' (count set to %lu).\n",
    560           str_cget(&geom->name), (long unsigned)geom->ref);
    561     }
    562   }
    563 
    564 end:
    565   return res;
    566 error:
    567   goto end;
    568 }
    569 
    570 res_T
    571 scad_geometry_ref_put
    572   (struct scad_geometry* geom)
    573 {
    574   res_T res = RES_OK;
    575   struct scad_device* dev = get_device();
    576 
    577   if(!geom) return RES_BAD_ARG;
    578   ERR(check_device(FUNC_NAME));
    579 
    580   if(dev->options.Misc.LogRefCounting & SCAD_LOG_GEOMETRY) {
    581     if(str_is_empty(&geom->name)) {
    582       logger_print(dev->logger, dev->log_type,
    583           "Putting a reference on unnamed geometry %p (count set to %lu).\n",
    584           (void*)geom, (long unsigned)geom->ref - 1);
    585     } else {
    586       logger_print(dev->logger, dev->log_type,
    587           "Putting a reference on geometry '%s' (count set to %lu).\n",
    588           str_cget(&geom->name), (long unsigned)geom->ref - 1);
    589     }
    590   }
    591   ref_put(&geom->ref, geometry_release);
    592 
    593 end:
    594   return res;
    595 error:
    596   goto end;
    597 }
    598 
    599 res_T
    600 scad_scene_count
    601   (size_t* count)
    602 {
    603   res_T res = RES_OK;
    604   struct scad_device* dev = get_device();
    605 
    606   if(!count) {
    607     res =RES_BAD_ARG;
    608     goto error;
    609   }
    610 
    611   ERR(check_device(FUNC_NAME));
    612 
    613   *count = htable_geometries_size_get(&dev->allgeom);
    614 
    615 end:
    616   return res;
    617 error:
    618   goto end;
    619 }
    620 
    621 res_T
    622 scad_scene_clear
    623   (void)
    624 {
    625   res_T res = RES_OK;
    626   int ierr;
    627   struct htable_geometries tmp;
    628   struct htable_geometries_iterator it, end;
    629   struct scad_device* dev = get_device();
    630   struct mem_allocator* allocator = NULL;
    631   int log, empty;
    632   enum scad_log_refcounting option;
    633   enum log_type log_type;
    634 
    635   ERR(check_device(FUNC_NAME));
    636   allocator = dev->allocator;
    637   option = dev->options.Misc.LogRefCounting;
    638 
    639   htable_geometries_init(allocator, &tmp);
    640   ERR(htable_geometries_copy(&tmp, &dev->allgeom));
    641   htable_geometries_begin(&tmp, &it);
    642   htable_geometries_end(&tmp, &end);
    643   empty = htable_geometries_is_empty(&dev->allgeom);
    644   log_type = empty ? LOG_OUTPUT : LOG_WARNING;
    645   log = (option & SCAD_LOG_DIMTAGS_ALL)
    646     || (!empty && (option & SCAD_LOG_DIMTAGS_ONLY_UNDELETED));
    647   SWAP(int, dev->log, log);
    648   SWAP(enum log_type, dev->log_type, log_type);
    649   if(dev->log) {
    650     logger_print(dev->logger, dev->log_type, "Clearing scene.\n");
    651     if(empty) {
    652       logger_print(dev->logger, dev->log_type, "scene is empty.\n");
    653     }
    654   }
    655   while(!htable_geometries_iterator_eq(&it, &end)) {
    656     struct scad_geometry* geom = *htable_geometries_iterator_key_get(&it);
    657     long cpt = geom->ref;
    658     while(cpt-- > 0) {
    659       ERR(scad_geometry_ref_put(geom));
    660     }
    661     htable_geometries_iterator_next(&it);
    662   }
    663   if(dev->log) {
    664     logger_print(dev->logger, dev->log_type, "End clearing scene.\n");
    665   }
    666   SWAP(int, log, dev->log);
    667   SWAP(enum log_type, log_type, dev->log_type);
    668 
    669   /* Ensure clear is complete (not scad-registered tags) */
    670   gmshClear(&ierr);
    671   ERR(gmsh_err_to_res_T(ierr));
    672 
    673 end:
    674   if(allocator) htable_geometries_release(&tmp);
    675   return res;
    676 error:
    677   goto end;
    678 }
    679 
    680 res_T
    681 scad_geometry_get_count
    682   (const struct scad_geometry* geom,
    683    size_t* count)
    684 {
    685   res_T res = RES_OK;
    686 
    687   if(!geom || !count) {
    688     res = RES_BAD_ARG;
    689     goto error;
    690   }
    691 
    692   ERR(check_device(FUNC_NAME));
    693 
    694   ASSERT(geom->gmsh_dimTags_n % 2 == 0);
    695   *count = geom->gmsh_dimTags_n / 2;
    696 
    697 exit:
    698   return res;
    699 error:
    700   goto exit;
    701 }
    702 
    703 res_T
    704 scad_geometry_is_empty
    705   (const struct scad_geometry* geom,
    706    int* empty)
    707 {
    708   res_T res = RES_OK;
    709 
    710   if(!geom || !empty) {
    711     res = RES_BAD_ARG;
    712     goto error;
    713   }
    714 
    715   ERR(check_device(FUNC_NAME));
    716 
    717   *empty = (geom->gmsh_dimTags_n == 0);
    718 
    719 exit:
    720   return res;
    721 error:
    722   goto exit;
    723 }
    724 
    725 res_T
    726 scad_geometry_set_custom_data
    727   (struct scad_geometry* geom,
    728    void (*release) (void* data),
    729    void* data)
    730 {
    731   res_T res = RES_OK;
    732 
    733   if(!geom) {
    734     res = RES_BAD_ARG;
    735     goto error;
    736   }
    737 
    738   ERR(check_device(FUNC_NAME));
    739 
    740   geom->custom = data;
    741   geom->release = release;
    742 
    743 exit:
    744   return res;
    745 error:
    746   goto exit;
    747 }
    748 
    749 res_T
    750 scad_geometry_get_custom_data
    751   (struct scad_geometry* geom,
    752    void** data)
    753 {
    754   res_T res = RES_OK;
    755 
    756   if(!geom || !data) {
    757     res = RES_BAD_ARG;
    758     goto error;
    759   }
    760 
    761   ERR(check_device(FUNC_NAME));
    762 
    763   if(geom->release && geom->custom) {
    764     (*geom->release)(geom->custom);
    765   }
    766   *data = geom->custom;
    767 
    768 exit:
    769   return res;
    770 error:
    771   goto exit;
    772 }
    773 
    774 res_T
    775 scad_geometry_set_visibility
    776   (const struct scad_geometry* geom,
    777    int visible,
    778    int recursive)
    779 {
    780   res_T res = RES_OK;
    781   int ierr;
    782   int* data = NULL;
    783   size_t sz;
    784 
    785   if(!geom) {
    786     res = RES_BAD_ARG;
    787     goto error;
    788   }
    789 
    790   ERR(check_device(FUNC_NAME));
    791 
    792   data = geom->gmsh_dimTags;
    793   sz = geom->gmsh_dimTags_n;
    794 
    795   ERR(sync_device());
    796   gmshModelSetVisibility(data, sz, visible, recursive, &ierr);
    797   ERR(gmsh_err_to_res_T(ierr));
    798 
    799 exit:
    800   return res;
    801 error:
    802   goto exit;
    803 }
    804 
    805 res_T
    806 scad_geometries_clear_mesh
    807   (struct scad_geometry** geometries,
    808    const size_t geometries_count)
    809 {
    810   res_T res = RES_OK;
    811   int ierr, dim;
    812   struct scad_device* dev = get_device();
    813   struct mem_allocator* allocator = NULL;
    814   size_t sz[4];
    815   int *data[4] = { NULL, NULL, NULL, NULL};
    816 
    817   if(!geometries || geometries_count == 0) {
    818     res = RES_BAD_ARG;
    819     goto error;
    820   }
    821 
    822   ERR(check_device(FUNC_NAME));
    823   allocator = dev->allocator;
    824 
    825   ERR(gather_tags_recursive(geometries, geometries_count, 0, data, sz));
    826   for(dim = 3; dim >= 0; dim--) {
    827     /* Cannot clear the mesh of lower dim entities if linked to higher dim
    828      * entities with uncleared mesh: start from the higher dim down to 0 */
    829     gmshModelMeshClear(data[dim], sz[dim], &ierr);
    830     ERR(gmsh_err_to_res_T(ierr));
    831   }
    832 
    833 exit:
    834   if(allocator) {
    835     for(dim = 3; dim >= 0; dim--) {
    836       MEM_RM(allocator, data[dim]);
    837     }
    838   }
    839   return res;
    840 error:
    841   goto exit;
    842 }
    843 
    844 res_T
    845 scad_geometry_get_name
    846   (const struct scad_geometry* geom,
    847    const char** name)
    848 {
    849   res_T res = RES_OK;
    850 
    851   if(!geom || !name) {
    852     res = RES_BAD_ARG;
    853     goto error;
    854   }
    855 
    856   ERR(check_device(FUNC_NAME));
    857 
    858   if(str_is_empty(&geom->name)) {
    859     *name = NULL;
    860   } else {
    861     *name = str_cget(&geom->name);
    862   }
    863 
    864 exit:
    865   return res;
    866 error:
    867   goto exit;
    868 }
    869 
    870 res_T
    871 scad_geometry_get_mass
    872   (struct scad_geometry* geom,
    873    double* mass)
    874 {
    875   res_T res = RES_OK;
    876   int ref_dim = 0;
    877   size_t i;
    878   int* data = NULL;
    879   size_t sz = 0;
    880   double m = 0;
    881 
    882   if(!geom || !mass) {
    883     res = RES_BAD_ARG;
    884     goto error;
    885   }
    886 
    887   ERR(check_device(FUNC_NAME));
    888 
    889   data = geom->gmsh_dimTags;
    890   sz = geom->gmsh_dimTags_n;
    891 
    892   ref_dim = data[0];
    893   for(i = 0; i < sz; i += 2) {
    894     double geom_mass;
    895     int dim = data[i], tag = data[i+1];
    896     int ierr = 0;
    897     if(ref_dim != dim) goto error;
    898     gmshModelOccGetMass(dim, tag, &geom_mass, &ierr);
    899     ERR(gmsh_err_to_res_T(ierr));
    900     m += geom_mass;
    901   }
    902   *mass = m;
    903 
    904 exit:
    905   return res;
    906 error:
    907   goto exit;
    908 }
    909 
    910 res_T
    911 scad_geometry_get_closest_point
    912   (struct scad_geometry* geom,
    913    const double from[3],
    914    double closest[3],
    915    double* closest_distance,
    916    struct scad_geometry** out_geometry)
    917 {
    918   res_T res = RES_OK;
    919   size_t i = 0;
    920   double* coord = NULL;
    921   double* pcoord = NULL;
    922   double tmp[3], min[3], min_d = DBL_MAX;
    923   struct scad_device* dev = get_device();
    924   struct darray_int tags;
    925   const int* data = NULL;
    926   size_t sz = 0;
    927   int initialized = 0, min_tag = -1;
    928   struct scad_geometry* out = NULL;
    929 
    930   if(!geom || !from || !closest || !closest_distance) {
    931     res = RES_BAD_ARG;
    932     goto error;
    933   }
    934 
    935   ERR(check_device(FUNC_NAME));
    936 
    937   darray_int_init(dev->allocator, &tags);
    938   initialized = 1;
    939 
    940   ERR(get_2d_tags(geom, &tags));
    941   data = darray_int_cdata_get(&tags);
    942   sz = darray_int_size_get(&tags);
    943 
    944   for(i = 0; i < sz; i++) {
    945     double d;
    946     int ierr = 0;
    947     int dim = 2;
    948     int tag = data[i];
    949     size_t pcoord_n;
    950     size_t coord_n;
    951 
    952     gmshModelGetClosestPoint(dim, tag, from, 3, &coord, &coord_n, &pcoord,
    953         &pcoord_n, &ierr);
    954     ERR(gmsh_err_to_res_T(ierr));
    955     ASSERT(pcoord_n == (size_t)dim);
    956     ASSERT(coord_n == 3);
    957     d = d3_len(d3_sub(tmp, from, coord));
    958     if(d < min_d) {
    959       min_tag = tag;
    960       min_d = d;
    961       d3_set(min, coord);
    962     }
    963     gmshFree(coord);
    964     gmshFree(pcoord);
    965     coord = pcoord = NULL;
    966   }
    967   ASSERT(min_tag != -1);
    968   d3_set(closest, min);
    969   *closest_distance = min_d;
    970   if(out_geometry) {
    971     ERR(geometry_create(&out));
    972     out->gmsh_dimTags =
    973       MEM_ALLOC(dev->allocator, 2 * sizeof(*out->gmsh_dimTags));
    974     if(!out->gmsh_dimTags) {
    975       res = RES_MEM_ERR;
    976       goto error;
    977     }
    978     out->gmsh_dimTags_n = 2;
    979     out->gmsh_dimTags[0] = 2;
    980     out->gmsh_dimTags[1] = min_tag;
    981     ERR(device_register_tags(out));
    982   }
    983 
    984 exit:
    985   if(initialized) darray_int_release(&tags);
    986   if(out_geometry) *out_geometry = out;
    987   gmshFree(coord);
    988   gmshFree(pcoord);
    989   return res;
    990 error:
    991   if(out) {
    992     SCAD(geometry_ref_put(out));
    993     out = NULL;
    994   }
    995   goto exit;
    996 }
    997 
    998 res_T
    999 scad_geometry_get_centerofmass
   1000   (struct scad_geometry* geom,
   1001    double center[3])
   1002 {
   1003   res_T res = RES_OK;
   1004   double *centers = NULL, *masses = NULL;
   1005   size_t i = 0, count;
   1006   struct mem_allocator* allocator;
   1007   int ierr;
   1008 
   1009   if(!geom || !center) {
   1010     res = RES_BAD_ARG;
   1011     goto error;
   1012   }
   1013 
   1014   ERR(check_device(FUNC_NAME));
   1015 
   1016   ASSERT(geom->gmsh_dimTags_n % 2 == 0);
   1017   count = geom->gmsh_dimTags_n / 2;
   1018   if(count == 1) {
   1019     int dim = geom->gmsh_dimTags[i];
   1020     int tag = geom->gmsh_dimTags[i + 1];
   1021     gmshModelOccGetCenterOfMass(dim, tag, center, center+1, center+2, &ierr);
   1022     ERR(gmsh_err_to_res_T(ierr));
   1023   } else {
   1024     double c[3] = { 0, 0, 0 }, tmp[3], m = 0;
   1025     allocator = get_device()->allocator;
   1026     centers = MEM_ALLOC(allocator, count * 3 * sizeof(*centers));
   1027     masses = MEM_ALLOC(allocator, count * sizeof(*masses));
   1028     if(!centers || !masses) {
   1029       res = RES_MEM_ERR;
   1030       goto error;
   1031     }
   1032     for(i = 0; i < count; i++) {
   1033       int dim = geom->gmsh_dimTags[2*i];
   1034       int tag = geom->gmsh_dimTags[2*i + 1];
   1035       gmshModelOccGetCenterOfMass(dim, tag,
   1036           centers+3*i, centers+3*i+1, centers+3*i+2, &ierr);
   1037       ERR(gmsh_err_to_res_T(ierr));
   1038       gmshModelOccGetMass(dim, tag, masses+i, &ierr);
   1039       ERR(gmsh_err_to_res_T(ierr));
   1040       d3_add(c, c, d3_muld(tmp, centers+3*i, masses[i]));
   1041       m += masses[i];
   1042     }
   1043     d3_muld(center, c, 1/m);
   1044   }
   1045 
   1046 exit:
   1047   if(centers) MEM_RM(allocator, centers);
   1048   if(masses) MEM_RM(allocator, masses);
   1049   return res;
   1050 error:
   1051   goto exit;
   1052 }
   1053 
   1054 res_T
   1055 scad_geometry_get_bounding_box
   1056   (struct scad_geometry* geom,
   1057    double min[3],
   1058    double max[3])
   1059 {
   1060   res_T res = RES_OK;
   1061   size_t n = 0;
   1062   double mi[3], ma[3];
   1063 
   1064   if(!geom || !min || !max) {
   1065     res = RES_BAD_ARG;
   1066     goto error;
   1067   }
   1068 
   1069   ERR(check_device(FUNC_NAME));
   1070 
   1071   d3_splat(mi, DBL_MAX);
   1072   d3_splat(ma, -DBL_MAX);
   1073   for(n = 0; n < geom->gmsh_dimTags_n; n += 2) {
   1074     double i[3], a[3];
   1075     int ierr = 0;
   1076     int dim = geom->gmsh_dimTags[n];
   1077     int tag = geom->gmsh_dimTags[n + 1];
   1078     gmshModelOccGetBoundingBox(dim, tag, i, i+1, i+2, a, a+1, a+2, &ierr);
   1079     ERR(gmsh_err_to_res_T(ierr));
   1080     d3_min(mi, mi, i);
   1081     d3_max(ma, ma, a);
   1082   }
   1083   d3_set(min, mi);
   1084   d3_set(max, ma);
   1085 
   1086 exit:
   1087   return res;
   1088 error:
   1089   goto exit;
   1090 }
   1091 
   1092 res_T
   1093 scad_add_rectangle
   1094   (const double xyz[3],
   1095    const double dxdy[2],
   1096    struct scad_geometry** out_geometry)
   1097 {
   1098   res_T res = RES_OK;
   1099   int ierr, gmsh_ID;
   1100   struct scad_geometry* geom = NULL;
   1101   struct scad_device* dev = get_device();
   1102   struct mem_allocator* allocator = NULL;
   1103 
   1104   if(!xyz || !dxdy || !out_geometry) {
   1105     res = RES_BAD_ARG;
   1106     goto error;
   1107   }
   1108 
   1109   ERR(check_device(FUNC_NAME));
   1110   allocator = dev->allocator;
   1111 
   1112   gmsh_ID = gmshModelOccAddRectangle(SPLIT3(xyz), SPLIT2(dxdy), -1, 0, &ierr);
   1113   ERR(gmsh_err_to_res_T(ierr));
   1114 
   1115   ERR(geometry_create(&geom));
   1116   geom->gmsh_dimTags_n = 2;
   1117   geom->gmsh_dimTags
   1118     = MEM_ALLOC(allocator, geom->gmsh_dimTags_n * sizeof(*geom->gmsh_dimTags));
   1119   if(!geom->gmsh_dimTags) {
   1120     res = RES_MEM_ERR;
   1121     goto error;
   1122   }
   1123   geom->gmsh_dimTags[0] = 2;
   1124   geom->gmsh_dimTags[1] = gmsh_ID;
   1125 
   1126   ERR(device_register_tags(geom));
   1127 
   1128 exit:
   1129   if(out_geometry) *out_geometry = geom;
   1130   return res;
   1131 error:
   1132   if(geom) {
   1133     SCAD(geometry_ref_put(geom));
   1134     geom = NULL;
   1135   }
   1136   goto exit;
   1137 }
   1138 
   1139 res_T
   1140 scad_add_disk
   1141   (const double xyz[3],
   1142    const double radius,
   1143    struct scad_geometry** out_geometry)
   1144 {
   1145   res_T res = RES_OK;
   1146   int ierr, gmsh_ID;
   1147   struct scad_geometry* geom = NULL;
   1148   struct scad_device* dev = get_device();
   1149   struct mem_allocator* allocator = NULL;
   1150 
   1151   if(!xyz || radius <= 0 || !out_geometry) {
   1152     res = RES_BAD_ARG;
   1153     goto error;
   1154   }
   1155 
   1156   ERR(check_device(FUNC_NAME));
   1157   allocator = dev->allocator;
   1158 
   1159   gmsh_ID = gmshModelOccAddDisk(SPLIT3(xyz), radius, radius, -1, NULL, 0,
   1160       NULL, 0, &ierr);
   1161   ERR(gmsh_err_to_res_T(ierr));
   1162 
   1163   ERR(geometry_create(&geom));
   1164   geom->gmsh_dimTags_n = 2;
   1165   geom->gmsh_dimTags
   1166     = MEM_ALLOC(allocator, geom->gmsh_dimTags_n * sizeof(*geom->gmsh_dimTags));
   1167   if(!geom->gmsh_dimTags) {
   1168     res = RES_MEM_ERR;
   1169     goto error;
   1170   }
   1171   geom->gmsh_dimTags[0] = 2;
   1172   geom->gmsh_dimTags[1] = gmsh_ID;
   1173 
   1174   ERR(device_register_tags(geom));
   1175 
   1176 exit:
   1177   if(out_geometry) *out_geometry = geom;
   1178   return res;
   1179 error:
   1180   if(geom) {
   1181     SCAD(geometry_ref_put(geom));
   1182     geom = NULL;
   1183   }
   1184   goto exit;
   1185 }
   1186 
   1187 res_T
   1188 scad_add_polygon
   1189   (void (*get_position)(const size_t ivert, double pos[2], void* data),
   1190    void* data,
   1191    const double z,
   1192    const size_t count,
   1193    struct scad_geometry** out_geometry)
   1194 {
   1195   res_T res = RES_OK;
   1196   int ierr, gmsh_ID;
   1197   struct scad_geometry* geom = NULL;
   1198   size_t i;
   1199   int* points = NULL;
   1200   int* lines = NULL;
   1201   int loop;
   1202   struct scad_device* dev = get_device();
   1203   struct mem_allocator* allocator = NULL;
   1204 
   1205   if(!get_position || count < 3 || !out_geometry) {
   1206     res = RES_BAD_ARG;
   1207     goto error;
   1208   }
   1209 
   1210   ERR(check_device(FUNC_NAME));
   1211   allocator = dev->allocator;
   1212 
   1213   points = MEM_ALLOC(allocator, count * sizeof(*points));
   1214   lines = MEM_ALLOC(allocator, count * sizeof(*lines));
   1215   if(!points || !lines) {
   1216     res = RES_MEM_ERR;
   1217     goto error;
   1218   }
   1219 
   1220   for(i=0; i<count; ++i) {
   1221     double pos[2];
   1222     get_position(i, pos, data);
   1223     points[i] = gmshModelOccAddPoint(pos[0], pos[1], z, -1, -1, &ierr);
   1224     ERR(gmsh_err_to_res_T(ierr));
   1225   }
   1226 
   1227   for(i=0; i<count; i++) {
   1228     size_t end = (i == count-1) ? 0 : i+1;
   1229     lines[i] = gmshModelOccAddLine(points[i], points[end], -1, &ierr);
   1230     ERR(gmsh_err_to_res_T(ierr));
   1231   }
   1232 
   1233   loop = gmshModelOccAddCurveLoop(lines, count, -1, &ierr);
   1234   ERR(gmsh_err_to_res_T(ierr));
   1235 
   1236   gmsh_ID = gmshModelOccAddPlaneSurface(&loop, 1, -1, &ierr);
   1237   ERR(gmsh_err_to_res_T(ierr));
   1238 
   1239   ERR(geometry_create(&geom));
   1240   geom->gmsh_dimTags_n = 2;
   1241   geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags));
   1242   if(!geom->gmsh_dimTags) {
   1243     res = RES_MEM_ERR;
   1244     goto error;
   1245   }
   1246   geom->gmsh_dimTags[0] = 2;
   1247   geom->gmsh_dimTags[1] = gmsh_ID;
   1248 
   1249   ERR(device_register_tags(geom));
   1250 
   1251 exit:
   1252   if(out_geometry) *out_geometry = geom;
   1253   if(allocator) {
   1254     MEM_RM(allocator, points);
   1255     MEM_RM(allocator, lines);
   1256   }
   1257   return res;
   1258 error:
   1259   if(geom) {
   1260     SCAD(geometry_ref_put(geom));
   1261     geom = NULL;
   1262   }
   1263   goto exit;
   1264 }
   1265 
   1266 res_T
   1267 scad_add_box
   1268   (const double xyz[3],
   1269    const double dxdydz[3],
   1270    struct scad_geometry** out_geometry)
   1271 {
   1272   res_T res = RES_OK;
   1273   int ierr, gmsh_ID;
   1274   struct scad_geometry* geom = NULL;
   1275   struct scad_device* dev = get_device();
   1276   struct mem_allocator* allocator = NULL;
   1277 
   1278   if(!xyz || !dxdydz || !out_geometry) {
   1279     res = RES_BAD_ARG;
   1280     goto error;
   1281   }
   1282 
   1283   ERR(check_device(FUNC_NAME));
   1284   allocator = dev->allocator;
   1285 
   1286   gmsh_ID = gmshModelOccAddBox(SPLIT3(xyz), SPLIT3(dxdydz), -1, &ierr);
   1287   ERR(gmsh_err_to_res_T(ierr));
   1288 
   1289   ERR(geometry_create(&geom));
   1290   geom->gmsh_dimTags_n = 2;
   1291   geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags));
   1292   if(!geom->gmsh_dimTags) {
   1293     res = RES_MEM_ERR;
   1294     goto error;
   1295   }
   1296   geom->gmsh_dimTags[0] = 3;
   1297   geom->gmsh_dimTags[1] = gmsh_ID;
   1298 
   1299   ERR(device_register_tags(geom));
   1300 
   1301 exit:
   1302   if(out_geometry) *out_geometry = geom;
   1303   return res;
   1304 error:
   1305   if(geom) {
   1306     SCAD(geometry_ref_put(geom));
   1307     geom = NULL;
   1308   }
   1309   goto exit;
   1310 }
   1311 
   1312 res_T
   1313 scad_add_cylinder
   1314   (const double xyz[3],
   1315    const double axis[3],
   1316    const double radius,
   1317    const double angle,
   1318    struct scad_geometry** out_geometry)
   1319 {
   1320   res_T res = RES_OK;
   1321   int ierr, gmsh_ID;
   1322   struct scad_geometry* geom = NULL;
   1323   struct scad_device* dev = get_device();
   1324   struct mem_allocator* allocator = NULL;
   1325 
   1326   if(!xyz || !axis || radius <= 0 || angle < 0 || angle > 2*PI || !out_geometry) {
   1327     res = RES_BAD_ARG;
   1328     goto error;
   1329   }
   1330 
   1331   ERR(check_device(FUNC_NAME));
   1332   allocator = dev->allocator;
   1333 
   1334   gmsh_ID = gmshModelOccAddCylinder(SPLIT3(xyz), SPLIT3(axis), radius, -1,
   1335       angle, &ierr);
   1336   ERR(gmsh_err_to_res_T(ierr));
   1337 
   1338   ERR(geometry_create(&geom));
   1339   geom->gmsh_dimTags_n = 2;
   1340   geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags));
   1341   if(!geom->gmsh_dimTags) {
   1342     res = RES_MEM_ERR;
   1343     goto error;
   1344   }
   1345   geom->gmsh_dimTags[0] = 3;
   1346   geom->gmsh_dimTags[1] = gmsh_ID;
   1347 
   1348   ERR(device_register_tags(geom));
   1349 
   1350 exit:
   1351   if(out_geometry) *out_geometry = geom;
   1352   return res;
   1353 error:
   1354   if(geom) {
   1355     SCAD(geometry_ref_put(geom));
   1356     geom = NULL;
   1357   }
   1358   goto exit;
   1359 }
   1360 
   1361 int
   1362 scad_add_sphere
   1363   (const double xyz[3],
   1364    const double radius,
   1365    struct scad_geometry** out_geometry)
   1366 {
   1367   res_T res = RES_OK;
   1368   int ierr, gmsh_ID;
   1369   struct scad_geometry* geom = NULL;
   1370   struct scad_device* dev = get_device();
   1371   struct mem_allocator* allocator = NULL;
   1372 
   1373   if(!xyz || radius <= 0 || !out_geometry) {
   1374     res = RES_BAD_ARG;
   1375     goto error;
   1376   }
   1377 
   1378   ERR(check_device(FUNC_NAME));
   1379   allocator = dev->allocator;
   1380 
   1381   gmsh_ID =
   1382     gmshModelOccAddSphere(SPLIT3(xyz), radius, -1, -PI/2, PI/2, 2*PI, &ierr);
   1383   ERR(gmsh_err_to_res_T(ierr));
   1384 
   1385   ERR(geometry_create(&geom));
   1386   geom->gmsh_dimTags_n = 2;
   1387   geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags));
   1388   if(!geom->gmsh_dimTags) {
   1389     res = RES_MEM_ERR;
   1390     goto error;
   1391   }
   1392   geom->gmsh_dimTags[0] = 3;
   1393   geom->gmsh_dimTags[1] = gmsh_ID;
   1394 
   1395   ERR(device_register_tags(geom));
   1396 
   1397 exit:
   1398   if(out_geometry) *out_geometry = geom;
   1399   return res;
   1400 error:
   1401   if(geom) {
   1402     SCAD(geometry_ref_put(geom));
   1403     geom = NULL;
   1404   }
   1405   goto exit;
   1406 }
   1407 
   1408 SCAD_API res_T
   1409 scad_add_cone
   1410   (const double center[3],
   1411    const double axis[3],
   1412    const double radius1,
   1413    const double radius2,
   1414    const double angle,
   1415    struct scad_geometry** cone)
   1416 {
   1417   res_T res = RES_OK;
   1418   int ierr, gmsh_ID;
   1419   struct scad_geometry* geom = NULL;
   1420   struct scad_device* dev = get_device();
   1421   struct mem_allocator* allocator = NULL;
   1422 
   1423   if(!center || !axis || radius1 < 0 || radius2 < 0 || (radius1 == radius2)
   1424       || angle < 0 || angle > 2*PI || !cone)
   1425   {
   1426     res = RES_BAD_ARG;
   1427     goto error;
   1428   }
   1429 
   1430   ERR(check_device(FUNC_NAME));
   1431   allocator = dev->allocator;
   1432 
   1433   gmsh_ID =
   1434     gmshModelOccAddCone(SPLIT3(center), SPLIT3(axis), radius1, radius2,
   1435         -1, 2*PI, &ierr);
   1436   ERR(gmsh_err_to_res_T(ierr));
   1437 
   1438   ERR(geometry_create(&geom));
   1439   geom->gmsh_dimTags_n = 2;
   1440   geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags));
   1441   if(!geom->gmsh_dimTags) {
   1442     res = RES_MEM_ERR;
   1443     goto error;
   1444   }
   1445   geom->gmsh_dimTags[0] = 3;
   1446   geom->gmsh_dimTags[1] = gmsh_ID;
   1447 
   1448   ERR(device_register_tags(geom));
   1449 
   1450 exit:
   1451   if(cone) *cone = geom;
   1452   return res;
   1453 error:
   1454   if(geom) {
   1455     SCAD(geometry_ref_put(geom));
   1456     geom = NULL;
   1457   }
   1458   goto exit;
   1459 }
   1460 
   1461 SCAD_API res_T
   1462 scad_add_torus
   1463   (const double center[3],
   1464    const double radius1,
   1465    const double radius2,
   1466    const double angle,
   1467    const double z_axis[3], /* Can be NULL */
   1468    struct scad_geometry** torus)
   1469 {
   1470   res_T res = RES_OK;
   1471   int ierr, gmsh_ID;
   1472   struct scad_geometry* geom = NULL;
   1473   struct scad_device* dev = get_device();
   1474   struct mem_allocator* allocator = NULL;
   1475 
   1476   if(!center || radius1 <= 0 || radius2 <= 0 || angle < 0 || angle > 2*PI
   1477       || !torus)
   1478   {
   1479     res = RES_BAD_ARG;
   1480     goto error;
   1481   }
   1482 
   1483   ERR(check_device(FUNC_NAME));
   1484   allocator = dev->allocator;
   1485 
   1486   gmsh_ID =
   1487     gmshModelOccAddTorus(SPLIT3(center), radius1, radius2,
   1488         -1, 2*PI, z_axis, (z_axis == NULL ? 0 : 3), &ierr);
   1489   ERR(gmsh_err_to_res_T(ierr));
   1490 
   1491   ERR(geometry_create(&geom));
   1492   geom->gmsh_dimTags_n = 2;
   1493   geom->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*geom->gmsh_dimTags));
   1494   if(!geom->gmsh_dimTags) {
   1495     res = RES_MEM_ERR;
   1496     goto error;
   1497   }
   1498   geom->gmsh_dimTags[0] = 3;
   1499   geom->gmsh_dimTags[1] = gmsh_ID;
   1500 
   1501   ERR(device_register_tags(geom));
   1502 
   1503 exit:
   1504   if(torus) *torus = geom;
   1505   return res;
   1506 error:
   1507   if(geom) {
   1508     SCAD(geometry_ref_put(geom));
   1509     geom = NULL;
   1510   }
   1511   goto exit;
   1512 }
   1513 
   1514 SCAD_API res_T
   1515 scad_geometry_equal
   1516   (const struct scad_geometry* geom1,
   1517    const struct scad_geometry* geom2,
   1518    int* equal)
   1519 {
   1520   res_T res = RES_OK;
   1521   struct scad_device* dev = get_device();
   1522   struct mem_allocator* allocator = NULL;
   1523   struct htable_tags t2, t3;
   1524   int  eq = 1, initialized = 0;
   1525 
   1526   if(!geom1 || !geom2 || !equal) {
   1527     res = RES_BAD_ARG;
   1528     goto error;
   1529   }
   1530 
   1531   ERR(check_device(FUNC_NAME));
   1532   allocator = dev->allocator;
   1533 
   1534   /* Trivial cases */
   1535   if(geom1 == geom2) {
   1536     eq = 1;
   1537   }
   1538   else if(geom1->gmsh_dimTags_n != geom2->gmsh_dimTags_n) {
   1539     eq = 0;
   1540   } else {
   1541     size_t i;
   1542     htable_tags_init(allocator, &t2);
   1543     htable_tags_init(allocator, &t3);
   1544     initialized = 1;
   1545     /* Create tables from geom1 tags */
   1546     for(i = 0; i < geom1->gmsh_dimTags_n; i += 2) {
   1547       char one = 1;
   1548       int d = geom1->gmsh_dimTags[i];
   1549       int t = geom1->gmsh_dimTags[i+1];
   1550       struct htable_tags* tn = (d == 2) ? &t2 : &t3;
   1551       ERR(htable_tags_set(tn, &t, &one));
   1552     }
   1553     ASSERT((htable_tags_size_get(&t2) + htable_tags_size_get(&t3)) * 2
   1554         == geom1->gmsh_dimTags_n);
   1555     /* Check if tags from geom2 are included */
   1556     for(i = 0; i < geom2->gmsh_dimTags_n; i += 2) {
   1557       char* found;
   1558       int d = geom2->gmsh_dimTags[i];
   1559       int t = geom2->gmsh_dimTags[i+1];
   1560       struct htable_tags* tn = (d == 2) ? &t2 : &t3;
   1561       found = htable_tags_find(tn, &t);
   1562       if(!found) {
   1563         eq = 0;
   1564         break;
   1565       }
   1566     }
   1567   }
   1568 
   1569   *equal = eq;
   1570 
   1571 exit:
   1572   if(initialized) {
   1573     htable_tags_release(&t2);
   1574     htable_tags_release(&t3);
   1575   }
   1576   return res;
   1577 error:
   1578   goto exit;
   1579 }
   1580 
   1581 SCAD_API res_T
   1582 scad_geometry_is_included
   1583   (const struct scad_geometry* geometry,
   1584    struct scad_geometry** geometries,
   1585    const size_t geometries_count,
   1586    int* included)
   1587 {
   1588   res_T res = RES_OK;
   1589   struct scad_device* dev = get_device();
   1590   struct mem_allocator* allocator = NULL;
   1591   struct htable_tags t2, t3;
   1592   int initialized = 0;
   1593   size_t i, n;
   1594 
   1595   if(!geometry || !geometries || geometries_count == 0 || !included) {
   1596     res = RES_BAD_ARG;
   1597     goto error;
   1598   }
   1599 
   1600   ERR(check_device(FUNC_NAME));
   1601   allocator = dev->allocator;
   1602 
   1603   /* Trivial case */
   1604   for(i = 0; i < geometries_count; i++) {
   1605     if(geometry == geometries[i]) {
   1606       *included = 1;
   1607       goto exit;
   1608     }
   1609   }
   1610 
   1611   /* Create tables from geometries tags */
   1612   htable_tags_init(allocator, &t2);
   1613   htable_tags_init(allocator, &t3);
   1614   initialized = 1;
   1615   for(n = 0; n < geometries_count; n++) {
   1616     const struct scad_geometry* geom = geometries[n];
   1617     for(i = 0; i < geometries[n]->gmsh_dimTags_n; i += 2) {
   1618       char one = 1;
   1619       int d = geom->gmsh_dimTags[i];
   1620       int t = geom->gmsh_dimTags[i+1];
   1621       struct htable_tags* tn = (d == 2) ? &t2 : &t3;
   1622       ERR(htable_tags_set(tn, &t, &one));
   1623     }
   1624   }
   1625 
   1626   /* Check if tags from geometry are included */
   1627   for(i = 0; i < geometry->gmsh_dimTags_n; i += 2) {
   1628     char* found;
   1629     int d = geometry->gmsh_dimTags[i];
   1630     int t = geometry->gmsh_dimTags[i+1];
   1631     struct htable_tags* tn = (d == 2) ? &t2 : &t3;
   1632     found = htable_tags_find(tn, &t);
   1633     if(!found) {
   1634       *included = 0;
   1635       goto exit;
   1636     }
   1637   }
   1638 
   1639   /* If here, no not-included tag was found */
   1640   *included = 1;
   1641 
   1642 exit:
   1643   if(initialized) {
   1644     htable_tags_release(&t2);
   1645     htable_tags_release(&t3);
   1646   }
   1647   return res;
   1648 error:
   1649   goto exit;
   1650 }
   1651 
   1652 res_T
   1653 scad_geometries_fuse
   1654   (struct scad_geometry** geometries,
   1655    const size_t geometries_count,
   1656    struct scad_geometry** tools,
   1657    const size_t tools_count,
   1658    struct scad_geometry** out_geometry)
   1659 {
   1660   res_T res = RES_OK;
   1661   int* tagout = NULL;
   1662   int** map = NULL;
   1663   size_t* mapn = NULL;
   1664   size_t tagoutn, mapnn = 0, sz1, sz2;
   1665   int* data1 = NULL;
   1666   int* data2 = NULL;
   1667   int ierr = 0;
   1668   struct scad_geometry* geom = NULL;
   1669   struct scad_device* dev = get_device();
   1670   struct mem_allocator* allocator = NULL;
   1671 #ifdef FIX_GMSH_BOOLEAN_OUTPUTS
   1672   int *tmp1 = NULL, *tmp2 = NULL;
   1673   size_t c1, c2;
   1674 #endif
   1675 
   1676   if(!geometries || !geometries_count || !tools || !tools_count || !out_geometry) {
   1677     res = RES_BAD_ARG;
   1678     goto error;
   1679   }
   1680 
   1681   ERR(check_device(FUNC_NAME));
   1682   allocator = dev->allocator;
   1683 
   1684   ERR(gather_tags(geometries, geometries_count, &data1, &sz1));
   1685   ERR(gather_tags(tools, tools_count, &data2, &sz2));
   1686 
   1687 #ifdef FIX_GMSH_BOOLEAN_OUTPUTS
   1688   gmshModelOccCopy(data1, sz1, &tmp1, &c1, &ierr);
   1689   ERR(gmsh_err_to_res_T(ierr));
   1690   gmshModelOccCopy(data2, sz2, &tmp2, &c2, &ierr);
   1691   ERR(gmsh_err_to_res_T(ierr));
   1692   /* We do remove gmsh objects here as they are temporary copies created to
   1693    * allow the remove flag to be used (gmsh is broken here if remove=0) */
   1694   gmshModelOccFuse(tmp1, c1, tmp2, c2, &tagout, &tagoutn, &map, &mapn, &mapnn,
   1695       -1, 1, 1, &ierr);
   1696 #else
   1697   /* We don't remove gmsh objects here; they are only removed when their tags are
   1698    * no longer used by any star-cad geometry */
   1699   gmshModelOccFuse(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn,
   1700       &mapnn, -1, 0, 0, &ierr);
   1701 #endif
   1702 
   1703   ASSERT(tagoutn % 2 == 0);
   1704   ERR(gmsh_err_to_res_T(ierr));
   1705 
   1706   ERR(geometry_create(&geom));
   1707   geom->gmsh_dimTags_n = tagoutn;
   1708   geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout));
   1709   if(!geom->gmsh_dimTags) {
   1710     res = RES_MEM_ERR;
   1711     goto error;
   1712   }
   1713   memcpy(geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout));
   1714 
   1715   ERR(device_register_tags(geom));
   1716 
   1717 exit:
   1718 #ifdef FIX_GMSH_BOOLEAN_OUTPUTS
   1719   gmshFree(tmp1);
   1720   gmshFree(tmp2);
   1721 #endif
   1722   if(out_geometry) *out_geometry = geom;
   1723   if(allocator) {
   1724     MEM_RM(allocator, data1);
   1725     MEM_RM(allocator, data2);
   1726   }
   1727   gmshFree(mapn);
   1728   gmshFree(tagout);
   1729   free_gmsh_map(map, mapnn);
   1730   return res;
   1731 error:
   1732   if(geom) {
   1733     SCAD(geometry_ref_put(geom));
   1734     geom = NULL;
   1735   }
   1736   if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
   1737   goto exit;
   1738 }
   1739 
   1740 res_T
   1741 scad_geometries_collect
   1742   (struct scad_geometry** geometries,
   1743    const size_t geometries_count,
   1744    struct scad_geometry** out_geometry)
   1745 {
   1746   res_T res = RES_OK;
   1747   size_t sz;
   1748   int* data = NULL;
   1749   struct scad_geometry* geom = NULL;
   1750 
   1751   if(!geometries || !geometries_count || !out_geometry) {
   1752     res = RES_BAD_ARG;
   1753     goto error;
   1754   }
   1755 
   1756   ERR(check_device(FUNC_NAME));
   1757 
   1758   ERR(gather_tags(geometries, geometries_count, &data, &sz));
   1759 
   1760   ERR(geometry_create(&geom));
   1761   geom->gmsh_dimTags_n = sz;
   1762   geom->gmsh_dimTags = data;
   1763 
   1764   ERR(device_register_tags(geom));
   1765 
   1766 exit:
   1767   if(out_geometry) *out_geometry = geom;
   1768   return res;
   1769 error:
   1770   if(geom) {
   1771     SCAD(geometry_ref_put(geom));
   1772     geom = NULL;
   1773   }
   1774   goto exit;
   1775 }
   1776 
   1777 res_T
   1778 scad_geometries_cut
   1779   (struct scad_geometry** geometries,
   1780    const size_t geometries_count,
   1781    struct scad_geometry** tools,
   1782    const size_t tools_count,
   1783    struct scad_geometry** out_geometry)
   1784 {
   1785   res_T res = RES_OK;
   1786   int* tagout = NULL;
   1787   int** map = NULL;
   1788   size_t* mapn = NULL;
   1789   size_t tagoutn, mapnn = 0, sz1, sz2;
   1790   int* data1 = NULL;
   1791   int* data2 = NULL;
   1792   int ierr = 0;
   1793   struct scad_geometry* geom = NULL;
   1794   struct scad_device* dev = get_device();
   1795   struct mem_allocator* allocator = NULL;
   1796 #ifdef FIX_GMSH_BOOLEAN_OUTPUTS
   1797   int *tmp1 = NULL, *tmp2 = NULL;
   1798   size_t c1, c2;
   1799 #endif
   1800 
   1801   if(!geometries || !geometries_count || !tools || !tools_count || !out_geometry) {
   1802     res = RES_BAD_ARG;
   1803     goto error;
   1804   }
   1805 
   1806   ERR(check_device(FUNC_NAME));
   1807   allocator = dev->allocator;
   1808 
   1809   ERR(gather_tags(geometries, geometries_count, &data1, &sz1));
   1810   ERR(gather_tags(tools, tools_count, &data2, &sz2));
   1811 
   1812 #ifdef FIX_GMSH_BOOLEAN_OUTPUTS
   1813   gmshModelOccCopy(data1, sz1, &tmp1, &c1, &ierr);
   1814   ERR(gmsh_err_to_res_T(ierr));
   1815   gmshModelOccCopy(data2, sz2, &tmp2, &c2, &ierr);
   1816   ERR(gmsh_err_to_res_T(ierr));
   1817   /* We do remove gmsh objects here as they are temporary copies created to
   1818    * allow the remove flag to be used (gmsh is broken here if remove=0) */
   1819   gmshModelOccCut(tmp1, c1, tmp2, c2, &tagout, &tagoutn, &map, &mapn,
   1820     &mapnn, -1, 1, 1, &ierr);
   1821 #else
   1822   /* We don't remove gmsh objects here; they are only removed when their tags are
   1823    * no longer used by any star-cad geometry */
   1824   gmshModelOccCut(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn,
   1825       &mapnn, -1, 0, 0, &ierr);
   1826 #endif
   1827 
   1828   ASSERT(tagoutn % 2 == 0);
   1829   ERR(gmsh_err_to_res_T(ierr));
   1830 
   1831   ERR(geometry_create(&geom));
   1832   geom->gmsh_dimTags_n = tagoutn;
   1833   geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout));
   1834   if(!geom->gmsh_dimTags) {
   1835     res = RES_MEM_ERR;
   1836     goto error;
   1837   }
   1838   memcpy(geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout));
   1839 
   1840   ERR(device_register_tags(geom));
   1841 
   1842 exit:
   1843   if(out_geometry) *out_geometry = geom;
   1844   if(allocator) {
   1845     MEM_RM(allocator, data1);
   1846     MEM_RM(allocator, data2);
   1847   }
   1848   gmshFree(mapn);
   1849   gmshFree(tagout);
   1850   free_gmsh_map(map, mapnn);
   1851 #ifdef FIX_GMSH_BOOLEAN_OUTPUTS
   1852   gmshFree(tmp1);
   1853   gmshFree(tmp2);
   1854 #endif
   1855   return res;
   1856 error:
   1857   if(ierr) {
   1858     int dim = INT_MAX;
   1859     if(!mixed_dim_err_msg("cut", geometries, geometries_count, &dim))
   1860       mixed_dim_err_msg("cut", tools, tools_count, &dim);
   1861     if(geom) {
   1862       SCAD(geometry_ref_put(geom));
   1863       geom = NULL;
   1864     }
   1865   }
   1866   if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
   1867   goto exit;
   1868 }
   1869 
   1870 res_T
   1871 scad_geometries_intersect
   1872   (struct scad_geometry** geometries,
   1873    const size_t geometries_count,
   1874    struct scad_geometry** tools,
   1875    const size_t tools_count,
   1876    struct scad_geometry** out_geometry)
   1877 {
   1878   res_T res = RES_OK;
   1879   int* tagout = NULL;
   1880   int** map = NULL;
   1881   size_t* mapn = NULL;
   1882   size_t tagoutn, mapnn = 0, sz1, sz2;
   1883   int* data1 = NULL;
   1884   int* data2 = NULL;
   1885   int ierr = 0;
   1886   struct scad_geometry* geom = NULL;
   1887   struct scad_device* dev = get_device();
   1888   struct mem_allocator* allocator = NULL;
   1889 #ifdef FIX_GMSH_BOOLEAN_OUTPUTS
   1890   int *tmp1 = NULL, *tmp2 = NULL;
   1891   size_t c1, c2;
   1892 #endif
   1893 
   1894   if(!geometries || !geometries_count || !tools || !tools_count || !out_geometry) {
   1895     res = RES_BAD_ARG;
   1896     goto error;
   1897   }
   1898 
   1899   ERR(check_device(FUNC_NAME));
   1900   allocator = dev->allocator;
   1901 
   1902   ERR(gather_tags(geometries, geometries_count, &data1, &sz1));
   1903   ERR(gather_tags(tools, tools_count, &data2, &sz2));
   1904 
   1905 #ifdef FIX_GMSH_BOOLEAN_OUTPUTS
   1906   gmshModelOccCopy(data1, sz1, &tmp1, &c1, &ierr);
   1907   ERR(gmsh_err_to_res_T(ierr));
   1908   gmshModelOccCopy(data2, sz2, &tmp2, &c2, &ierr);
   1909   ERR(gmsh_err_to_res_T(ierr));
   1910   /* We do remove gmsh objects here as they are temporary copies created to
   1911    * allow the remove flag to be used (gmsh is broken here if remove=0) */
   1912   gmshModelOccIntersect(tmp1, c1, tmp2, c2, &tagout, &tagoutn, &map,
   1913     &mapn, &mapnn, -1, 1, 1, &ierr);
   1914 #else
   1915   /* We don't remove gmsh objects here; they are only removed when their tags are
   1916    * no longer used by any star-cad geometry */
   1917   gmshModelOccIntersect(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn,
   1918       &mapnn, -1, 0, 0, &ierr);
   1919 #endif
   1920 
   1921   ASSERT(tagoutn % 2 == 0);
   1922   ERR(gmsh_err_to_res_T(ierr));
   1923 
   1924   ERR(geometry_create(&geom));
   1925   geom->gmsh_dimTags_n = tagoutn;
   1926   if(tagoutn != 0){
   1927     geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout));
   1928     if(!geom->gmsh_dimTags) {
   1929       res = RES_MEM_ERR;
   1930       goto error;
   1931     }
   1932     memcpy(geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout));
   1933   }
   1934 
   1935   ERR(device_register_tags(geom));
   1936 
   1937 exit:
   1938   if(out_geometry) *out_geometry = geom;
   1939   if(allocator) {
   1940     MEM_RM(allocator, data1);
   1941     MEM_RM(allocator, data2);
   1942   }
   1943   gmshFree(mapn);
   1944   gmshFree(tagout);
   1945   free_gmsh_map(map, mapnn);
   1946 #ifdef FIX_GMSH_BOOLEAN_OUTPUTS
   1947   gmshFree(tmp1);
   1948   gmshFree(tmp2);
   1949 #endif
   1950   return res;
   1951 error:
   1952   if(geom) {
   1953     SCAD(geometry_ref_put(geom));
   1954     geom = NULL;
   1955   }
   1956   if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
   1957   goto exit;
   1958 }
   1959 
   1960 res_T
   1961 scad_geometries_common_boundaries
   1962   (struct scad_geometry** geometries,
   1963    const size_t geometries_count,
   1964    struct scad_geometry** tools,
   1965    const size_t tools_count,
   1966    struct scad_geometry*** out_boundaries,
   1967    size_t *out_count)
   1968 {
   1969   res_T res = RES_OK;
   1970   int* tagout = NULL;
   1971   size_t tagoutn, sz1, sz2, u_sz;
   1972   int* data1 = NULL;
   1973   int* data2 = NULL;
   1974   int* unique = NULL;
   1975   int ierr = 0;
   1976   int* bound1 = NULL;
   1977   int* bound2 = NULL;
   1978   size_t n1, n2;
   1979   struct scad_geometry** out_geom = NULL;
   1980   struct mem_allocator* allocator = NULL;
   1981   struct scad_device* dev = get_device();
   1982   int log;
   1983   enum log_type log_type;
   1984   size_t i, c = 0, n;
   1985   struct str msg;
   1986   int msg_initialized = 0;
   1987 
   1988   if(!geometries || !geometries_count || !tools || !tools_count
   1989       || !out_boundaries || !out_count)
   1990   {
   1991     res = RES_BAD_ARG;
   1992     goto error;
   1993   }
   1994 
   1995   ERR(check_device(FUNC_NAME));
   1996   allocator = dev->allocator;
   1997 
   1998   ERR(gather_tags(geometries, geometries_count, &data1, &sz1));
   1999   ERR(gather_tags(tools, tools_count, &data2, &sz2));
   2000 
   2001   /* data1 and data2 can have tags in common: deduplicate them!
   2002    * (even if the refcounting stuff can manage duplicates) */
   2003   ERR(process_tag_list(data1, sz1, data2, sz2, UNIQUE_TAGS, &unique, &u_sz));
   2004 
   2005   ERR(sync_device());
   2006   gmshModelGetBoundary(data1, sz1, &bound1, &n1, 1, 0, 0, &ierr);
   2007   ERR(gmsh_err_to_res_T(ierr));
   2008   gmshModelGetBoundary(data2, sz2, &bound2, &n2, 1, 0, 0, &ierr);
   2009   ERR(gmsh_err_to_res_T(ierr));
   2010 
   2011   ERR(process_tag_list(bound1, n1, bound2, n2, COMMON_TAGS, &tagout, &tagoutn));
   2012   ASSERT(tagoutn % 2 == 0);
   2013   c = tagoutn / 2;
   2014 
   2015   if(tagoutn == 0) {
   2016     log_message(dev, "Common boundaries list is empty.\n");
   2017     goto exit;
   2018   }
   2019 
   2020   log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL);
   2021   log_type = dev->log_type;
   2022   if(log) {
   2023     str_init(allocator, &msg);
   2024     msg_initialized = 1;
   2025     logger_print(dev->logger, log_type,
   2026         "Common boundaries specific tag management:\n");
   2027     ERR(str_printf(&msg, "  tags ["));
   2028     for(i = 0; i < tagoutn; i += 2) {
   2029       const int dim = tagout[i];
   2030       const int tag = tagout[i+1];
   2031       ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
   2032     }
   2033     ERR(str_append_printf(&msg, "] getting a ref to tags ["));
   2034     for(i = 0; i < u_sz; i += 2) {
   2035       const int dim = unique[i];
   2036       const int tag = unique[i+1];
   2037       ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
   2038     }
   2039     logger_print(dev->logger, log_type, "%s].\n", str_cget(&msg));
   2040   }
   2041 
   2042   out_geom = MEM_CALLOC(allocator, c, sizeof(*out_geom));
   2043   if(!out_geom) {
   2044     res = RES_MEM_ERR;
   2045     goto error;
   2046   }
   2047 
   2048   for(i = 0, n = 0; i < tagoutn; i += 2, n++) {
   2049     int dim = tagout[i];
   2050     int tag = tagout[i+1];
   2051     struct tag_desc* desc;
   2052     ASSERT(dim == 2);
   2053     ERR(geometry_create(out_geom+n));
   2054     out_geom[n]->gmsh_dimTags_n = 2;
   2055     out_geom[n]->gmsh_dimTags = MEM_ALLOC(allocator,
   2056         2 * sizeof(*out_geom[n]->gmsh_dimTags));
   2057     if(!out_geom[n]->gmsh_dimTags) {
   2058       res = RES_MEM_ERR;
   2059       goto error;
   2060     }
   2061     out_geom[n]->gmsh_dimTags[0] = dim;
   2062     out_geom[n]->gmsh_dimTags[1] = tag;
   2063     ERR(device_register_tags(out_geom[n]));
   2064     /* Need to protect out_geometry's tags by getting a ref on input tags or
   2065      * deleting input geometry will possibly delete them */
   2066     ERR(device_register_ref_to_tags(dim, tag, unique, u_sz));
   2067     /* As the 2D tags will be deleted when the 3D tag they are part of are
   2068      * deleted, they shouldn't be deleted when the geometry they belongs to are
   2069      * released. */
   2070     desc = device_get_description(dim, tag);
   2071     ASSERT(desc);
   2072     desc->delete_policy = Scad_do_not_delete;
   2073   }
   2074 
   2075 exit:
   2076   if(msg_initialized) str_release(&msg);
   2077   if(out_boundaries) *out_boundaries = out_geom;
   2078   if(out_count) *out_count = c;
   2079   if(allocator) {
   2080     MEM_RM(allocator, data1);
   2081     MEM_RM(allocator, data2);
   2082     MEM_RM(allocator, unique);
   2083     MEM_RM(allocator, tagout);
   2084   }
   2085   gmshFree(bound1);
   2086   gmshFree(bound2);
   2087   return res;
   2088 error:
   2089   if(ierr) {
   2090     int dim = INT_MAX;
   2091     if(!mixed_dim_err_msg("common boundaries", geometries, geometries_count, &dim))
   2092       mixed_dim_err_msg("common boundaries", tools, tools_count, &dim);
   2093   }
   2094   if(out_geom) {
   2095     for(i = 0; i < c; i++) {
   2096       if(out_geom[i]) SCAD(geometry_ref_put(out_geom[i]));
   2097     }
   2098     MEM_RM(allocator, out_geom);
   2099     out_geom = NULL;
   2100     c = 0;
   2101   }
   2102   if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
   2103   goto exit;
   2104 }
   2105 
   2106 res_T
   2107 scad_geometries_common_boundary
   2108   (struct scad_geometry** geometries,
   2109    const size_t geometries_count,
   2110    struct scad_geometry** tools,
   2111    const size_t tools_count,
   2112    struct scad_geometry** out_boundary)
   2113 {
   2114   res_T res = RES_OK;
   2115   int* tagout = NULL;
   2116   size_t tagoutn, sz1, sz2, u_sz;
   2117   int* data1 = NULL;
   2118   int* data2 = NULL;
   2119   int* unique = NULL;
   2120   int ierr = 0;
   2121   int* bound1 = NULL;
   2122   int* bound2 = NULL;
   2123   size_t n1, n2;
   2124   struct scad_geometry* out_geom = NULL;
   2125   struct mem_allocator* allocator = NULL;
   2126   struct scad_device* dev = get_device();
   2127   int log;
   2128   enum log_type log_type;
   2129   size_t i, n;
   2130   struct str msg;
   2131   int msg_initialized = 0;
   2132 
   2133   if(!geometries || !geometries_count || !tools || !tools_count || !out_boundary) {
   2134     res = RES_BAD_ARG;
   2135     goto error;
   2136   }
   2137 
   2138   ERR(check_device(FUNC_NAME));
   2139   allocator = dev->allocator;
   2140 
   2141   ERR(gather_tags(geometries, geometries_count, &data1, &sz1));
   2142   ERR(gather_tags(tools, tools_count, &data2, &sz2));
   2143 
   2144   /* data1 and data2 can have tags in common: deduplicate them!
   2145    * (even if the refcounting stuff can manage duplicates) */
   2146   ERR(process_tag_list(data1, sz1, data2, sz2, UNIQUE_TAGS, &unique, &u_sz));
   2147 
   2148   ERR(sync_device());
   2149   gmshModelGetBoundary(data1, sz1, &bound1, &n1, 1, 0, 0, &ierr);
   2150   ERR(gmsh_err_to_res_T(ierr));
   2151   gmshModelGetBoundary(data2, sz2, &bound2, &n2, 1, 0, 0, &ierr);
   2152   ERR(gmsh_err_to_res_T(ierr));
   2153 
   2154   ERR(process_tag_list(bound1, n1, bound2, n2, COMMON_TAGS, &tagout, &tagoutn));
   2155   ASSERT(tagoutn % 2 == 0);
   2156 
   2157   if(tagoutn == 0) {
   2158     log_message(dev, "Common boundary list is empty.\n");
   2159   } else {
   2160     log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL);
   2161     log_type = dev->log_type;
   2162     if(log) {
   2163       str_init(allocator, &msg);
   2164       msg_initialized = 1;
   2165       logger_print(dev->logger, log_type,
   2166           "Common boundary specific tag management:\n");
   2167       ERR(str_printf(&msg, "  tags ["));
   2168       for(i = 0; i < tagoutn; i += 2) {
   2169         const int dim = tagout[i];
   2170         const int tag = tagout[i+1];
   2171         ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
   2172       }
   2173       ERR(str_append_printf(&msg, "] getting a ref to tags ["));
   2174       for(i = 0; i < u_sz; i += 2) {
   2175         const int dim = unique[i];
   2176         const int tag = unique[i+1];
   2177         ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
   2178       }
   2179       logger_print(dev->logger, log_type, "%s].\n", str_cget(&msg));
   2180     }
   2181   }
   2182 
   2183   ERR(geometry_create(&out_geom));
   2184   out_geom->gmsh_dimTags_n = tagoutn;
   2185   out_geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout));
   2186   if(!out_geom->gmsh_dimTags) {
   2187     res = RES_MEM_ERR;
   2188     goto error;
   2189   }
   2190   memcpy(out_geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout));
   2191   ERR(device_register_tags(out_geom));
   2192 
   2193   for(i = 0, n = 0; i < tagoutn; i += 2, n++) {
   2194     int dim = tagout[i];
   2195     int tag = tagout[i+1];
   2196     struct tag_desc* desc = device_get_description(dim, tag);
   2197     ASSERT(desc);
   2198     ASSERT(dim == 2);
   2199     /* Need to protect out_geometry's tags by getting a ref on input tags or
   2200      * deleting input geometry will possibly delete them */
   2201     ERR(device_register_ref_to_tags(dim, tag, unique, u_sz));
   2202     /* As the 2D tags will be deleted when the 3D tag they are part of are
   2203      * deleted, they shouldn't be deleted when the geometry they belongs to are
   2204      * released. */
   2205     desc->delete_policy = Scad_do_not_delete;
   2206   }
   2207 
   2208 exit:
   2209   if(msg_initialized) str_release(&msg);
   2210   if(out_boundary) *out_boundary = out_geom;
   2211   if(allocator) {
   2212     MEM_RM(allocator, data1);
   2213     MEM_RM(allocator, data2);
   2214     MEM_RM(allocator, unique);
   2215     MEM_RM(allocator, tagout);
   2216   }
   2217   gmshFree(bound1);
   2218   gmshFree(bound2);
   2219   return res;
   2220 error:
   2221   if(ierr) {
   2222     int dim = INT_MAX;
   2223     if(!mixed_dim_err_msg("common boundary", geometries, geometries_count, &dim))
   2224       mixed_dim_err_msg("common boundary", tools, tools_count, &dim);
   2225   }
   2226   if(out_geom) {
   2227     SCAD(geometry_ref_put(out_geom));
   2228     out_geom = NULL;
   2229   }
   2230   if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
   2231   goto exit;
   2232 }
   2233 
   2234 res_T
   2235 scad_geometry_rotate
   2236   (const struct scad_geometry* geom,
   2237    const double pt[3],
   2238    const double axis[3],
   2239    const double angle,
   2240    struct scad_geometry** out_geometry)
   2241 {
   2242   int ierr = 0;
   2243   struct scad_geometry* out = NULL;
   2244   res_T res = RES_OK;
   2245 
   2246   if(!geom || !pt || !axis || ! out_geometry) {
   2247     res = RES_BAD_ARG;
   2248     goto error;
   2249   }
   2250 
   2251   ERR(check_device(FUNC_NAME));
   2252 
   2253   ERR(scad_geometry_copy(geom, &out));
   2254 
   2255   gmshModelOccRotate(out->gmsh_dimTags, out->gmsh_dimTags_n, SPLIT3(pt),
   2256       SPLIT3(axis), angle, &ierr);
   2257   ERR(gmsh_err_to_res_T(ierr));
   2258 
   2259 exit:
   2260   if(out_geometry) *out_geometry = out;
   2261   return res;
   2262 error:
   2263   if(out) SCAD(geometry_ref_put(out));
   2264   out = NULL;
   2265   goto exit;
   2266 }
   2267 
   2268 res_T
   2269 scad_geometry_extrude
   2270   (const struct scad_geometry* geom,
   2271    const double dxdydz[3],
   2272    struct scad_geometry** out_geometry)
   2273 {
   2274   res_T res = RES_OK;
   2275   int *tagout = NULL;
   2276   size_t tagoutn;
   2277   size_t i;
   2278   int *ed, *extrude_data = NULL;
   2279   size_t extrude_sz = 0;
   2280   int ierr = 0;
   2281   struct scad_geometry* extrude_geom = NULL;
   2282   struct scad_device* dev = get_device();
   2283   int log;
   2284   enum log_type log_type;
   2285   struct mem_allocator* allocator = NULL;
   2286   struct str msg;
   2287   int init = 0;
   2288 
   2289   if(!geom || !dxdydz || !out_geometry) {
   2290     res = RES_BAD_ARG;
   2291     goto error;
   2292   }
   2293 
   2294   ERR(check_device(FUNC_NAME));
   2295   allocator = dev->allocator;
   2296 
   2297   /* OCC cannot extrude 3D geometries */
   2298   for(i = 0; i < geom->gmsh_dimTags_n; i += 2) {
   2299     const int dim = geom->gmsh_dimTags[i];
   2300     if(dim != 2) {
   2301       if(str_is_empty(&geom->name)) {
   2302         log_error(get_device(),
   2303             "Cannot extrude unnamed 3D geometry '%p'.\n",
   2304             (void*)geom);
   2305       } else {
   2306         log_error(get_device(),
   2307             "Cannot extrude 3D geometry '%s'.\n",
   2308             str_cget(&geom->name));
   2309       }
   2310       res = RES_BAD_ARG;
   2311       goto error;
   2312     }
   2313   }
   2314 
   2315   gmshModelOccExtrude(geom->gmsh_dimTags, geom->gmsh_dimTags_n, SPLIT3(dxdydz),
   2316       &tagout, &tagoutn, NULL, 0, NULL, 0, 0, &ierr);
   2317   ERR(gmsh_err_to_res_T(ierr));
   2318 
   2319   /* Output includes both the 3D result and its 2D constituents.
   2320    * Keep only 3D entities. */
   2321   for(i = 0; i < tagoutn; i += 2) {
   2322     int dim = tagout[i];
   2323     if(dim == 3) extrude_sz += 2;
   2324   }
   2325   extrude_data = MEM_ALLOC(allocator, extrude_sz * sizeof(*extrude_data));
   2326   if(!extrude_data) {
   2327     res = RES_MEM_ERR;
   2328     goto error;
   2329   }
   2330   ed = extrude_data;
   2331   for(i = 0; i < tagoutn; i += 2) {
   2332     int dim = tagout[i];
   2333     int tag = tagout[i+1];
   2334     if(dim == 3) {
   2335       *ed++ = dim;
   2336       *ed++ = tag;
   2337     }
   2338   }
   2339 
   2340   ERR(geometry_create(&extrude_geom));
   2341   extrude_geom->gmsh_dimTags_n = extrude_sz;
   2342   extrude_geom->gmsh_dimTags = extrude_data;
   2343 
   2344   ERR(device_register_tags(extrude_geom));
   2345 
   2346   log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL);
   2347   log_type = dev->log_type;
   2348   if(log) {
   2349     str_init(allocator, &msg);
   2350     init = 1;
   2351     logger_print(dev->logger, log_type, "Extrude specific tag management:\n");
   2352     ERR(str_printf(&msg, "  tags ["));
   2353     for(i = 0; i < geom->gmsh_dimTags_n; i += 2) {
   2354       const int dim = geom->gmsh_dimTags[i];
   2355       const int tag = geom->gmsh_dimTags[i+1];
   2356       ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
   2357     }
   2358     ERR(str_append_printf(&msg, "] getting a ref to tags ["));
   2359     for(i = 0; i < extrude_sz; i += 2) {
   2360       const int dim = extrude_data[i];
   2361       const int tag = extrude_data[i+1];
   2362       ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
   2363     }
   2364     logger_print(dev->logger, log_type, "%s].\n", str_cget(&msg));
   2365   }
   2366   for(i = 0; i < geom->gmsh_dimTags_n; i += 2) {
   2367     const int dim = geom->gmsh_dimTags[i];
   2368     const int tag = geom->gmsh_dimTags[i+1];
   2369     struct tag_desc* desc = device_get_description(dim, tag);
   2370     ASSERT(dim == 2);
   2371     ASSERT(desc != NULL);
   2372     /* Need to protect input geometry's tags by getting a ref on output tags or
   2373      * deleting out_geometry will possibly delete them */
   2374     ERR(device_register_ref_to_tags(dim, tag, extrude_data, extrude_sz));
   2375     /* As the 2D tags will be deleted when the 3D tag they are part of are
   2376      * deleted, they shouldn't be deleted when the geometry they belongs to is
   2377      * released. */
   2378     desc->delete_policy = Scad_do_not_delete;
   2379   }
   2380 
   2381 exit:
   2382   if(init) str_release(&msg);
   2383   if(out_geometry) *out_geometry = extrude_geom;
   2384   gmshFree(tagout);
   2385   return res;
   2386 error:
   2387   if(extrude_geom) {
   2388     SCAD(geometry_ref_put(extrude_geom));
   2389     extrude_geom = NULL;
   2390   }
   2391   if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
   2392   goto exit;
   2393 }
   2394 
   2395 res_T
   2396 scad_geometry_explode
   2397   (const struct scad_geometry* geom,
   2398    struct scad_geometry*** out_geometry,
   2399    size_t* out_n)
   2400 {
   2401   res_T res = RES_OK;
   2402   int* data = NULL;
   2403   size_t i, n, sz = 0;
   2404   struct scad_geometry** geom_array = NULL;
   2405   struct str name;
   2406   int name_initialized = 0;
   2407   struct scad_device* dev = get_device();
   2408   struct mem_allocator* allocator = NULL;
   2409 
   2410   if(!geom || !out_geometry || !out_n) {
   2411     res = RES_BAD_ARG;
   2412     goto error;
   2413   }
   2414 
   2415   ERR(check_device(FUNC_NAME));
   2416 
   2417   allocator = dev->allocator;
   2418   data = geom->gmsh_dimTags;
   2419   sz = geom->gmsh_dimTags_n;
   2420 
   2421   ASSERT(sz % 2 == 0);
   2422   geom_array = MEM_CALLOC(allocator, sz/2, sizeof(*geom_array));
   2423   if(!geom_array) {
   2424     res = RES_MEM_ERR;
   2425     goto error;
   2426   }
   2427 
   2428   for(i = 0, n = 0; i < sz; i += 2, n++) {
   2429     const int dim = data[i];
   2430     const int tag = data[i+1];
   2431     ERR(geometry_create(geom_array+n));
   2432     geom_array[n]->gmsh_dimTags_n = 2;
   2433     geom_array[n]->gmsh_dimTags
   2434       = MEM_ALLOC(allocator, 2 * sizeof(*geom_array[0]->gmsh_dimTags));
   2435     if(!geom_array[n]->gmsh_dimTags) {
   2436       res = RES_MEM_ERR;
   2437       goto error;
   2438     }
   2439     geom_array[n]->gmsh_dimTags[0] = dim;
   2440     geom_array[n]->gmsh_dimTags[1] = tag;
   2441 
   2442     ERR(device_register_tags(geom_array[n]));
   2443   }
   2444 
   2445 exit:
   2446   if(out_n) *out_n = sz/2 ;
   2447   if(out_geometry) *out_geometry = geom_array;
   2448   if(name_initialized) str_release(&name);
   2449   return res;
   2450 error:
   2451   if(geom_array) {
   2452     for(i = 0, n = 0; i < sz; i += 2, n++) {
   2453       if(geom_array[n]) SCAD(geometry_ref_put(geom_array[n]));
   2454     }
   2455     MEM_RM(allocator, geom_array);
   2456     geom_array = NULL;
   2457   }
   2458   goto exit;
   2459 }
   2460 
   2461 
   2462 res_T
   2463 scad_geometry_copy
   2464   (const struct scad_geometry* geom,
   2465    struct scad_geometry** out_geometry)
   2466 {
   2467   res_T res = RES_OK;
   2468   int* data1;
   2469   int* tagout = NULL;
   2470   size_t sz1, tagoutn;
   2471   int ierr = 0;
   2472   struct scad_geometry* copy = NULL;
   2473   struct scad_device* dev = get_device();
   2474   struct mem_allocator* allocator = NULL;
   2475 
   2476   if(!geom || !out_geometry) {
   2477     res = RES_BAD_ARG;
   2478     goto error;
   2479   }
   2480 
   2481   ERR(check_device(FUNC_NAME));
   2482   allocator = dev->allocator;
   2483 
   2484   sz1 = geom->gmsh_dimTags_n;
   2485   data1 = geom->gmsh_dimTags;
   2486   gmshModelOccCopy(data1, sz1, &tagout, &tagoutn, &ierr);
   2487 
   2488   ASSERT(tagoutn % 2 == 0);
   2489   ERR(gmsh_err_to_res_T(ierr));
   2490 
   2491   ERR(geometry_create(&copy));
   2492   copy->gmsh_dimTags_n = tagoutn;
   2493   copy->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout));
   2494   if(!copy->gmsh_dimTags) {
   2495     res = RES_MEM_ERR;
   2496     goto error;
   2497   }
   2498   memcpy(copy->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout));
   2499 
   2500   ERR(device_register_tags(copy));
   2501 
   2502 exit:
   2503   if(out_geometry) *out_geometry = copy;
   2504   gmshFree(tagout);
   2505   return res;
   2506 error:
   2507   if(copy) {
   2508     SCAD(geometry_ref_put(copy));
   2509     copy = NULL;
   2510   }
   2511   if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
   2512   goto exit;
   2513 }
   2514 
   2515 res_T
   2516 scad_geometries_set_name
   2517   (struct scad_geometry** geometries,
   2518    const size_t geometries_count,
   2519    const char* prefix_name, /* Can be NULL */
   2520    const size_t from_rank) /* Can be NULL */
   2521 {
   2522   res_T res = RES_OK;
   2523   struct str tmp;
   2524   int initialized = 0;
   2525   struct mem_allocator* allocator = NULL;
   2526   size_t i, cpt;
   2527 
   2528   if(!geometries || geometries_count == 0) {
   2529     res = RES_BAD_ARG;
   2530     goto error;
   2531   }
   2532 
   2533   ERR(check_device(FUNC_NAME));
   2534 
   2535   if(!prefix_name) {
   2536     for(i = 0; i < geometries_count; i++) {
   2537       ERR(geom_set_name(geometries[i], NULL));
   2538     }
   2539   }
   2540   else if(strlen(prefix_name) == 0) {
   2541     log_error(get_device(), "Geometry name \"\" is invalid.\n");
   2542     res = RES_BAD_ARG;
   2543     goto error;
   2544   } else {
   2545     str_init(allocator, &tmp);
   2546     initialized = 1;
   2547     cpt = from_rank;
   2548     for(i = 0; i < geometries_count; i++) {
   2549       ERR(str_printf(&tmp, "%s_%ld", prefix_name, cpt++));
   2550       ERR(geom_set_name(geometries[i], &tmp));
   2551     }
   2552   }
   2553 
   2554 exit:
   2555   if(initialized) str_release(&tmp);
   2556   return res;
   2557 error:
   2558   goto exit;
   2559 }
   2560 
   2561 res_T
   2562 scad_geometry_set_name
   2563   (struct scad_geometry* geom,
   2564    const char* name) /* Can be NULL */
   2565 {
   2566   res_T res = RES_OK;
   2567   struct str tmp;
   2568   int initialized = 0;
   2569   struct mem_allocator* allocator = NULL;
   2570 
   2571   if(!geom) {
   2572     res = RES_BAD_ARG;
   2573     goto error;
   2574   }
   2575 
   2576   ERR(check_device(FUNC_NAME));
   2577 
   2578   if(!name) {
   2579     ERR(geom_set_name(geom, NULL));
   2580   }
   2581   else if(strlen(name) == 0) {
   2582     log_error(get_device(), "Geometry name \"\" is invalid.\n");
   2583     res = RES_BAD_ARG;
   2584     goto error;
   2585   } else {
   2586     str_init(allocator, &tmp);
   2587     initialized = 1;
   2588     ERR(str_set(&tmp, name));
   2589     ERR(geom_set_name(geom, &tmp));
   2590   }
   2591 
   2592 exit:
   2593   if(initialized) str_release(&tmp);
   2594   return res;
   2595 error:
   2596   goto exit;
   2597 }
   2598 
   2599 res_T
   2600 scad_geometry_translate
   2601   (const struct scad_geometry* geom,
   2602    const double dxdydz[3],
   2603    struct scad_geometry** out_geometry)
   2604 {
   2605   int ierr = 0;
   2606   struct scad_geometry* out = NULL;
   2607   res_T res = RES_OK;
   2608 
   2609   if(!geom || !dxdydz || ! out_geometry) {
   2610     res = RES_BAD_ARG;
   2611     goto error;
   2612   }
   2613 
   2614   ERR(check_device(FUNC_NAME));
   2615 
   2616   ERR(scad_geometry_copy(geom, &out));
   2617 
   2618   gmshModelOccTranslate(out->gmsh_dimTags, out->gmsh_dimTags_n, SPLIT3(dxdydz),
   2619       &ierr);
   2620   ERR(gmsh_err_to_res_T(ierr));
   2621 
   2622 exit:
   2623   if(out_geometry) *out_geometry = out;
   2624   return res;
   2625 error:
   2626   if(out) SCAD(geometry_ref_put(out));
   2627   out = NULL;
   2628   goto exit;
   2629 }
   2630 
   2631 static char
   2632 geom_uses_dimTag
   2633   (const struct scad_geometry* g,
   2634    const int dim,
   2635    const int tag)
   2636 {
   2637   size_t i;
   2638   ASSERT(g);
   2639   for(i = 0; i < g->gmsh_dimTags_n; i+=2) {
   2640     int d = g->gmsh_dimTags[i];
   2641     int t = g->gmsh_dimTags[i+1];
   2642     if(dim == d && tag == t) return 1;
   2643   }
   2644   return 0;
   2645 }
   2646 
   2647 res_T
   2648 scad_geometries_partition
   2649   (struct scad_geometry** geometries,
   2650    const size_t geometries_count,
   2651    const int flags,
   2652    struct scad_geometry** out_geometries)
   2653 {
   2654   res_T res = RES_OK;
   2655   size_t i;
   2656   int* tagout = NULL;
   2657   int** map = NULL;
   2658   size_t* mapn = NULL;
   2659   size_t tagoutn = 0, mapnn = 0, sz;
   2660   int* data = NULL;
   2661   int ierr = 0;
   2662   struct scad_geometry** geoms = NULL;
   2663   struct htable_mappings m2, m3;
   2664   struct scad_device* dev = get_device();
   2665   struct htable_tags t2, t3;
   2666   struct htable_tags_iterator it, end;
   2667   int ht_initialized = 0, tmp_initialized = 0;
   2668   struct mem_allocator* allocator = NULL;
   2669   int dont_call_fragment = 0;
   2670   char* overlap = NULL;
   2671   const int invalid_flags =
   2672     ~(SCAD_ALLOW_OVERLAPPING | SCAD_DUMP_ON_OVERLAPPING_ERROR);
   2673   const int dump_overlapping_err = flags & SCAD_DUMP_ON_OVERLAPPING_ERROR;
   2674   static size_t err_cpt = 0;
   2675   struct str tmp;
   2676 
   2677   if(!geometries || !geometries_count || !out_geometries || flags & invalid_flags) {
   2678     res = RES_BAD_ARG;
   2679     goto error;
   2680   }
   2681 
   2682   ERR(check_device(FUNC_NAME));
   2683   allocator = dev->allocator;
   2684   str_init(allocator, &tmp);
   2685   tmp_initialized = 1;
   2686 
   2687   ERR(gather_tags(geometries, geometries_count, &data, &sz));
   2688 
   2689   /* Create output geometries */
   2690   geoms = MEM_CALLOC(allocator, geometries_count, sizeof(*geoms));
   2691   if(!geoms) {
   2692     res = RES_MEM_ERR;
   2693     goto error;
   2694   }
   2695 
   2696   dont_call_fragment = (sz == 2);
   2697   if(dont_call_fragment) {
   2698     /* gmshModelOccFragment doesn't allow to process a single entity: need to
   2699      * create the result (same as input) here.
   2700      * Not so simple as one could have provided more than 1 input geometry with
   2701      * identical or no dim.tag(s). */
   2702     mapnn = 1;
   2703     map = gmshMalloc(sizeof(*map));
   2704     map[0] = gmshMalloc(2 * sizeof(**map));
   2705     mapn = gmshMalloc(sizeof(*mapn));
   2706     if(!map || !map[0] || !mapn) {
   2707       res = RES_MEM_ERR;
   2708       goto error;
   2709     }
   2710     mapn[0] = 2;
   2711     map[0][0] = data[0];
   2712     map[0][1] = data[1];
   2713   } else {
   2714     /* As a general principle, we don't remove gmsh objects directly; they are
   2715      * only removed from scad_geometry_ref_put when their tags are no longer
   2716      * used by any star-cad geometry. */
   2717     gmshModelOccFragment(data, sz, NULL, 0, &tagout, &tagoutn, &map, &mapn,
   2718         &mapnn, -1, 0, 0, &ierr);
   2719     ERR(gmsh_err_to_res_T(ierr));
   2720     ASSERT(sz == 2*mapnn); /* Because input tags where deduplicated */
   2721 
   2722     /* Check first if there was an overlapping problem */
   2723     if(!(flags & SCAD_ALLOW_OVERLAPPING)) {
   2724       /* No overlapping means that each tag in geometries is translated into a
   2725        * single tag in map */
   2726       size_t ov = 0;
   2727       overlap = MEM_CALLOC(allocator, geometries_count, sizeof(*overlap));
   2728       if(!overlap) {
   2729         res = RES_MEM_ERR;
   2730         goto error;
   2731       }
   2732       for(i = 0; i < mapnn; i++) {
   2733         if(mapn[i] != 2) {
   2734           int dim = data[2*i];
   2735           int tag = data[2*i+1];
   2736           size_t k;
   2737           ov++;
   2738           res = RES_BAD_ARG;
   2739           /* dim.tag #i overlaps: search geometries using it in input */
   2740           for(k = 0; k < geometries_count; k++) {
   2741             overlap[k] |= geom_uses_dimTag(geometries[k], dim, tag);
   2742           }
   2743         }
   2744       }
   2745       if(ov) {
   2746         size_t k, item_cpt = 0;
   2747         res_T tmp_err;
   2748         if(dump_overlapping_err) {
   2749           ERR(scad_scene_mesh());
   2750         }
   2751         for(k = 0; k < geometries_count; k++) {
   2752           struct scad_geometry* g = geometries[k];
   2753           if(!overlap[k])
   2754             continue;
   2755           tmp_err = RES_BAD_OP;
   2756           if(dump_overlapping_err) {
   2757             if(str_is_empty(&g->name)) {
   2758               str_printf(&tmp, "unamed_partition_error_%lu_%lu",
   2759                   err_cpt, (long unsigned)item_cpt++);
   2760               tmp_err =
   2761                 scad_stl_export(g, str_cget(&tmp), SCAD_KEEP_NORMALS_UNCHANGED, 0);
   2762               if(tmp_err == RES_OK) {
   2763                 log_error(get_device(),
   2764                     "Unnamed geometry '%p' overlapping (dumped in '%s).\n",
   2765                     (void*)g, str_cget(&tmp));
   2766               } else {
   2767                 log_error(get_device(), "Could not dump geometry.\n");
   2768               }
   2769             } else {
   2770               str_printf(&tmp, "%s_partition_error_%lu_%lu",
   2771                   str_cget(&g->name), err_cpt, (long unsigned)item_cpt++);
   2772               tmp_err =
   2773                 scad_stl_export(g, str_cget(&tmp), SCAD_KEEP_NORMALS_UNCHANGED, 0);
   2774               if(tmp_err == RES_OK) {
   2775                 log_error(get_device(),
   2776                     "Geometry '%s' overlapping (dumped in '%s).\n",
   2777                     str_cget(&g->name), str_cget(&tmp));
   2778               } else {
   2779                 log_error(get_device(), "Could not dump geometry.\n");
   2780               }
   2781             }
   2782           }
   2783           if(tmp_err != RES_OK) { /* not dumped */
   2784             if(str_is_empty(&g->name)) {
   2785               log_error(get_device(), "Unnamed geometry '%p' overlapping.\n",
   2786                   (void*)g);
   2787             } else {
   2788               log_error(get_device(), "Geometry '%s' overlapping.\n",
   2789                   str_cget(&g->name));
   2790             }
   2791           }
   2792         }
   2793         err_cpt++;
   2794       }
   2795       if(ov) {
   2796         res = RES_BAD_ARG;
   2797         goto error;
   2798       }
   2799     }
   2800   }
   2801 
   2802   /* Create htables (mappings are used to ease access) */
   2803   htable_mappings_init(allocator, &m2);
   2804   htable_mappings_init(allocator, &m3);
   2805   htable_tags_init(allocator, &t2);
   2806   htable_tags_init(allocator, &t3);
   2807   ht_initialized = 1;
   2808   for(i = 0; i < sz; i += 2) {
   2809     int dim = data[i];
   2810     int tag = data[i+1];
   2811     size_t mapping = i/2;
   2812     struct htable_mappings* mn = (dim == 2) ? &m2 : &m3;
   2813     ASSERT(dim == 2 || dim == 3);
   2814     ERR(htable_mappings_set(mn, &tag, &mapping));
   2815   }
   2816 
   2817   for(i = 0; i < geometries_count; i++) {
   2818     struct scad_geometry* geom = geometries[i];
   2819     size_t c, n, j;
   2820     int* dt = NULL;
   2821     /* For each tag in geometries[i] out_geometries[i] includes the mapped tags.
   2822      * Because of the overlapping case, the resulting tags need to be
   2823      * deduplicated though. */
   2824     htable_tags_clear(&t2);
   2825     htable_tags_clear(&t3);
   2826     for(j = 0; j < geom->gmsh_dimTags_n; j += 2) {
   2827       int dim = geom->gmsh_dimTags[j];
   2828       int tag = geom->gmsh_dimTags[j+1];
   2829       struct htable_mappings* mn = (dim == 2) ? &m2 : &m3;
   2830       size_t k;
   2831       size_t* mapping = htable_mappings_find(mn, &tag);
   2832       ASSERT(dim == 2 || dim == 3);
   2833       ASSERT(mapping && *mapping < mapnn);
   2834       for(k = 0; k < mapn[*mapping]; k += 2) {
   2835         char one = 1;
   2836         int d = map[*mapping][k];
   2837         int t = map[*mapping][k+1];
   2838         struct htable_tags* tn = (d == 2) ? &t2 : &t3;
   2839         ERR(htable_tags_set(tn, &t, &one));
   2840       }
   2841     }
   2842     /* Allocate result */
   2843     n = htable_tags_size_get(&t2) + htable_tags_size_get(&t3);
   2844     dt = MEM_ALLOC(allocator, sizeof(*dt) * 2 * n);
   2845     if(!dt) {
   2846       res = RES_MEM_ERR;
   2847       goto error;
   2848     }
   2849     /* Copy tags */
   2850     c = 0;
   2851     htable_tags_begin(&t2, &it);
   2852     htable_tags_end(&t2, &end);
   2853     while(!htable_tags_iterator_eq(&it, &end)) {
   2854       dt[c++] = 2;
   2855       dt[c++] = *htable_tags_iterator_key_get(&it);
   2856       htable_tags_iterator_next(&it);
   2857     }
   2858     htable_tags_begin(&t3, &it);
   2859     htable_tags_end(&t3, &end);
   2860     while(!htable_tags_iterator_eq(&it, &end)) {
   2861       dt[c++] = 3;
   2862       dt[c++] = *htable_tags_iterator_key_get(&it);
   2863       htable_tags_iterator_next(&it);
   2864     }
   2865     ASSERT(c == 2*n);
   2866 
   2867     /* Create geometry */
   2868     ERR(geometry_create(geoms+i));
   2869     geoms[i]->gmsh_dimTags_n = c;
   2870     geoms[i]->gmsh_dimTags = dt;
   2871     ERR(device_register_tags(geoms[i]));
   2872   }
   2873   memcpy(out_geometries, geoms, geometries_count * sizeof(*geoms));
   2874 
   2875 exit:
   2876   gmshFree(mapn);
   2877   free_gmsh_map(map, mapnn);
   2878   if(ht_initialized) {
   2879     htable_mappings_release(&m2);
   2880     htable_mappings_release(&m3);
   2881     htable_tags_release(&t2);
   2882     htable_tags_release(&t3);
   2883   }
   2884   if(tmp_initialized) str_release(&tmp);
   2885   if(allocator) {
   2886     MEM_RM(allocator, data);
   2887     MEM_RM(allocator, geoms);
   2888     MEM_RM(allocator, overlap);
   2889   }
   2890   gmshFree(tagout);
   2891   return res;
   2892 error:
   2893   if(geoms) {
   2894     for(i = 0; i < geometries_count; i++) {
   2895       if(geoms[i]) {
   2896         SCAD(geometry_ref_put(geoms[i]));
   2897       }
   2898     }
   2899   }
   2900   if(tagout) {
   2901      gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
   2902      /* Do not check ierr, first because we are in error handler already, but
   2903       * also because an error is expected if these OCC entities have allready
   2904       * been freed through the call to ref_put above */
   2905   }
   2906   goto exit;
   2907 }
   2908 
   2909 res_T
   2910 scad_geometries_swap
   2911   (struct scad_geometry** pool1,
   2912    struct scad_geometry** pool2,
   2913    const size_t count,
   2914    const int flags)
   2915 {
   2916   res_T res = RES_OK;
   2917   struct scad_device* dev = get_device();
   2918   size_t i;
   2919   struct mem_allocator* allocator = NULL;
   2920   struct str tmp, msg;
   2921 
   2922   if(!pool1 || !pool2 || !flags || count == 0) {
   2923     res = RES_BAD_ARG;
   2924     goto error;
   2925   }
   2926 
   2927   ERR(check_device(FUNC_NAME));
   2928   allocator = dev->allocator;
   2929   if(flags & SCAD_SWAP_NAME) str_init(allocator, &tmp);
   2930   if(flags & SCAD_SWAP_GEOMETRY && dev->log) str_init(allocator, &msg);
   2931   for(i = 0; i < count; i++) {
   2932     struct scad_geometry *g1 = pool1[i], *g2 = pool2[i];
   2933     size_t c1 = g1->gmsh_dimTags_n, c2 = g2->gmsh_dimTags_n;
   2934     int *dt1 = g1->gmsh_dimTags, *dt2 = g2->gmsh_dimTags;
   2935     if(pool1[i] == pool2[i])
   2936       continue;
   2937     /* Swap content according to flags. Don't swap  refcount! */
   2938     if(flags & SCAD_SWAP_NAME) {
   2939       if(dev->log) {
   2940         if(str_is_empty(&g1->name) && str_is_empty(&g2->name)) {
   2941           /* Do nothing */
   2942         }
   2943         else if(str_is_empty(&g1->name)) {
   2944           logger_print(dev->logger, dev->log_type,
   2945               "Swapping names for geometry %p and geometry '%s'.\n",
   2946               (void*)g1, str_cget(&g2->name));
   2947           logger_print(dev->logger, dev->log_type,
   2948               "Geometry '%s' is now unnamed geometry %p.\n",
   2949               str_cget(&g2->name), (void*)g2);
   2950         }
   2951         else if(str_is_empty(&g2->name)) {
   2952           logger_print(dev->logger, dev->log_type,
   2953               "Swapping names for geometry %p and geometry '%s'.\n",
   2954               (void*)g2, str_cget(&g1->name));
   2955           logger_print(dev->logger, dev->log_type,
   2956               "Geometry '%s' is now unnamed geometry %p.\n",
   2957               str_cget(&g1->name), (void*)g1);
   2958         } else { /* Both named */
   2959           logger_print(dev->logger, dev->log_type,
   2960               "Swapping names for geometries '%s' and '%s'.\n",
   2961               str_cget(&g1->name), str_cget(&g1->name));
   2962         }
   2963       }
   2964       if(!str_is_empty(&g1->name)) {
   2965         ERR(htable_names_set(&dev->geometry_names, &g1->name, &g2));
   2966       }
   2967       if(!str_is_empty(&g2->name)) {
   2968         ERR(htable_names_set(&dev->geometry_names, &g2->name, &g1));
   2969       }
   2970       if(!str_is_empty(&g1->name) || !str_is_empty(&g2->name)) {
   2971         ERR(str_copy(&tmp, &g1->name));
   2972         ERR(str_copy(&g1->name, &g2->name));
   2973         ERR(str_copy(&g2->name, &tmp));
   2974       }
   2975     }
   2976     if(flags & SCAD_SWAP_GEOMETRY) {
   2977       /* Swap in tag2geom tables */
   2978       size_t n;
   2979       if(dev->log) {
   2980         if(str_is_empty(&g1->name) && str_is_empty(&g2->name)) {
   2981           logger_print(dev->logger, dev->log_type,
   2982               "Swapping tags for unnamed geometries %p and %p.\n",
   2983               (void*)g1, (void*)g2);
   2984         }
   2985         else if(str_is_empty(&g1->name)) {
   2986           logger_print(dev->logger, dev->log_type,
   2987               "Swapping tags for unnamed geometry %p and geometry '%s'.\n",
   2988               (void*)g1, str_cget(&g2->name));
   2989         }
   2990         else if(str_is_empty(&g2->name)) {
   2991           logger_print(dev->logger, dev->log_type,
   2992               "Swapping tags for unnamed geometry %p and geometry '%s'.\n",
   2993               (void*)g2, str_cget(&g1->name));
   2994         }
   2995         else {
   2996           logger_print(dev->logger, dev->log_type,
   2997               "Swapping tags for geometries '%s' and '%s'.\n",
   2998               str_cget(&g1->name), str_cget(&g1->name));
   2999         }
   3000         if(str_is_empty(&g1->name)) {
   3001           ERR(str_printf(&msg,
   3002                 "Tags now registered against unnamed geometry '%p': ",
   3003                 (void*)g1));
   3004         } else {
   3005           ERR(str_printf(&msg, "Tags now registered against geometry '%s': ",
   3006                 str_cget(&g1->name)));
   3007         }
   3008         for(n = 0; n < c2; n += 2) {
   3009           int dim = dt2[n];
   3010           int tag = dt2[n+1];
   3011           ERR(str_append_printf(&msg, (n ? ", %d.%d" : "%d.%d"), dim, tag));
   3012         }
   3013         logger_print(dev->logger, dev->log_type, "%s.\n", str_cget(&msg));
   3014         if(str_is_empty(&g2->name)) {
   3015           ERR(str_printf(&msg,
   3016                 "Tags now registered against unnamed geometry '%p': ",
   3017                 (void*)g2));
   3018         } else {
   3019           ERR(str_printf(&msg, "Tags now registered against geometry '%s': ",
   3020                 str_cget(&g2->name)));
   3021         }
   3022         for(n = 0; n < c1; n += 2) {
   3023           int dim = dt1[n];
   3024           int tag = dt1[n+1];
   3025           ERR(str_append_printf(&msg, (n ? ", %d.%d" : "%d.%d"), dim, tag));
   3026         }
   3027         logger_print(dev->logger, dev->log_type, "%s.\n", str_cget(&msg));
   3028       }
   3029       /* Swap tags */
   3030       SWAP(int*, g1->gmsh_dimTags, g2->gmsh_dimTags);
   3031       SWAP(size_t, g1->gmsh_dimTags_n, g2->gmsh_dimTags_n);
   3032     }
   3033   }
   3034 
   3035 exit:
   3036   if(flags & SCAD_SWAP_NAME) str_release(&tmp);
   3037   if(flags & SCAD_SWAP_GEOMETRY && dev->log) str_release(&msg);
   3038   return res;
   3039 error:
   3040   goto exit;
   3041 }
   3042 
   3043 res_T
   3044 scad_geometries_boundaries
   3045   (struct scad_geometry** geometries,
   3046    const size_t geometries_count,
   3047    struct scad_geometry*** out_boundaries,
   3048    size_t *out_count)
   3049 {
   3050   res_T res = RES_OK;
   3051   int* tagout = NULL;
   3052   size_t tagoutn, sz;
   3053   int* data = NULL;
   3054   int ierr = 0;
   3055   struct scad_geometry** out_geom = NULL;
   3056   struct mem_allocator* allocator = NULL;
   3057   struct scad_device* dev = get_device();
   3058   int log;
   3059   enum log_type log_type;
   3060   size_t i, c = 0, n;
   3061   struct str msg;
   3062   int msg_initialized = 0;
   3063 
   3064   if(!geometries || geometries_count == 0 || !out_boundaries || !out_count) {
   3065     res = RES_BAD_ARG;
   3066     goto error;
   3067   }
   3068 
   3069   ERR(check_device(FUNC_NAME));
   3070   allocator = dev->allocator;
   3071 
   3072   ERR(gather_tags(geometries, geometries_count, &data, &sz));
   3073   ERR(sync_device());
   3074   gmshModelGetBoundary(data, sz, &tagout, &tagoutn, 1, 0, 0, &ierr);
   3075   ERR(gmsh_err_to_res_T(ierr));
   3076 
   3077   ASSERT(tagoutn % 2 == 0);
   3078   c = tagoutn / 2;
   3079   out_geom = MEM_CALLOC(allocator, c, sizeof(*out_geom));
   3080   if(!out_geom) {
   3081     res = RES_MEM_ERR;
   3082     goto error;
   3083   }
   3084 
   3085   log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL);
   3086   log_type = dev->log_type;
   3087   if(log) {
   3088     str_init(allocator, &msg);
   3089     msg_initialized = 1;
   3090     logger_print(dev->logger, log_type, "Boundary specific tag management:\n");
   3091     ERR(str_printf(&msg, "  tags ["));
   3092     for(i = 0; i < tagoutn; i += 2) {
   3093       const int dim = tagout[i];
   3094       const int tag = tagout[i+1];
   3095       ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
   3096     }
   3097     ERR(str_append_printf(&msg, "] getting a ref to tags ["));
   3098     for(i = 0; i < sz; i += 2) {
   3099       const int dim = data[i];
   3100       const int tag = data[i+1];
   3101       ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
   3102     }
   3103     logger_print(dev->logger, log_type, "%s].\n", str_cget(&msg));
   3104   }
   3105 
   3106   for(i = 0, n = 0; i < tagoutn; i += 2, n++) {
   3107     int dim = tagout[i];
   3108     int tag = tagout[i+1];
   3109     struct tag_desc* desc = NULL;
   3110     ASSERT(dim == 2);
   3111     ERR(geometry_create(out_geom+n));
   3112     out_geom[n]->gmsh_dimTags_n = 2;
   3113     out_geom[n]->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*tagout));
   3114     if(!out_geom[n]->gmsh_dimTags) {
   3115       res = RES_MEM_ERR;
   3116       goto error;
   3117     }
   3118     out_geom[n]->gmsh_dimTags[0] = dim;
   3119     out_geom[n]->gmsh_dimTags[1] = tag;
   3120 
   3121     ERR(device_register_tags(out_geom[n]));
   3122 
   3123     /* Need to protect out_geometry's tags by getting a ref on input tags or
   3124      * deleting input geometry will possibly delete them */
   3125     ERR(device_register_ref_to_tags(dim, tag, data, sz));
   3126     /* As the 2D tags will be deleted when the 3D tag they are part of are
   3127      * deleted, they shouldn't be deleted when the geometry they belongs to are
   3128      * released. */
   3129     desc = device_get_description(dim, tag);
   3130     ASSERT(desc);
   3131     desc->delete_policy = Scad_do_not_delete;
   3132   }
   3133 
   3134 exit:
   3135   if(msg_initialized) str_release(&msg);
   3136   if(allocator) MEM_RM(allocator, data);
   3137   if(out_boundaries) *out_boundaries = out_geom;
   3138   if(out_count) *out_count = c;
   3139   gmshFree(tagout);
   3140   return res;
   3141 error:
   3142   if(out_geom) {
   3143     for(i = 0; i < c; i++) {
   3144       if(out_geom[i]) SCAD(geometry_ref_put(out_geom[i]));
   3145     }
   3146     MEM_RM(allocator, out_geom);
   3147     out_geom = NULL;
   3148     c = 0;
   3149   }
   3150   if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
   3151   goto exit;
   3152 }
   3153 
   3154 
   3155 res_T
   3156 scad_geometries_boundary
   3157   (struct scad_geometry** geometries,
   3158    const size_t geometries_count,
   3159    struct scad_geometry** out_boundary)
   3160 {
   3161   res_T res = RES_OK;
   3162   int* tagout = NULL;
   3163   size_t tagoutn, sz;
   3164   int* data = NULL;
   3165   int ierr = 0;
   3166   struct scad_geometry* out_geom = NULL;
   3167   struct mem_allocator* allocator = NULL;
   3168   struct scad_device* dev = get_device();
   3169   int log;
   3170   enum log_type log_type;
   3171   size_t i;
   3172   struct str msg;
   3173   int msg_initialized = 0;
   3174 
   3175   if(!geometries || geometries_count == 0 || !out_boundary) {
   3176     res = RES_BAD_ARG;
   3177     goto error;
   3178   }
   3179 
   3180   ERR(check_device(FUNC_NAME));
   3181   allocator = dev->allocator;
   3182 
   3183   ERR(gather_tags(geometries, geometries_count, &data, &sz));
   3184   ERR(sync_device());
   3185   gmshModelGetBoundary(data, sz, &tagout, &tagoutn, 1, 0, 0, &ierr);
   3186   ERR(gmsh_err_to_res_T(ierr));
   3187   ASSERT(tagoutn % 2 == 0);
   3188 
   3189   log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL);
   3190   log_type = dev->log_type;
   3191   if(log) {
   3192     str_init(allocator, &msg);
   3193     msg_initialized = 1;
   3194     logger_print(dev->logger, log_type, "Boundary specific tag management:\n");
   3195     ERR(str_printf(&msg, "  tags ["));
   3196     for(i = 0; i < tagoutn; i += 2) {
   3197       const int dim = tagout[i];
   3198       const int tag = tagout[i+1];
   3199       ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
   3200     }
   3201     ERR(str_append_printf(&msg, "] getting a ref to tags ["));
   3202     for(i = 0; i < sz; i += 2) {
   3203       const int dim = data[i];
   3204       const int tag = data[i+1];
   3205       ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
   3206     }
   3207     logger_print(dev->logger, log_type, "%s].\n", str_cget(&msg));
   3208   }
   3209 
   3210   ERR(geometry_create(&out_geom));
   3211   out_geom->gmsh_dimTags_n = tagoutn;
   3212   out_geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout));
   3213   if(!out_geom->gmsh_dimTags) {
   3214     res = RES_MEM_ERR;
   3215     goto error;
   3216   }
   3217   memcpy(out_geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout));
   3218   ERR(device_register_tags(out_geom));
   3219   for(i = 0; i < out_geom->gmsh_dimTags_n; i += 2) {
   3220     const int dim = out_geom->gmsh_dimTags[i];
   3221     const int tag = out_geom->gmsh_dimTags[i+1];
   3222     struct tag_desc* desc = device_get_description(dim, tag);
   3223     ASSERT(dim == 2);
   3224     ASSERT(desc != NULL);
   3225     /* Need to protect input geometry's tags by getting a ref on output tags or
   3226      * deleting out_geometry will possibly delete them */
   3227     ERR(device_register_ref_to_tags(dim, tag, data, sz));
   3228     /* As the 2D tags will be deleted when the 3D tag they are part of are
   3229      * deleted, they shouldn't be deleted when the geometry they belongs to is
   3230      * released. */
   3231     desc->delete_policy = Scad_do_not_delete;
   3232   }
   3233 
   3234 exit:
   3235   if(msg_initialized) str_release(&msg);
   3236   if(allocator) MEM_RM(allocator, data);
   3237   if(out_boundary) *out_boundary = out_geom;
   3238   gmshFree(tagout);
   3239   return res;
   3240 error:
   3241   if(out_geom) {
   3242     SCAD(geometry_ref_put(out_geom));
   3243     out_geom = NULL;
   3244   }
   3245   if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
   3246   goto exit;
   3247 }
   3248 
   3249 res_T
   3250 scad_step_import
   3251   (const char* filename,
   3252    struct scad_geometry*** out_geometry,
   3253    size_t* out_n)
   3254 {
   3255   int ierr;
   3256   int* tagout = NULL;
   3257   size_t tagoutn, i, ga_sz;
   3258   struct str strname;
   3259   int name_initialized = 0;
   3260   struct scad_geometry** geom_array = NULL;
   3261   struct mem_allocator* allocator = NULL;
   3262   struct scad_device* dev = get_device();
   3263   res_T res = RES_OK;
   3264 
   3265   if(!filename || !out_geometry || !out_n) {
   3266     res = RES_BAD_ARG;
   3267     goto error;
   3268   }
   3269 
   3270   ERR(check_device(FUNC_NAME));
   3271   allocator = dev->allocator;
   3272 
   3273   gmshModelOccImportShapes(filename, &tagout, &tagoutn, 1, "step", &ierr);
   3274   ERR(gmsh_err_to_res_T(ierr));
   3275 
   3276   ASSERT(tagoutn % 2 == 0);
   3277   ga_sz = tagoutn / 2;
   3278   allocator = get_device()->allocator;
   3279   geom_array = MEM_CALLOC(allocator, ga_sz, sizeof(*geom_array));
   3280   if(!geom_array) {
   3281     res = RES_MEM_ERR;
   3282     goto error;
   3283   }
   3284 
   3285   str_init(allocator, &strname);
   3286   name_initialized = 1;
   3287   for(i=0; i<ga_sz; ++i) {
   3288     ERR(geometry_create(geom_array+i));
   3289     geom_array[i]->gmsh_dimTags_n = 2;
   3290     geom_array[i]->gmsh_dimTags
   3291       = MEM_ALLOC(allocator, 2 * sizeof(*geom_array[i]->gmsh_dimTags));
   3292     if(!geom_array[i]->gmsh_dimTags) {
   3293       res = RES_MEM_ERR;
   3294       goto error;
   3295     }
   3296     geom_array[i]->gmsh_dimTags[0] = tagout[2*i];
   3297     geom_array[i]->gmsh_dimTags[1] = tagout[2*i+1];
   3298 
   3299     ERR(device_register_tags(geom_array[i]));
   3300   }
   3301 
   3302 exit:
   3303   gmshFree(tagout);
   3304   if(out_n) *out_n = ga_sz ;
   3305   if(out_geometry) *out_geometry = geom_array;
   3306   if(name_initialized) str_release(&strname);
   3307   return res;
   3308 error:
   3309   if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
   3310   ga_sz = 0;
   3311   if(geom_array) {
   3312     for(i=0; i<ga_sz; ++i) {
   3313       if(geom_array[i]) SCAD(geometry_ref_put(geom_array[i]));
   3314     }
   3315     MEM_RM(allocator, geom_array);
   3316     geom_array = NULL;
   3317   }
   3318   goto exit;
   3319 }
   3320 
   3321 res_T
   3322 scad_geometry_get_normal
   3323   (struct scad_geometry* geom,
   3324    const double p[3],
   3325    double N[3],
   3326    struct scad_geometry** out_geometry)
   3327 {
   3328   res_T res = RES_OK;
   3329   int ierr = 0;
   3330   size_t i;
   3331   const int* data = NULL;
   3332   size_t sz = 0;
   3333   struct scad_geometry* out = NULL;
   3334   struct scad_device* dev = get_device();
   3335   int log;
   3336   enum log_type log_type;
   3337   struct mem_allocator* allocator = NULL;
   3338   double* coord = NULL;
   3339   double* pcoord = NULL;
   3340   double* normals = NULL;
   3341   struct darray_int tags;
   3342   int initialized = 0;
   3343   double d, min_d = DBL_MAX, pcoord_min[2];
   3344   int min_tag = -1;
   3345   size_t normals_n;
   3346 
   3347   if(!geom || !p || !N) {
   3348     res = RES_BAD_ARG;
   3349     goto error;
   3350   }
   3351 
   3352   ERR(check_device(FUNC_NAME));
   3353   allocator = dev->allocator;
   3354 
   3355   darray_int_init(dev->allocator, &tags);
   3356   initialized = 1;
   3357 
   3358   ERR(get_2d_tags(geom, &tags));
   3359   data = darray_int_cdata_get(&tags);
   3360   sz = darray_int_size_get(&tags);
   3361 
   3362   log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL);
   3363   log_type = dev->log_type;
   3364 
   3365   /* Find the closest point on tags of geom */
   3366   for(i = 0; i < sz; ++i) {
   3367     size_t pcoord_n;
   3368     size_t coord_n;
   3369     const int dim = 2;
   3370     int tag = data[i];
   3371     double tmp[3];
   3372 
   3373     gmshModelGetClosestPoint(dim, tag, p, 3, &coord, &coord_n, &pcoord,
   3374         &pcoord_n, &ierr);
   3375     ERR(gmsh_err_to_res_T(ierr));
   3376     ASSERT(pcoord_n == (size_t)dim);
   3377     ASSERT(coord_n == 3);
   3378     d = d3_len(d3_sub(tmp, p, coord));
   3379     if(d < min_d) {
   3380       min_d = d;
   3381       min_tag = tag;
   3382       d2_set(pcoord_min, pcoord);
   3383     }
   3384     gmshFree(coord);
   3385     gmshFree(pcoord);
   3386     coord = pcoord = NULL;
   3387   }
   3388   if(min_d == DBL_MAX) { /* At least if sz==0 */
   3389     goto exit;
   3390   }
   3391 
   3392   /* Get the normal at the selected point */
   3393   gmshModelGetNormal(min_tag, pcoord_min, 2, &normals, &normals_n, &ierr);
   3394   ERR(gmsh_err_to_res_T(ierr));
   3395   ASSERT(normals_n == 3);
   3396   d3_set(N, normals);
   3397 
   3398   /* Create a geometry if required */
   3399   if(out_geometry) {
   3400     ERR(geometry_create(&out));
   3401     out->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*out->gmsh_dimTags));
   3402     if(!out->gmsh_dimTags) {
   3403       res = RES_MEM_ERR;
   3404       goto error;
   3405     }
   3406     out->gmsh_dimTags_n = 2;
   3407     out->gmsh_dimTags[0] = 2;
   3408     out->gmsh_dimTags[1] = min_tag;
   3409     ERR(device_register_tags(out));
   3410 
   3411     /* Need to protect geometries' tags or deleting out geometry will possibly
   3412      * delete them */
   3413     if(log) {
   3414       logger_print(dev->logger, log_type,
   3415           "Tag %d.%d getting a reference to other tags.\n", 2, min_tag);
   3416     }
   3417     ERR(device_register_ref_to_tags(2, min_tag, geom->gmsh_dimTags,
   3418           geom->gmsh_dimTags_n));
   3419     if(log) {
   3420       logger_print(dev->logger, log_type,
   3421           "Tag %d.%d getting a reference to other tags done.\n", 2, min_tag);
   3422     }
   3423   }
   3424 
   3425 exit:
   3426   gmshFree(coord);
   3427   gmshFree(pcoord);
   3428   gmshFree(normals);
   3429   if(initialized) darray_int_release(&tags);
   3430   if(out_geometry) *out_geometry = out;
   3431   return res;
   3432 error:
   3433   if(out) {
   3434     SCAD(geometry_ref_put(out));
   3435     out = NULL;
   3436   }
   3437   goto exit;
   3438 }
   3439 
   3440 res_T
   3441 scad_geometry_dilate
   3442   (const struct scad_geometry* geom,
   3443    const double center[3],
   3444    const double scale[3],
   3445    struct scad_geometry** out_geometry)
   3446 {
   3447   res_T res = RES_OK;
   3448   int ierr = 0;
   3449   struct scad_geometry* out = NULL;
   3450 
   3451   if(!geom || !scale|| !center || ! out_geometry) {
   3452     res = RES_BAD_ARG;
   3453     goto error;
   3454   }
   3455 
   3456   ERR(check_device(FUNC_NAME));
   3457 
   3458   ERR(scad_geometry_copy(geom, &out));
   3459 
   3460   gmshModelOccDilate(out->gmsh_dimTags, out->gmsh_dimTags_n, SPLIT3(center),
   3461       SPLIT3(scale), &ierr);
   3462   ERR(gmsh_err_to_res_T(ierr));
   3463 
   3464 exit:
   3465   if(out_geometry) *out_geometry = out;
   3466   return res;
   3467 error:
   3468   if(out) SCAD(geometry_ref_put(out));
   3469   out = NULL;
   3470   goto exit;
   3471 }
   3472 
   3473 res_T
   3474 scad_geometries_set_mesh_size_modifier
   3475   (struct scad_geometry** geometries,
   3476    const size_t geometries_count,
   3477    enum scad_size_modifier_type type,
   3478    double modifier)
   3479 {
   3480   res_T res = RES_OK;
   3481   struct scad_device* dev = get_device();
   3482   int ierr, dim, some = 0;
   3483   int* tagout[4] = { NULL, NULL, NULL, NULL };
   3484   size_t tagoutn[4] = { 0, 0, 0, 0}, i;
   3485 
   3486   if(!geometries || geometries_count == 0 || modifier <= 0
   3487       || (type != SCAD_ABSOLUTE_SIZE && type != SCAD_SIZE_FACTOR))
   3488   {
   3489     res = RES_BAD_ARG;
   3490     goto error;
   3491   }
   3492 
   3493   ERR(check_device(FUNC_NAME));
   3494 
   3495   if(type == SCAD_ABSOLUTE_SIZE) modifier = -modifier;
   3496   ERR(gather_tags_recursive(geometries, geometries_count, 0, tagout, tagoutn));
   3497   for(dim = 0; dim < 4; dim++) {
   3498     for(i = 0; i < tagoutn[dim]; i += 2) {
   3499       int d = tagout[dim][i];
   3500       int tag = tagout[dim][i+1];
   3501       struct htable_size_modifiers* modifiers = dev->size_modifiers_by_dim + dim;
   3502       double* f_ptr = htable_size_modifiers_find(modifiers, &tag);
   3503       ASSERT(d == dim);(void)d;
   3504       if(f_ptr && modifier == 1) { /* Size factor of 1 => no modifier */
   3505         size_t c = htable_size_modifiers_erase(modifiers, &tag);
   3506         ASSERT(c == 1);(void)c;
   3507       } else if(f_ptr) {
   3508         *f_ptr = modifier;
   3509       } else {
   3510         ERR(htable_size_modifiers_set(modifiers, &tag, &modifier));
   3511       }
   3512     }
   3513   }
   3514   for(dim = 0; dim < 4; dim++) {
   3515     if(htable_size_modifiers_size_get(dev->size_modifiers_by_dim + dim) > 0) {
   3516       some = 1;
   3517       break;
   3518     }
   3519   }
   3520 
   3521   gmshModelMeshSetSizeCallback((some ? size_callback : NULL), NULL, &ierr);
   3522   ERR(gmsh_err_to_res_T(ierr));
   3523 
   3524 exit:
   3525   if(dev) {
   3526     for(dim = 0; dim < 4; dim++) {
   3527       MEM_RM(dev->allocator, tagout[dim]);
   3528     }
   3529   }
   3530   return res;
   3531 error:
   3532   goto exit;
   3533 }
   3534 
   3535 res_T
   3536 scad_geometries_set_mesh_algorithm
   3537   (struct scad_geometry** geometries,
   3538    const size_t geometries_count,
   3539    enum scad_mesh_algorithm algorithm)
   3540 {
   3541   res_T res = RES_OK;
   3542   struct scad_device* dev = get_device();
   3543   int ierr;
   3544   int* tagout[4] = { NULL, NULL, NULL, NULL };
   3545   size_t tagoutn[4], i;
   3546 
   3547   if(!geometries || geometries_count == 0 ||
   3548       (algorithm != SCAD_MESHADAPT &&
   3549        algorithm != SCAD_AUTOMATIC &&
   3550        algorithm != SCAD_INITIAL_MESH_ONLY &&
   3551        algorithm != SCAD_DELAUNAY &&
   3552        algorithm != SCAD_FRONTAL_DELAUNAY &&
   3553        algorithm != SCAD_QUASI_STRUCTURED))
   3554   {
   3555     res = RES_BAD_ARG;
   3556     goto error;
   3557   }
   3558 
   3559   ERR(check_device(FUNC_NAME));
   3560 
   3561   ERR(gather_tags_recursive(geometries, geometries_count, 2, tagout, tagoutn));
   3562   for(i = 0; i < tagoutn[2]; i += 2) {
   3563     int dim = tagout[2][i];
   3564     int tag = tagout[2][i+1];
   3565     gmshModelMeshSetAlgorithm(dim, tag, (int)algorithm, &ierr);
   3566     ERR(gmsh_err_to_res_T(ierr));
   3567   }
   3568 
   3569 exit:
   3570   if(dev) {
   3571     for(i = 2; i < 4; i++) {
   3572       MEM_RM(dev->allocator, tagout[i]);
   3573     }
   3574   }
   3575   return res;
   3576 error:
   3577   goto exit;
   3578 }
   3579 
   3580 res_T
   3581 scad_geometries_set_periodic
   3582   (struct scad_geometry** source,
   3583    const size_t source_count,
   3584    struct scad_geometry** target,
   3585    const size_t target_count,
   3586    double affine[16])
   3587 {
   3588   res_T res = RES_OK;
   3589   struct scad_device* dev = get_device();
   3590   struct mem_allocator* allocator = NULL;
   3591   int ierr;
   3592   int* src_dimTagout[4] = { NULL, NULL, NULL, NULL };
   3593   int* tgt_dimTagout[4] = { NULL, NULL, NULL, NULL };
   3594   int* src_tags = NULL, *tgt_tags = NULL;
   3595   size_t src_dimTagoutn[4] = { 0,0,0,0 }, tgt_dimTagoutn[4] = { 0,0,0,0 };
   3596   size_t src_tagsn = 0, tgt_tagsn = 0,  i;
   3597 
   3598   if(!source || source_count == 0 || !target || target_count == 0 || !affine) {
   3599     res = RES_BAD_ARG;
   3600     goto error;
   3601   }
   3602 
   3603   ERR(check_device(FUNC_NAME));
   3604 
   3605   ERR(gather_tags_recursive(source, source_count, 2, src_dimTagout, src_dimTagoutn));
   3606   ERR(gather_tags_recursive(target, target_count, 2, tgt_dimTagout, tgt_dimTagoutn));
   3607   ASSERT(src_dimTagoutn[2] % 2 == 0 && tgt_dimTagoutn[2] % 2 == 0);
   3608   src_tagsn = src_dimTagoutn[2] / 2;
   3609   tgt_tagsn = tgt_dimTagoutn[2] / 2;
   3610   if(src_tagsn == 0 || tgt_tagsn == 0) {
   3611     res = RES_BAD_ARG;
   3612     goto error;
   3613   }
   3614 
   3615   allocator = dev->allocator;
   3616   src_tags = MEM_ALLOC(allocator, src_tagsn * sizeof(*src_tags));
   3617   tgt_tags = MEM_ALLOC(allocator, tgt_tagsn * sizeof(*tgt_tags));
   3618   if(!src_tags || !tgt_tags) {
   3619     res = RES_MEM_ERR;
   3620     goto error;
   3621   }
   3622   for(i = 0; i < src_tagsn; i += 2) {
   3623     src_tags[i] = src_dimTagout[2][i+1];
   3624   }
   3625   for(i = 0; i < tgt_tagsn; i += 2) {
   3626     tgt_tags[i] = tgt_dimTagout[2][i+1];
   3627   }
   3628   gmshModelMeshSetPeriodic(2, tgt_tags, tgt_tagsn, src_tags, src_tagsn,
   3629       affine, 16, &ierr);
   3630   ERR(gmsh_err_to_res_T(ierr));
   3631 
   3632 exit:
   3633   if(dev) {
   3634     for(i = 2; i < 4; i++) {
   3635       MEM_RM(dev->allocator, src_dimTagout[i]);
   3636       MEM_RM(dev->allocator, tgt_dimTagout[i]);
   3637     }
   3638     MEM_RM(dev->allocator, src_tags);
   3639     MEM_RM(dev->allocator, tgt_tags);
   3640   }
   3641   return res;
   3642 error:
   3643   goto exit;
   3644 }