star-geometry-2d

Cleaning and decorating 2D geometries
git clone git://git.meso-star.fr/star-geometry-2d.git
Log | Files | Refs | README | LICENSE

sg2d_geometry.c (32454B)


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