star-geometry-3d

Clean and decorate 3D geometries
git clone git://git.meso-star.fr/star-geometry-3d.git
Log | Files | Refs | README | LICENSE

sg3d_geometry.c (33990B)


      1 /* Copyright (C) 2019, 2020, 2023, 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 "sg3d.h"
     17 #include "sg3d_geometry.h"
     18 #include "sg3d_device.h"
     19 
     20 #include <rsys/double3.h>
     21 
     22 #include <limits.h>
     23 
     24 /******************************************************************************
     25  * Helper functions
     26  *****************************************************************************/
     27 static void
     28 geometry_release(ref_T* ref)
     29 {
     30   struct sg3d_geometry* geom;
     31   struct sg3d_device* dev;
     32   ASSERT(ref);
     33   geom = CONTAINER_OF(ref, struct sg3d_geometry, ref);
     34   dev = geom->dev;
     35   darray_triangle_release(&geom->unique_triangles);
     36   darray_vertex_release(&geom->unique_vertices);
     37   htable_trg_release(&geom->unique_triangles_ids);
     38   htable_vrtx_release(&geom->unique_vertices_ids);
     39   darray_trg_descriptions_release(&geom->trg_descriptions);
     40   MEM_RM(dev->allocator, geom);
     41   SG3D(device_ref_put(dev));
     42 }
     43 
     44 static FINLINE int /* Return 1 if reversed */
     45 trg_make_key(struct vrtx_id3* k, const vrtx_id_t t[3])
     46 {
     47   ASSERT(t);
     48   ASSERT(t[0] != t[1] && t[0] != t[2] && t[1] != t[2]);
     49   if(t[0] < t[2]) {
     50     if(t[0] < t[1]) {
     51       k->x[0] = t[0];
     52       if(t[1] < t[2]) {
     53         k->x[1] = t[1];
     54         k->x[2] = t[2];
     55         return 0;
     56       } else {
     57         k->x[1] = t[2];
     58         k->x[2] = t[1];
     59         return 1;
     60       }
     61     } else {
     62       k->x[0] = t[1];
     63       if(t[0] < t[2]) {
     64         k->x[1] = t[0];
     65         k->x[2] = t[2];
     66         return 1;
     67       } else {
     68         k->x[1] = t[2];
     69         k->x[2] = t[0];
     70         return 0;
     71       }
     72     }
     73   }
     74   else if(t[2] < t[1]) {
     75     k->x[0] = t[2];
     76     if(t[0] < t[1]) {
     77       k->x[1] = t[0];
     78       k->x[2] = t[1];
     79       return 0;
     80     } else {
     81       k->x[1] = t[1];
     82       k->x[2] = t[0];
     83       return 1;
     84     }
     85   } else {
     86     k->x[0] = t[1];
     87     if(t[0] < t[2]) {
     88       k->x[1] = t[0];
     89       k->x[2] = t[2];
     90       return 1;
     91     } else {
     92       k->x[1] = t[2];
     93       k->x[2] = t[0];
     94       return 0;
     95     }
     96   }
     97 }
     98 
     99 static void
    100 dump_trg_property
    101   (const struct sg3d_geometry* geom,
    102    FILE* stream,
    103    const enum sg3d_property_type type)
    104 {
    105   size_t i;
    106   const struct trg_descriptions* descriptions;
    107   ASSERT(geom && stream && type < SG3D_PROP_TYPES_COUNT__);
    108 
    109   descriptions
    110     = darray_trg_descriptions_cdata_get(&geom->trg_descriptions);
    111   FOR_EACH(i, 0, darray_triangle_size_get(&geom->unique_triangles)) {
    112     prop_id_t property = SG3D_UNSPECIFIED_PROPERTY;
    113     size_t tdefs_count
    114       = darray_definition_size_get(&descriptions[i].defs[type]);
    115     if(tdefs_count && descriptions[i].property_defined[type]) {
    116       const struct definition* tdefs
    117         = darray_definition_cdata_get(&descriptions[i].defs[type]);
    118       size_t j;
    119       FOR_EACH(j, 0, tdefs_count) {
    120         if(tdefs->property_value != SG3D_UNSPECIFIED_PROPERTY) {
    121           property = tdefs->property_value;
    122           break; /* Found the defined value */
    123         }
    124         tdefs++; /* Next value */
    125       }
    126     }
    127     /* In VTK dumps UINT_MAX is used for unspecified */
    128     if(property == SG3D_UNSPECIFIED_PROPERTY)
    129       fprintf(stream, "%u\n", UINT_MAX);
    130     else fprintf(stream, "%u\n", (unsigned)property);
    131   }
    132 }
    133 
    134 /******************************************************************************
    135  * Local functions
    136  *****************************************************************************/
    137 res_T
    138 geometry_register_triangle
    139   (struct sg3d_geometry* geom,
    140    const struct triangle* triangle,
    141    const trg_id_t triangle_unique_id,
    142    const unsigned set_id,
    143    const int merge_conflict)
    144 {
    145   res_T res = RES_OK;
    146   struct trg_descriptions* trg_d;
    147   int i;
    148   char keep_prop_def[SG3D_PROP_TYPES_COUNT__];
    149 
    150   ASSERT(geom && triangle);
    151 
    152   ERR(geometry_enlarge_trg_descriptions(geom, triangle_unique_id + 1));
    153   trg_d = (darray_trg_descriptions_data_get(&geom->trg_descriptions)
    154     + triangle_unique_id);
    155   /* Record information */
    156   FOR_EACH(i, 0, SG3D_PROP_TYPES_COUNT__) {
    157     struct darray_definition* definitions;
    158     struct definition* defs;
    159     int done = 0;
    160     size_t j;
    161     keep_prop_def[i] = trg_d->property_defined[i];
    162     if(triangle->properties[i] == SG3D_UNSPECIFIED_PROPERTY)
    163       trg_d->defs_include_unspecified = 1;
    164     else trg_d->property_defined[i] = 1;
    165     definitions = trg_d->defs + i;
    166     defs = darray_definition_data_get(definitions);
    167     FOR_EACH(j, 0, darray_definition_size_get(definitions)) {
    168       if(defs[j].property_value == triangle->properties[i]) {
    169         /* This property_value is already registered: no conflict */
    170         const unsigned* ids = darray_uint_cdata_get(&defs[j].set_ids);
    171         size_t k;
    172         /* Search if property_value already includes set_id */
    173         FOR_EACH(k, 0, darray_uint_size_get(&defs[j].set_ids)) {
    174           if(ids[k] == set_id) {
    175             /* Same value+set_id was there already */
    176             done = 1;
    177             break;
    178           }
    179         }
    180         if(!done) {
    181           /* Need to add the set_id for this property_value */
    182           ERR(darray_uint_push_back(&defs[j].set_ids, &set_id));
    183           done = 1;
    184         }
    185         break;
    186       }
    187     }
    188     if(!done) {
    189       /* This property_value was not recorded already */
    190       size_t defs_sz = darray_definition_size_get(definitions);
    191       struct definition* new_def;
    192       ERR(darray_definition_resize(definitions, 1 + defs_sz));
    193       new_def = darray_definition_data_get(definitions) + defs_sz;
    194       ERR(darray_uint_push_back(&new_def->set_ids, &set_id));
    195       new_def->property_value = triangle->properties[i];
    196       if(!trg_d->merge_conflict && merge_conflict) {
    197         /* If more than 1 merge_conflict occur, the first one remains */
    198         trg_d->merge_conflict = merge_conflict;
    199         geom->merge_conflict_count++;
    200       }
    201     }
    202   }
    203 
    204   if((!keep_prop_def[SG3D_FRONT] || !keep_prop_def[SG3D_BACK])
    205     && trg_d->property_defined[SG3D_FRONT] && trg_d->property_defined[SG3D_BACK])
    206   {
    207     /* Both sides are now defined */
    208     ASSERT(geom->trg_with_unspecified_sides_count > 0);
    209     geom->trg_with_unspecified_sides_count--;
    210   }
    211 
    212   if(!keep_prop_def[SG3D_INTFACE] && trg_d->property_defined[SG3D_INTFACE]) {
    213     /* Interface is now defined */
    214     ASSERT(geom->trg_with_unspecified_intface_count > 0);
    215     geom->trg_with_unspecified_intface_count--;
    216   }
    217 
    218 exit:
    219   return res;
    220 error:
    221   goto exit;
    222 }
    223 
    224 res_T
    225 geometry_enlarge_trg_descriptions
    226   (struct sg3d_geometry* geom,
    227    const trg_id_t sz)
    228 {
    229   res_T res = RES_OK;
    230   size_t old_sz =
    231     darray_trg_descriptions_size_get(&geom->trg_descriptions);
    232   if(sz <= old_sz) return RES_OK;
    233   ERR(darray_trg_descriptions_resize(&geom->trg_descriptions, sz));
    234   ASSERT(geom->trg_with_unspecified_sides_count + sz - old_sz <= TRG_MAX__);
    235   ASSERT(geom->trg_with_unspecified_intface_count + sz - old_sz <= TRG_MAX__);
    236   geom->trg_with_unspecified_sides_count += (trg_id_t)(sz - old_sz);
    237   geom->trg_with_unspecified_intface_count += (trg_id_t)(sz - old_sz);
    238 
    239 exit:
    240   return res;
    241 error:
    242   goto exit;
    243 }
    244 
    245 static void
    246 dump_partition
    247   (const struct sg3d_geometry* geom,
    248    FILE* stream,
    249    const char* group_name,
    250    enum sg3d_obj_dump_content partition)
    251 {
    252   const struct trg_descriptions* trg_descriptions;
    253   const struct triangle* triangles;
    254   size_t sz, i;
    255   ASSERT(geom && stream && group_name);
    256   ASSERT(partition == SG3D_OBJ_DUMP_MERGE_CONFLICTS
    257     || partition == SG3D_OBJ_DUMP_PROPERTY_CONFLICTS
    258     || partition == SG3D_OBJ_DUMP_VALID_PRIMITIVE);
    259   trg_descriptions
    260     = darray_trg_descriptions_cdata_get(&geom->trg_descriptions);
    261   sz = darray_trg_descriptions_size_get(&geom->trg_descriptions);
    262   triangles = darray_triangle_cdata_get(&geom->unique_triangles);
    263   fprintf(stream, "g %s\n", group_name);
    264   FOR_EACH(i, 0, sz) {
    265     int dump;
    266     if(partition == SG3D_OBJ_DUMP_VALID_PRIMITIVE)
    267       dump = !(trg_descriptions[i].merge_conflict
    268         || trg_descriptions[i].properties_conflict);
    269     else if(partition == SG3D_OBJ_DUMP_MERGE_CONFLICTS)
    270       dump = trg_descriptions[i].merge_conflict;
    271     else {
    272       ASSERT(partition == SG3D_OBJ_DUMP_PROPERTY_CONFLICTS);
    273       dump = trg_descriptions[i].properties_conflict;
    274     }
    275     if(!dump) continue;
    276     fprintf(stream, "f "PRTF_VRTX" "PRTF_VRTX" "PRTF_VRTX"\n",
    277       /* OBJ indexing starts at 1 */
    278       1 + triangles[i].vertex_ids[0],
    279       1 + triangles[i].vertex_ids[1],
    280       1 + triangles[i].vertex_ids[2]);
    281   }
    282 }
    283 
    284 /******************************************************************************
    285  * Exported functions
    286  *****************************************************************************/
    287 res_T
    288 sg3d_geometry_create
    289   (struct sg3d_device* dev,
    290    struct sg3d_geometry** out_geometry)
    291 {
    292   struct sg3d_geometry* geom = NULL;
    293   res_T res = RES_OK;
    294 
    295   if(!dev || !out_geometry) {
    296     res = RES_BAD_ARG;
    297     goto error;
    298   }
    299 
    300   geom = MEM_CALLOC(dev->allocator, 1, sizeof(struct sg3d_geometry));
    301   if(!geom) {
    302     log_err(dev,
    303         LIB_NAME":%s: could not allocate the sg3d.\n", FUNC_NAME);
    304     res = RES_MEM_ERR;
    305     goto error;
    306   }
    307 
    308   SG3D(device_ref_get(dev));
    309   darray_triangle_init(dev->allocator, &geom->unique_triangles);
    310   darray_vertex_init(dev->allocator, &geom->unique_vertices);
    311   htable_trg_init(dev->allocator, &geom->unique_triangles_ids);
    312   htable_vrtx_init(dev->allocator, &geom->unique_vertices_ids);
    313   darray_trg_descriptions_init(dev->allocator, &geom->trg_descriptions);
    314   geom->triangle_count_including_duplicates = 0;
    315   geom->sides_with_defined_medium_count = 0;
    316   geom->set_id = 0;
    317   geom->trg_with_unspecified_sides_count = 0;
    318   geom->trg_with_unspecified_intface_count = 0;
    319   geom->merge_conflict_count = 0;
    320   geom->properties_conflict_count = 0;
    321   geom->dev = dev;
    322 
    323   ref_init(&geom->ref);
    324 
    325 exit:
    326   if(out_geometry) *out_geometry = geom;
    327   return res;
    328 error:
    329   if(geom) {
    330     SG3D(geometry_ref_put(geom));
    331     geom = NULL;
    332   }
    333   goto exit;
    334 }
    335 
    336 res_T
    337 sg3d_geometry_reserve
    338   (struct sg3d_geometry* geom,
    339    const vrtx_id_t vertices_count,
    340    const trg_id_t triangles_count,
    341    const prop_id_t properties_count)
    342 {
    343   res_T res = RES_OK;
    344   if(!geom) return RES_BAD_ARG;
    345 
    346   ERR(darray_triangle_reserve(&geom->unique_triangles, triangles_count));
    347   ERR(darray_vertex_reserve(&geom->unique_vertices, vertices_count));
    348   ERR(htable_vrtx_reserve(&geom->unique_vertices_ids, vertices_count));
    349   ERR(htable_trg_reserve(&geom->unique_triangles_ids, triangles_count));
    350   ERR(darray_trg_descriptions_reserve(&geom->trg_descriptions,
    351     properties_count));
    352 
    353 end:
    354   return res;
    355 error:
    356   goto end;
    357 }
    358 
    359 res_T
    360 sg3d_geometry_add
    361   (struct sg3d_geometry* geom,
    362    const vrtx_id_t nverts,
    363    const trg_id_t ntris,
    364    const struct sg3d_geometry_add_callbacks* callbacks,
    365    void* ctx) /* Can be NULL */
    366 {
    367   res_T res = RES_OK;
    368   struct mem_allocator* alloc;
    369   size_t nutris, nuverts;
    370   vrtx_id_t nv, n_new_uverts = 0;
    371   trg_id_t nt, n_new_utris = 0;
    372   struct triangle* trg;
    373   /* Tmp table of IDs to record unique IDs of the currently added vertices */
    374   struct darray_vertice_ids unique_vertice_ids;
    375   int unique_vertice_ids_initialized = 0;
    376   get_indices_t get_ind;
    377   get_properties_t get_prop;
    378   get_position_t get_pos;
    379   add_triangle_t add_trg;
    380   merge_triangle_t mrg_trg;
    381   degenerated_triangle_t dege_trg;
    382 
    383   if(!geom || !callbacks || !callbacks->get_indices || !callbacks->get_position)
    384   {
    385     res = RES_BAD_ARG;
    386     goto error;
    387   }
    388 
    389   get_ind = callbacks->get_indices;
    390   get_prop = callbacks->get_properties;
    391   get_pos = callbacks->get_position;
    392   add_trg = callbacks->add_triangle;
    393   mrg_trg = callbacks->merge_triangle;
    394   dege_trg = callbacks->degenerated_triangle;
    395   alloc = geom->dev->allocator;
    396   nuverts = darray_vertex_size_get(&geom->unique_vertices);
    397   nutris = darray_triangle_size_get(&geom->unique_triangles);
    398 
    399   /* Make room for new geometry; suppose no more duplicates */
    400   darray_vertice_ids_init(alloc, &unique_vertice_ids);
    401   unique_vertice_ids_initialized = 1;
    402   ERR(darray_vertice_ids_reserve(&unique_vertice_ids, nverts));
    403   ERR(darray_vertex_reserve(&geom->unique_vertices, nuverts + nverts));
    404   ERR(darray_triangle_reserve(&geom->unique_triangles, nutris + ntris));
    405   ERR(htable_vrtx_reserve(&geom->unique_vertices_ids, nuverts + nverts));
    406   ERR(htable_trg_reserve(&geom->unique_triangles_ids, nutris + ntris));
    407   ASSERT(nutris == darray_trg_descriptions_size_get(&geom->trg_descriptions));
    408   ERR(darray_trg_descriptions_reserve(&geom->trg_descriptions, nutris + ntris));
    409   /* Get vertices and deduplicate */
    410   FOR_EACH(nv, 0, nverts) {
    411     vrtx_id_t* p_vrtx;
    412     struct vertex tmp;
    413     vrtx_id_t v_idx;
    414     get_pos(nv, tmp.coord, ctx);
    415     p_vrtx = htable_vrtx_find(&geom->unique_vertices_ids, &tmp);
    416     if(p_vrtx) {
    417       /* Duplicate vertex */
    418       v_idx = *p_vrtx;
    419     } else {
    420       /* New vertex */
    421       ASSERT(nuverts + n_new_uverts <= VRTX_MAX__);
    422       v_idx = (vrtx_id_t)(nuverts + n_new_uverts);
    423       ASSERT(v_idx == htable_vrtx_size_get(&geom->unique_vertices_ids));
    424       ERR(darray_vertex_push_back(&geom->unique_vertices, &tmp));
    425       ERR(htable_vrtx_set(&geom->unique_vertices_ids, &tmp, &v_idx));
    426       ++n_new_uverts;
    427     }
    428     /* Keep the unique ID for vertex nv */
    429     ERR(darray_vertice_ids_push_back(&unique_vertice_ids, &v_idx));
    430   }
    431 
    432   /* Get triangles and deduplicate */
    433   trg = darray_triangle_data_get(&geom->unique_triangles);
    434   FOR_EACH(nt, 0, ntris) {
    435     int j, reversed, dg;
    436     struct vrtx_id3 trg_key;
    437     struct triangle tmp = TRG_UNDEF__;
    438     trg_id_t* p_trg;
    439     struct trg_descriptions* trg_descriptions = NULL;
    440     trg_id_t unique_id;
    441 
    442     get_ind(nt, tmp.vertex_ids, ctx);
    443     FOR_EACH(j, 0, 3) {
    444       if(tmp.vertex_ids[j] >= nverts) {
    445         res = RES_BAD_ARG;
    446         goto error;
    447       }
    448       /* Replace the vertex ID by its the unique ID */
    449       tmp.vertex_ids[j]
    450         = darray_vertice_ids_cdata_get(&unique_vertice_ids)[tmp.vertex_ids[j]];
    451     }
    452     dg = (tmp.vertex_ids[0] == tmp.vertex_ids[1]
    453       || tmp.vertex_ids[0] == tmp.vertex_ids[2]
    454       || tmp.vertex_ids[1] == tmp.vertex_ids[2]);
    455     if(!dg) {
    456       double edge1[3], edge2[3], n[3];
    457       const struct vertex* vertices = darray_vertex_cdata_get(&geom->unique_vertices);
    458       d3_sub(edge1, vertices[tmp.vertex_ids[1]].coord, vertices[tmp.vertex_ids[0]].coord);
    459       d3_sub(edge2, vertices[tmp.vertex_ids[2]].coord, vertices[tmp.vertex_ids[0]].coord);
    460       d3_cross(n, edge1, edge2);
    461       dg = d3_len(n) == 0;
    462     }
    463     if(dg) {
    464       int abort = 0;
    465       if(dege_trg) {
    466         /* Let the client app rule. */
    467         ERR(dege_trg(nt, ctx, &abort));
    468       } else {
    469         log_warn(geom->dev,
    470           LIB_NAME":%s: triangle "PRTF_TRG" is degenerated (removed).\n",
    471           FUNC_NAME, nt);
    472       }
    473       if(abort) {
    474         res = RES_BAD_ARG;
    475         goto error;
    476       }
    477       else continue;
    478     }
    479     /* Get properties */
    480     if(get_prop) get_prop(nt, tmp.properties, ctx);
    481     /* Find duplicate triangles */
    482     reversed = trg_make_key(&trg_key, tmp.vertex_ids);
    483     p_trg = htable_trg_find(&geom->unique_triangles_ids, &trg_key);
    484     if(p_trg) {
    485       /* Duplicate triangle. Need to check duplicate validity */
    486       trg_id_t trg_id = *p_trg;
    487       struct vrtx_id3 utrg_key;
    488       int ureversed = trg_make_key(&utrg_key, trg[trg_id].vertex_ids);
    489       int same = (reversed == ureversed);
    490       int already_conflict;
    491       ASSERT(trg_key_eq(&trg_key, &utrg_key));
    492       unique_id = trg_id;
    493       ERR(geometry_enlarge_trg_descriptions(geom, 1 + trg_id));
    494       trg_descriptions
    495         = darray_trg_descriptions_data_get(&geom->trg_descriptions);
    496       if(!same)
    497         SWAP(prop_id_t, tmp.properties[SG3D_FRONT], tmp.properties[SG3D_BACK]);
    498       already_conflict = trg_descriptions[trg_id].merge_conflict;
    499       if(mrg_trg) {
    500         /* Let the client app rule. */
    501         ERR(mrg_trg(trg_id, nt, !same, trg[trg_id].properties,
    502           tmp.properties, ctx, &trg_descriptions[trg_id].merge_conflict));
    503       } else {
    504         FOR_EACH(j, 0, SG3D_PROP_TYPES_COUNT__) {
    505           if(!sg3d_compatible_property(trg[trg_id].properties[j],
    506             tmp.properties[j]))
    507           {
    508             trg_descriptions[trg_id].merge_conflict = 1;
    509             break;
    510           }
    511         }
    512       }
    513       if(trg_descriptions[trg_id].merge_conflict && !already_conflict)
    514         geom->merge_conflict_count++;
    515       /* Replace SG3D_UNSPECIFIED_PROPERTY properties */
    516       FOR_EACH(j, 0, SG3D_PROP_TYPES_COUNT__) {
    517         if(trg[trg_id].properties[j] == SG3D_UNSPECIFIED_PROPERTY
    518           && tmp.properties[j] != SG3D_UNSPECIFIED_PROPERTY) {
    519           trg[trg_id].properties[j] = tmp.properties[j];
    520           if(j == SG3D_FRONT || j == SG3D_BACK)
    521             geom->sides_with_defined_medium_count++;
    522         }
    523       }
    524     } else {
    525       /* New triangle */
    526       trg_id_t new_id = (trg_id_t)(nutris + n_new_utris);
    527       ASSERT(new_id <= TRG_MAX__);
    528       unique_id = new_id;
    529       tmp.user_id = geom->triangle_count_including_duplicates + nt;
    530       if(add_trg) ERR(add_trg(new_id, nt, ctx));
    531       ERR(geometry_enlarge_trg_descriptions(geom, 1 + new_id));
    532       trg_descriptions
    533         = darray_trg_descriptions_data_get(&geom->trg_descriptions);
    534       ERR(darray_triangle_push_back(&geom->unique_triangles, &tmp));
    535       trg = darray_triangle_data_get(&geom->unique_triangles);
    536       FOR_EACH(j, 0, SG3D_PROP_TYPES_COUNT__) {
    537         if((j == SG3D_FRONT || j == SG3D_BACK)
    538           && tmp.properties[j] != SG3D_UNSPECIFIED_PROPERTY)
    539           geom->sides_with_defined_medium_count++;
    540       }
    541       ASSERT(new_id == htable_trg_size_get(&geom->unique_triangles_ids));
    542       ERR(htable_trg_set(&geom->unique_triangles_ids, &trg_key, &new_id));
    543       n_new_utris++;
    544     }
    545     ERR(geometry_register_triangle(geom, &tmp, unique_id, geom->set_id,
    546       trg_descriptions[unique_id].properties_conflict));
    547     if(trg_descriptions[unique_id].properties_conflict)
    548       geom->merge_conflict_count++;
    549   }
    550 
    551   ASSERT(nuverts + n_new_uverts
    552     == htable_vrtx_size_get(&geom->unique_vertices_ids));
    553   ASSERT(nutris + n_new_utris
    554     == htable_trg_size_get(&geom->unique_triangles_ids));
    555 exit:
    556   if(geom) {
    557     geom->set_id++;
    558     geom->triangle_count_including_duplicates += ntris;
    559   }
    560   if(unique_vertice_ids_initialized)
    561     darray_vertice_ids_release(&unique_vertice_ids);
    562   return res;
    563 error:
    564   goto exit;
    565 }
    566 
    567 res_T
    568 sg3d_geometry_validate_properties
    569   (struct sg3d_geometry* geom,
    570    res_T(*validate)(const trg_id_t, const prop_id_t*, void*, int*),
    571    void* ctx)
    572 {
    573   size_t sz__;
    574   trg_id_t i, sz;
    575   struct trg_descriptions* trg_descriptions;
    576   res_T res = RES_OK;
    577 
    578   if(!geom || !validate) {
    579     res = RES_BAD_ARG;
    580     goto error;
    581   }
    582 
    583   sz__ = darray_trg_descriptions_size_get(&geom->trg_descriptions);
    584   ASSERT(sz__ <= PROP_MAX__);
    585   sz = (unsigned)sz__;
    586   trg_descriptions
    587     = darray_trg_descriptions_data_get(&geom->trg_descriptions);
    588   geom->properties_conflict_count = 0; /* Reset count */
    589   FOR_EACH(i, 0, sz) {
    590     int p;
    591     prop_id_t props[SG3D_PROP_TYPES_COUNT__];
    592     struct trg_descriptions* trgd = trg_descriptions + i;
    593     /* Validate only triangle not flagged with merge_conflict */
    594     if(trgd->merge_conflict) {
    595       trgd->properties_conflict = 0;
    596       continue;
    597     }
    598     /* Get properties for non-conflict triangles */
    599     FOR_EACH(p, 0, SG3D_PROP_TYPES_COUNT__) {
    600       size_t j;
    601       const struct definition* defs = darray_definition_cdata_get(trgd->defs + p);
    602       props[p] = SG3D_UNSPECIFIED_PROPERTY;
    603       FOR_EACH(j, 0, darray_definition_size_get(trgd->defs + p)) {
    604         if(defs[j].property_value != SG3D_UNSPECIFIED_PROPERTY) {
    605           props[p] = defs[j].property_value;
    606           break;
    607         }
    608       }
    609     }
    610     /* Call validation */
    611     ERR(validate(i, props, ctx, &trgd->properties_conflict));
    612     if(trgd->properties_conflict)
    613       geom->properties_conflict_count++;
    614   }
    615 
    616 exit:
    617   return res;
    618 error:
    619   goto exit;
    620 }
    621 
    622 res_T
    623 sg3d_geometry_get_unique_vertices_count
    624   (const struct sg3d_geometry* geom,
    625    vrtx_id_t* count)
    626 {
    627   res_T res = RES_OK;
    628   size_t sz;
    629   if(!geom || !count) {
    630     res = RES_BAD_ARG;
    631     goto error;
    632   }
    633   sz = darray_vertex_size_get(&geom->unique_vertices);
    634   ASSERT(sz <= VRTX_MAX__);
    635   *count = (vrtx_id_t)sz;
    636 exit:
    637   return res;
    638 error:
    639   goto exit;
    640 }
    641 
    642 res_T
    643 sg3d_geometry_get_unique_vertex
    644   (const struct sg3d_geometry* geom,
    645    const vrtx_id_t ivtx,
    646    double coord[SG3D_GEOMETRY_DIMENSION])
    647 {
    648   res_T res = RES_OK;
    649   const struct vertex* vertices;
    650   if(!geom || !coord
    651     || ivtx >= darray_vertex_size_get(&geom->unique_vertices))
    652   {
    653     res = RES_BAD_ARG;
    654     goto error;
    655   }
    656   vertices = darray_vertex_cdata_get(&geom->unique_vertices);
    657   d3_set(coord, vertices[ivtx].coord);
    658 exit:
    659   return res;
    660 error:
    661   goto exit;
    662 }
    663 
    664 res_T
    665 sg3d_geometry_get_added_triangles_count
    666   (const struct sg3d_geometry* geom,
    667    trg_id_t* count)
    668 {
    669   res_T res = RES_OK;
    670   if(!geom || !count) {
    671     res = RES_BAD_ARG;
    672     goto error;
    673   }
    674   *count = geom->triangle_count_including_duplicates;
    675 exit:
    676   return res;
    677 error:
    678   goto exit;
    679 }
    680 
    681 res_T
    682 sg3d_geometry_get_unique_triangles_count
    683   (const struct sg3d_geometry* geom,
    684    trg_id_t* count)
    685 {
    686   res_T res = RES_OK;
    687   size_t sz;
    688   if(!geom || !count) {
    689     res = RES_BAD_ARG;
    690     goto error;
    691   }
    692   sz = darray_triangle_size_get(&geom->unique_triangles);
    693   ASSERT(sz <= TRG_MAX__);
    694   *count = (unsigned)sz;
    695 exit:
    696   return res;
    697 error:
    698   goto exit;
    699 }
    700 
    701 res_T
    702 sg3d_geometry_get_unique_triangle_vertices
    703   (const struct sg3d_geometry* geom,
    704    const trg_id_t itri,
    705    vrtx_id_t indices[SG3D_GEOMETRY_DIMENSION])
    706 {
    707   res_T res = RES_OK;
    708   const struct triangle* triangles;
    709   size_t i;
    710   if(!geom || !indices
    711     || itri >= darray_triangle_size_get(&geom->unique_triangles))
    712   {
    713     res = RES_BAD_ARG;
    714     goto error;
    715   }
    716   triangles = darray_triangle_cdata_get(&geom->unique_triangles);
    717   FOR_EACH(i, 0, SG3D_GEOMETRY_DIMENSION)
    718     indices[i] = triangles[itri].vertex_ids[i];
    719 exit:
    720   return res;
    721 error:
    722   goto exit;
    723 }
    724 
    725 res_T
    726 sg3d_geometry_get_unique_triangle_properties
    727   (const struct sg3d_geometry* geom,
    728    const trg_id_t itri,
    729    prop_id_t properties[SG3D_PROP_TYPES_COUNT__])
    730 {
    731   res_T res = RES_OK;
    732   const struct triangle* triangles;
    733   size_t i;
    734   if(!geom || !properties
    735     || itri >= darray_triangle_size_get(&geom->unique_triangles))
    736   {
    737     res = RES_BAD_ARG;
    738     goto error;
    739   }
    740   triangles = darray_triangle_cdata_get(&geom->unique_triangles);
    741   FOR_EACH(i, 0, SG3D_PROP_TYPES_COUNT__)
    742     properties[i] = triangles[itri].properties[i];
    743 exit:
    744   return res;
    745 error:
    746   goto exit;
    747 }
    748 
    749 res_T
    750 sg3d_geometry_get_unique_triangle_user_id
    751   (const struct sg3d_geometry* geom,
    752    const trg_id_t itri,
    753    trg_id_t* user_id)
    754 {
    755   res_T res = RES_OK;
    756   const struct triangle* triangles;
    757   if(!geom || !user_id
    758     || itri >= darray_triangle_size_get(&geom->unique_triangles))
    759   {
    760     res = RES_BAD_ARG;
    761     goto error;
    762   }
    763   triangles = darray_triangle_cdata_get(&geom->unique_triangles);
    764   *user_id = triangles[itri].user_id;
    765 exit:
    766   return res;
    767 error:
    768   goto exit;
    769 }
    770 
    771 res_T
    772 sg3d_geometry_get_unique_triangles_with_unspecified_side_count
    773   (const struct sg3d_geometry* geom,
    774    trg_id_t* count)
    775 {
    776   res_T res = RES_OK;
    777   if(!geom || !count) {
    778     res = RES_BAD_ARG;
    779     goto error;
    780   }
    781   *count = geom->trg_with_unspecified_sides_count;
    782 exit:
    783   return res;
    784 error:
    785   goto exit;
    786 }
    787 
    788 res_T
    789 sg3d_geometry_get_unique_triangles_with_unspecified_interface_count
    790   (const struct sg3d_geometry* geom,
    791    trg_id_t* count)
    792 {
    793   res_T res = RES_OK;
    794   if(!geom || !count) {
    795     res = RES_BAD_ARG;
    796     goto error;
    797   }
    798   *count = geom->trg_with_unspecified_intface_count;
    799 exit:
    800   return res;
    801 error:
    802   goto exit;
    803 }
    804 
    805 res_T
    806 sg3d_geometry_get_unique_triangles_with_merge_conflict_count
    807   (const struct sg3d_geometry* geom,
    808    trg_id_t* count)
    809 {
    810   res_T res = RES_OK;
    811   if(!geom || !count) {
    812     res = RES_BAD_ARG;
    813     goto error;
    814   }
    815   *count = geom->merge_conflict_count;
    816 exit:
    817   return res;
    818 error:
    819   goto exit;
    820 }
    821 
    822 res_T
    823 sg3d_geometry_get_unique_triangles_with_properties_conflict_count
    824   (const struct sg3d_geometry* geom,
    825    trg_id_t* count)
    826 {
    827   res_T res = RES_OK;
    828   if(!geom || !count) {
    829     res = RES_BAD_ARG;
    830     goto error;
    831   }
    832   *count = geom->properties_conflict_count;
    833 exit:
    834   return res;
    835 error:
    836   goto exit;
    837 }
    838 
    839 res_T
    840 sg3d_geometry_dump_as_obj
    841   (const struct sg3d_geometry* geom,
    842    FILE* stream,
    843    int flags)
    844 {
    845   res_T res = RES_OK;
    846   const struct vertex* vertices;
    847   size_t vsz, tsz, i;
    848   if(!geom || !stream || !flags
    849     || !geom->triangle_count_including_duplicates)
    850   {
    851     if(geom && !geom->triangle_count_including_duplicates)
    852       log_err(geom->dev,
    853         LIB_NAME":%s: cannot dump empty geometries as OBJ\n",
    854         FUNC_NAME);
    855     res = RES_BAD_ARG;
    856     goto error;
    857   }
    858   /* Headers */
    859   fprintf(stream, "# Dump of star-geometry-3d\n");
    860   fprintf(stream, "# Geometry counts:\n");
    861   vsz = darray_vertex_size_get(&geom->unique_vertices);
    862   ASSERT(vsz <= VRTX_MAX__);
    863   fprintf(stream, "# . "PRTF_VRTX" vertices\n", (vrtx_id_t)vsz);
    864   tsz = darray_triangle_size_get(&geom->unique_triangles);
    865   ASSERT(tsz <= TRG_MAX__);
    866   fprintf(stream, "# . "PRTF_TRG" triangles\n", (trg_id_t)tsz);
    867   fprintf(stream,
    868     "# . "PRTF_TRG" triangles flagged with a merge conflict\n",
    869     geom->merge_conflict_count);
    870   fprintf(stream,
    871     "# . "PRTF_TRG" triangles flagged with a property conflict\n",
    872     geom->merge_conflict_count);
    873 
    874   /* Dump vertices */
    875   vertices = darray_vertex_cdata_get(&geom->unique_vertices);
    876   FOR_EACH(i, 0, vsz)
    877     fprintf(stream, "v %.16g %.16g %.16g\n", SPLIT3(vertices[i].coord));
    878 
    879   /* Dump triangles by groups */
    880   if(flags & SG3D_OBJ_DUMP_VALID_PRIMITIVE) {
    881     dump_partition(geom, stream,
    882         "Valid_triangles", SG3D_OBJ_DUMP_VALID_PRIMITIVE);
    883   }
    884   if(flags & SG3D_OBJ_DUMP_MERGE_CONFLICTS) {
    885     dump_partition(geom, stream,
    886         "Merge_conflicts", SG3D_OBJ_DUMP_MERGE_CONFLICTS);
    887   }
    888   if(flags & SG3D_OBJ_DUMP_PROPERTY_CONFLICTS) {
    889     dump_partition(geom, stream,
    890         "Property_conflicts", SG3D_OBJ_DUMP_PROPERTY_CONFLICTS);
    891   }
    892 
    893 exit:
    894   return res;
    895 error:
    896   goto exit;
    897 }
    898 
    899 res_T
    900 sg3d_geometry_dump_as_vtk
    901   (const struct sg3d_geometry* geom,
    902    FILE* stream)
    903 {
    904   res_T res = RES_OK;
    905   const struct vertex* vertices;
    906   const struct triangle* triangles;
    907   const struct trg_descriptions* descriptions;
    908   size_t vsz, tsz, i;
    909   if(!geom || !stream || !geom->triangle_count_including_duplicates) {
    910     if(geom && !geom->triangle_count_including_duplicates)
    911       log_err(geom->dev,
    912         LIB_NAME":%s: cannot dump empty geometries as VTK\n",
    913         FUNC_NAME);
    914     res = RES_BAD_ARG;
    915     goto error;
    916   }
    917   /* Headers */
    918   fprintf(stream, "# vtk DataFile Version 2.0\n");
    919   fprintf(stream, "Dump of star-geometry-3d geometry\n");
    920   fprintf(stream, "ASCII\n");
    921   fprintf(stream, "DATASET POLYDATA\n");
    922 
    923   /* Dump vertices */
    924   vsz = darray_vertex_size_get(&geom->unique_vertices);
    925   ASSERT(vsz <= VRTX_MAX__);
    926   fprintf(stream, "POINTS "PRTF_VRTX" double\n", (vrtx_id_t)vsz);
    927   vertices = darray_vertex_cdata_get(&geom->unique_vertices);
    928   FOR_EACH(i, 0, vsz)
    929     fprintf(stream, "%.16g %.16g %.16g\n", SPLIT3(vertices[i].coord));
    930 
    931   /* Dump triangles */
    932   tsz = darray_triangle_size_get(&geom->unique_triangles);
    933   ASSERT(4 * tsz <= TRG_MAX__);
    934   fprintf(stream, "POLYGONS "PRTF_TRG" "PRTF_TRG"\n",
    935     (trg_id_t)tsz, (trg_id_t)(4 * tsz));
    936   triangles = darray_triangle_cdata_get(&geom->unique_triangles);
    937   FOR_EACH(i, 0, tsz)
    938     fprintf(stream, "3 "PRTF_VRTX" "PRTF_VRTX" "PRTF_VRTX"\n",
    939       SPLIT3(triangles[i].vertex_ids));
    940 
    941   /* Start triangles properties */
    942   fprintf(stream, "CELL_DATA "PRTF_TRG"\n", (trg_id_t)tsz);
    943   descriptions = darray_trg_descriptions_cdata_get(&geom->trg_descriptions);
    944 
    945   /* Dump front medium */
    946   fprintf(stream, "SCALARS Front_medium unsigned_int 1\n");
    947   fprintf(stream, "LOOKUP_TABLE default\n");
    948   dump_trg_property(geom, stream, SG3D_FRONT);
    949 
    950   /* Dump back medium */
    951   fprintf(stream, "SCALARS Back_medium unsigned_int 1\n");
    952   fprintf(stream, "LOOKUP_TABLE default\n");
    953   dump_trg_property(geom, stream, SG3D_BACK);
    954 
    955   /* Dump interface */
    956   fprintf(stream, "SCALARS Interface unsigned_int 1\n");
    957   fprintf(stream, "LOOKUP_TABLE default\n");
    958   dump_trg_property(geom, stream, SG3D_INTFACE);
    959 
    960   /* Dump unique_id */
    961   fprintf(stream, "SCALARS Unique_ID unsigned_int 1\n");
    962   fprintf(stream, "LOOKUP_TABLE default\n");
    963   FOR_EACH(i, 0, tsz) fprintf(stream, PRTF_TRG"\n", (trg_id_t)i);
    964 
    965   /* Dump user_id */
    966   fprintf(stream, "SCALARS User_ID unsigned_int 1\n");
    967   fprintf(stream, "LOOKUP_TABLE default\n");
    968   FOR_EACH(i, 0, tsz) fprintf(stream, PRTF_TRG"\n", triangles[i].user_id);
    969 
    970   /* Dump merge conflict status (if any) */
    971   if(geom->merge_conflict_count) {
    972     fprintf(stream, "SCALARS Merge_conflict int 1\n");
    973     fprintf(stream, "LOOKUP_TABLE default\n");
    974     FOR_EACH(i, 0, tsz)
    975       fprintf(stream, "%d\n", descriptions[i].merge_conflict);
    976   }
    977 
    978   /* Dump property conflict status (if any) */
    979   if(geom->properties_conflict_count) {
    980     fprintf(stream, "SCALARS Property_conflict int 1\n");
    981     fprintf(stream, "LOOKUP_TABLE default\n");
    982     FOR_EACH(i, 0, tsz)
    983       fprintf(stream, "%d\n", descriptions[i].properties_conflict);
    984   }
    985 
    986   /* Dump rank of the sg3d_geometry_add that created the triangle */
    987   fprintf(stream, "SCALARS Created_at_sg3d_geometry_add unsigned_int 1\n");
    988   fprintf(stream, "LOOKUP_TABLE default\n");
    989   FOR_EACH(i, 0, tsz) {
    990     const struct definition* tdefs;
    991     const unsigned* ranks;
    992     ASSERT(darray_definition_size_get(&descriptions[i].defs[SG3D_FRONT]) > 0);
    993     /* Rank is the first set_id of the first definition of any property */
    994     tdefs = darray_definition_cdata_get(&descriptions[i].defs[SG3D_FRONT]);
    995     ranks = darray_uint_cdata_get(&tdefs[0].set_ids);
    996     fprintf(stream, "%u\n", ranks[0]);
    997   }
    998 
    999 exit:
   1000   return res;
   1001 error:
   1002   goto exit;
   1003 }
   1004 
   1005 res_T
   1006 sg3d_geometry_dump_as_c_code
   1007   (const struct sg3d_geometry* geom,
   1008    FILE* stream,
   1009    const char* name_prefix,
   1010    const int flags)
   1011 {
   1012   res_T res = RES_OK;
   1013   const struct vertex* vertices;
   1014   const struct triangle* triangles;
   1015   const char* qualifiers;
   1016   size_t vsz, tsz, i;
   1017   if(!geom || !stream
   1018     || geom->merge_conflict_count
   1019     || geom->properties_conflict_count
   1020     || !geom->triangle_count_including_duplicates)
   1021   {
   1022     if(geom
   1023       && (geom->merge_conflict_count
   1024         || geom->properties_conflict_count))
   1025       log_err(geom->dev,
   1026         LIB_NAME":%s: cannot dump geometries with conflict as C code\n",
   1027         FUNC_NAME);
   1028     if(geom && !geom->triangle_count_including_duplicates)
   1029       log_err(geom->dev,
   1030         LIB_NAME":%s: cannot dump empty geometries as C code\n",
   1031         FUNC_NAME);
   1032     res = RES_BAD_ARG;
   1033     goto error;
   1034   }
   1035   if(!name_prefix) name_prefix = "";
   1036   /* Headers */
   1037   if(name_prefix && name_prefix[0] != '\0')
   1038     fprintf(stream, "/* Dump of star-geometry-3d '%s'. */\n", name_prefix);
   1039   else
   1040     fprintf(stream, "/* Dump of star-geometry-3d. */\n");
   1041   vsz = darray_vertex_size_get(&geom->unique_vertices);
   1042   ASSERT(3 * vsz <= VRTX_MAX__);
   1043   tsz = darray_triangle_size_get(&geom->unique_triangles);
   1044   ASSERT(3 * tsz <= TRG_MAX__);
   1045 
   1046   if(vsz == 0 || tsz == 0) {
   1047     log_err(geom->dev,
   1048       "%s: no geometry to dump\n",
   1049       FUNC_NAME);
   1050     res = RES_BAD_ARG;
   1051     goto error;
   1052   }
   1053 
   1054   if(flags & SG3D_C_DUMP_CONST && flags & SG3D_C_DUMP_STATIC)
   1055     qualifiers = "static const ";
   1056   else if(flags & SG3D_C_DUMP_CONST)
   1057     qualifiers = "const ";
   1058   else if(flags & SG3D_C_DUMP_STATIC)
   1059     qualifiers = "static ";
   1060   else qualifiers = "";
   1061 
   1062   /* Dump vertices */
   1063   fprintf(stream, "%s"VRTX_TYPE_NAME" %s_vertices_count = "PRTF_VRTX";\n",
   1064     qualifiers, name_prefix, (vrtx_id_t)vsz);
   1065 
   1066   vertices = darray_vertex_cdata_get(&geom->unique_vertices);
   1067   fprintf(stream,
   1068     "%sdouble %s_vertices["PRTF_VRTX"] =\n"
   1069     "{\n",
   1070     qualifiers, name_prefix, (vrtx_id_t)(3 * vsz));
   1071   FOR_EACH(i, 0, vsz - 1)
   1072     fprintf(stream,
   1073       "   %.16g, %.16g, %.16g,\n", SPLIT3(vertices[i].coord));
   1074   fprintf(stream,
   1075     "   %.16g, %.16g, %.16g\n", SPLIT3(vertices[vsz - 1].coord));
   1076   fprintf(stream,
   1077     "};\n");
   1078 
   1079   /* Dump triangles */
   1080   fprintf(stream, "%s"TRG_TYPE_NAME" %s_triangles_count = "PRTF_TRG";\n",
   1081     qualifiers, name_prefix, (trg_id_t)tsz);
   1082 
   1083   triangles = darray_triangle_cdata_get(&geom->unique_triangles);
   1084   fprintf(stream,
   1085     "%s"TRG_TYPE_NAME" %s_triangles["PRTF_TRG"] =\n"
   1086     "{\n",
   1087     qualifiers, name_prefix, (trg_id_t)(3 * tsz));
   1088   FOR_EACH(i, 0, tsz - 1)
   1089     fprintf(stream,
   1090       "   "PRTF_VRTX", "PRTF_VRTX", "PRTF_VRTX",\n",
   1091       SPLIT3(triangles[i].vertex_ids));
   1092   fprintf(stream,
   1093     "   "PRTF_VRTX", "PRTF_VRTX", "PRTF_VRTX"\n",
   1094     SPLIT3(triangles[tsz - 1].vertex_ids));
   1095   fprintf(stream,
   1096     "};\n");
   1097 
   1098   /* Dump properties */
   1099   fprintf(stream,
   1100     "%s"PROP_TYPE_NAME" %s_properties["PRTF_PROP"] =\n"
   1101     "{\n",
   1102     qualifiers, name_prefix, (prop_id_t)(SG3D_PROP_TYPES_COUNT__ * tsz));
   1103   FOR_EACH(i, 0, tsz) {
   1104     int p;
   1105     fprintf(stream, "  ");
   1106     FOR_EACH(p, 0, SG3D_PROP_TYPES_COUNT__) {
   1107       if(triangles[i].properties[p] == SG3D_UNSPECIFIED_PROPERTY)
   1108         fprintf(stream, " SG3D_UNSPECIFIED_PROPERTY");
   1109       else fprintf(stream," "PRTF_PROP"", triangles[i].properties[p]);
   1110       if(i < tsz-1 || p < 2) fprintf(stream, ",");
   1111       if(p == 2) fprintf(stream, "\n");
   1112     }
   1113   }
   1114   fprintf(stream,
   1115     "};\n");
   1116 
   1117 exit:
   1118   return res;
   1119 error:
   1120   goto exit;
   1121 }
   1122 
   1123 res_T
   1124 sg3d_geometry_ref_get(struct sg3d_geometry* geom)
   1125 {
   1126   if(!geom) return RES_BAD_ARG;
   1127   ref_get(&geom->ref);
   1128   return RES_OK;
   1129 }
   1130 
   1131 res_T
   1132 sg3d_geometry_ref_put(struct sg3d_geometry* geom)
   1133 {
   1134   if(!geom) return RES_BAD_ARG;
   1135   ref_put(&geom->ref, geometry_release);
   1136   return RES_OK;
   1137 }