star-2d

Contour structuring for efficient 2D geometric queries
git clone git://git.meso-star.fr/star-2d.git
Log | Files | Refs | README | LICENSE

s2d_line_segments.c (16215B)


      1 /* Copyright (C) 2016-2021, 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 "s2d_c.h"
     17 #include "s2d_device_c.h"
     18 #include "s2d_line_segments.h"
     19 
     20 #include <rsys/float2.h>
     21 
     22 /*******************************************************************************
     23  * Helper functions
     24  ******************************************************************************/
     25 static FINLINE float
     26 line_compute_segment_length
     27   (struct line_segments* lines,
     28    const size_t isegment)
     29 {
     30   const uint32_t* ids;
     31   const float* pos;
     32   const float* v0;
     33   const float* v1;
     34   const size_t id = isegment*2/*#ids per segment*/;
     35   float tmp[2];
     36   ASSERT(lines && isegment < line_segments_get_nsegments(lines));
     37 
     38   ids = line_segments_get_ids(lines);
     39   pos = line_segments_get_pos(lines);
     40   v0 = pos + ids[id+0]*2/*#coords*/;
     41   v1 = pos + ids[id+1]*2/*#coords*/;
     42   return f2_len(f2_sub(tmp, v1, v0));
     43 }
     44 
     45 static void
     46 line_setup_indices
     47   (struct line_segments* lines,
     48    const unsigned nsegments,
     49    void (*get_indices)(const unsigned isegment, unsigned ids[2], void*),
     50    const unsigned nverts,
     51    void* data)
     52 {
     53   uint32_t* indices;
     54   unsigned isegment;
     55   unsigned nsegments_prev;
     56   unsigned nverts_new;
     57   res_T res = RES_OK;
     58   ASSERT(lines && nsegments && nverts);
     59 
     60   nsegments_prev = (unsigned)line_segments_get_nsegments(lines);
     61   ASSERT(get_indices != S2D_KEEP || nsegments == nsegments_prev);
     62 
     63   if(get_indices == S2D_KEEP)
     64     return;
     65 
     66   if(nsegments == nsegments_prev) {
     67     lines->update_mask |= (INDEX_BUFFER & !lines->resize_mask);
     68   } else {
     69     lines->resize_mask |=  INDEX_BUFFER;
     70     lines->update_mask &= !INDEX_BUFFER;
     71   }
     72 
     73   if(lines->indices) { /* Release the old index buffer */
     74     index_buffer_ref_put(lines->indices);
     75     lines->indices = NULL;
     76   }
     77 
     78   /* Allocate the new index buffer */
     79   res = index_buffer_create(lines->dev->allocator, &lines->indices);
     80   if(res != RES_OK) FATAL("Insufficient memory\n");
     81   res = darray_u32_resize(&lines->indices->data, nsegments * 2/*# segmet ids*/);
     82   if(res != RES_OK) FATAL("Insufficient memory\n");
     83 
     84   /* Setup the lines indices */
     85   indices = line_segments_get_ids(lines);
     86   nverts_new = 0;
     87   FOR_EACH(isegment, 0, nsegments) {
     88     uint32_t* ids = indices + isegment*2/*#ids per segment*/;
     89     STATIC_ASSERT(sizeof(unsigned) == sizeof(uint32_t), Unexpected_Type);
     90     get_indices(isegment, ids, data);
     91     nverts_new = MMAX(nverts_new, ids[0]);
     92     nverts_new = MMAX(nverts_new, ids[1]);
     93   }
     94   /* Transform nverts from the last vertex id to vertices count */
     95   if(nverts_new > nverts)
     96     FATAL("Out of bound indexation\n");
     97 }
     98 
     99 static void
    100 line_setup_positions
    101   (struct line_segments* lines,
    102    const unsigned nverts,
    103    struct s2d_vertex_data* attr,
    104    void* data)
    105 {
    106   float* positions;
    107   unsigned ivert, nverts_prev;
    108   res_T res;
    109   ASSERT(lines && nverts && attr && attr->usage == S2D_POSITION);
    110 
    111   if(attr->get == S2D_KEEP) {
    112     ASSERT(lines->attribs[S2D_POSITION]);
    113     ASSERT(darray_float_size_get(&lines->attribs[S2D_POSITION]->data) == nverts*2);
    114     return;
    115   }
    116 
    117   nverts_prev = (unsigned)line_segments_get_nverts(lines);
    118   if(nverts == nverts_prev) {
    119     lines->update_mask |= (VERTEX_BUFFER & ~lines->resize_mask);
    120   } else {
    121     lines->resize_mask |=  VERTEX_BUFFER;
    122     lines->update_mask &= ~VERTEX_BUFFER;
    123   }
    124 
    125   /* Release the old vertex buffer */
    126   if(lines->attribs[S2D_POSITION]) {
    127     vertex_buffer_ref_put(lines->attribs[S2D_POSITION]);
    128     lines->attribs[S2D_POSITION] = NULL;
    129   }
    130 
    131   /* Allocate the vertex positions */
    132   res = vertex_buffer_create(lines->dev->allocator, &lines->attribs[S2D_POSITION]);
    133   if(res != RES_OK) FATAL("Insufficient memory\n");
    134   res = darray_float_resize(&lines->attribs[S2D_POSITION]->data, nverts*2);
    135   if(res != RES_OK) FATAL("Insufficient memory\n");
    136   lines->attribs_type[S2D_POSITION] = S2D_FLOAT2;
    137 
    138   /* Setup the vertex positions */
    139   positions = darray_float_data_get(&lines->attribs[S2D_POSITION]->data);
    140   if(attr->type == S2D_FLOAT2) {
    141     FOR_EACH(ivert, 0, nverts) {
    142       attr->get(ivert, positions + ivert*2/*# coords per vertex*/, data);
    143     }
    144   } else {
    145     FOR_EACH(ivert, 0, nverts) {
    146       float pos[3];
    147       unsigned ipos = ivert * 2/*# coords per vertex*/;
    148       attr->get(ivert, pos, data);
    149       switch(attr->type) {
    150         case S2D_FLOAT:
    151           positions[ipos + 0] = pos[0];
    152           positions[ipos + 1] = 0.f;
    153           break;
    154         case S2D_FLOAT2:
    155           positions[ipos + 0] = pos[0];
    156           positions[ipos + 1] = pos[1];
    157           break;
    158         case S2D_FLOAT3:
    159           positions[ipos + 0] = pos[0] / pos[2];
    160           positions[ipos + 1] = pos[1] / pos[2];
    161           break;
    162         default: FATAL("Unreachable code\n"); break;
    163       }
    164     }
    165   }
    166 }
    167 
    168 static void
    169 line_setup_attribs
    170   (struct line_segments* lines,
    171    const unsigned nverts,
    172    const struct s2d_vertex_data* attr,
    173    void* data)
    174 {
    175   float* attr_data;
    176   unsigned dim;
    177   unsigned ivert;
    178   res_T res;
    179   ASSERT(lines && nverts && attr);
    180   ASSERT(attr->usage!=S2D_POSITION && (unsigned)attr->usage<S2D_ATTRIBS_COUNT__);
    181 
    182   dim = s2d_type_get_dimension(attr->type);
    183   if(attr->get == S2D_KEEP) {
    184     ASSERT(lines->attribs_type[attr->usage] == attr->type);
    185     ASSERT(lines->attribs[attr->usage]);
    186     ASSERT(darray_float_size_get(&lines->attribs[attr->usage]->data) == nverts*dim);
    187     return;
    188   }
    189 
    190   if(lines->attribs[attr->usage]) { /* Release the previous vertex buffer */
    191     vertex_buffer_ref_put(lines->attribs[attr->usage]);
    192     lines->attribs[attr->usage] = NULL;
    193   }
    194 
    195   /* Allocate the new vertex buffer */
    196   res = vertex_buffer_create(lines->dev->allocator, &lines->attribs[attr->usage]);
    197   if(res != RES_OK) FATAL("Insufficient memory\n");
    198   res = darray_float_resize(&lines->attribs[attr->usage]->data, nverts * dim);
    199   if(res != RES_OK) FATAL("Insufficient memory\n");
    200 
    201   /* Setup the vertex attrib */
    202   attr_data = darray_float_data_get(&lines->attribs[attr->usage]->data);
    203   FOR_EACH(ivert, 0, nverts) {
    204     attr->get(ivert, attr_data, data);
    205     attr_data += dim;
    206   }
    207 }
    208 
    209 static void
    210 line_release(ref_T* ref)
    211 {
    212   struct line_segments* lines;
    213   struct s2d_device* dev;
    214   ASSERT(ref);
    215 
    216   lines = CONTAINER_OF(ref, struct line_segments, ref);
    217   line_segments_clear(lines);
    218   dev = lines->dev;
    219   darray_float_release(&lines->cdf);
    220   MEM_RM(dev->allocator, lines);
    221   S2D(device_ref_put(dev));
    222 }
    223 
    224 /*******************************************************************************
    225  * Local functions
    226  ******************************************************************************/
    227 res_T
    228 line_segments_create(struct s2d_device* dev, struct line_segments** out_lines)
    229 {
    230   struct line_segments* lines = NULL;
    231   res_T res = RES_OK;
    232   ASSERT(dev && out_lines);
    233 
    234   lines = (struct line_segments*)MEM_CALLOC
    235     (dev->allocator, 1, sizeof(struct line_segments));
    236   if(!lines) {
    237     res = RES_MEM_ERR;
    238     goto error;
    239   }
    240   ref_init(&lines->ref);
    241   S2D(device_ref_get(dev));
    242   lines->dev = dev;
    243   darray_float_init(dev->allocator, &lines->cdf);
    244 
    245 exit:
    246   *out_lines = lines;
    247   return res;
    248 error:
    249   if(lines) {
    250     line_segments_ref_put(lines);
    251     lines = NULL;
    252   }
    253   goto exit;
    254 }
    255 
    256 void
    257 line_segments_ref_get(struct line_segments* lines)
    258 {
    259   ASSERT(lines);
    260   ref_get(&lines->ref);
    261 }
    262 
    263 void
    264 line_segments_ref_put(struct line_segments* lines)
    265 {
    266   ASSERT(lines);
    267   ref_put(&lines->ref, line_release);
    268 }
    269 
    270 void
    271 line_segments_clear(struct line_segments* lines)
    272 {
    273   size_t iattr;
    274   ASSERT(lines);
    275   if(lines->indices) {
    276     index_buffer_ref_put(lines->indices);
    277     lines->indices = NULL;
    278   }
    279   FOR_EACH(iattr, 0, S2D_ATTRIBS_COUNT__) {
    280     if(lines->attribs[iattr]) {
    281       vertex_buffer_ref_put(lines->attribs[iattr]);
    282       lines->attribs[iattr] = NULL;
    283     }
    284   }
    285   lines->resize_mask = 0;
    286   lines->update_mask = 0;
    287   darray_float_clear(&lines->cdf);
    288 }
    289 
    290 size_t
    291 line_segments_get_nsegments(const struct line_segments* lines)
    292 {
    293   size_t nids;
    294   ASSERT(lines);
    295   if(!lines->indices)
    296     return 0;
    297   nids = darray_u32_size_get(&lines->indices->data);
    298   ASSERT(nids % 2 == 0); /* 2 vertices per segment */
    299   return nids / 2/* #vertices per segement */;
    300 }
    301 
    302 size_t
    303 line_segments_get_nverts(const struct line_segments* lines)
    304 {
    305   size_t ncoords;
    306   ASSERT(lines);
    307   if(!lines->attribs[S2D_POSITION])
    308     return 0;
    309   ASSERT(lines->attribs_type[S2D_POSITION] == S2D_FLOAT2);
    310   ncoords = darray_float_size_get(&lines->attribs[S2D_POSITION]->data);
    311   ASSERT(ncoords % 2 == 0);
    312   return ncoords / 2/* #coords per vertices */;
    313 }
    314 
    315 uint32_t*
    316 line_segments_get_ids(struct line_segments* lines)
    317 {
    318   ASSERT(lines && lines->indices);
    319   return darray_u32_data_get(&lines->indices->data);
    320 }
    321 
    322 float*
    323 line_segments_get_pos(struct line_segments* lines)
    324 {
    325   ASSERT(lines && lines->attribs[S2D_POSITION]);
    326   ASSERT(lines->attribs_type[S2D_POSITION] == S2D_FLOAT2);
    327   return darray_float_data_get(&lines->attribs[S2D_POSITION]->data);
    328 }
    329 
    330 float*
    331 line_segments_get_attr
    332   (struct line_segments* lines,
    333    const enum s2d_attrib_usage usage)
    334 {
    335   ASSERT(lines && usage < S2D_ATTRIBS_COUNT__ && lines->attribs[usage]);
    336   return darray_float_data_get(&lines->attribs[usage]->data);
    337 }
    338 
    339 res_T
    340 line_segments_compute_cdf(struct line_segments* lines)
    341 {
    342   size_t iseg, nsegs;
    343   float length = 0.f;
    344   res_T res = RES_OK;
    345   ASSERT(lines);
    346 
    347   darray_float_clear(&lines->cdf);
    348 
    349   nsegs = line_segments_get_nsegments(lines);
    350   if(!nsegs) goto exit;
    351 
    352   res = darray_float_resize(&lines->cdf, nsegs);
    353   if(res != RES_OK) goto error;
    354 
    355   FOR_EACH(iseg, 0, nsegs) {
    356     length += line_compute_segment_length(lines, iseg);
    357     darray_float_data_get(&lines->cdf)[iseg] = length;
    358   }
    359 
    360 exit:
    361   return res;
    362 error:
    363   darray_float_clear(&lines->cdf);
    364   goto exit;
    365 }
    366 
    367 float
    368 line_segments_compute_length(struct line_segments* lines)
    369 {
    370   size_t iseg, nsegs;
    371   float length = 0.f;
    372   ASSERT(lines);
    373 
    374   nsegs = line_segments_get_nsegments(lines);
    375   if(!nsegs) return 0.f;
    376 
    377   FOR_EACH(iseg, 0, nsegs)
    378     length += line_compute_segment_length(lines, iseg);
    379 
    380   return length;
    381 }
    382 
    383 float
    384 line_segments_compute_area
    385   (struct line_segments* lines,
    386    const char flip_contour)
    387 {
    388   const uint32_t* ids;
    389   const float* pos;
    390   size_t iseg, nsegs;
    391   double area2 = 0.f;
    392   ASSERT(lines);
    393 
    394   nsegs = line_segments_get_nsegments(lines);
    395   if(!nsegs) return 0.f;
    396 
    397   ids = line_segments_get_ids(lines);
    398   pos = line_segments_get_pos(lines);
    399 
    400   /* Build a triangle whose base is the contour segment and its appex is the
    401    * origin. Then compute the area of the triangle and add or sub it from the
    402    * overall area whether the normal point toward or backward the appex */
    403   FOR_EACH(iseg, 0, nsegs) {
    404     const size_t id = iseg * 2/*#ids per segment*/;
    405     const float* v0 = pos + ids[id+0]*2/*#coords*/;
    406     const float* v1 = pos + ids[id+1]*2/*#coords*/;
    407     double tmp;
    408     float dx, dy, N[2], C;
    409 
    410     dx = v1[0] - v0[0];
    411     dy = v1[1] - v0[1];
    412 
    413     if(flip_contour) {
    414       N[0] = -dy;
    415       N[1] = dx;
    416     } else {
    417       N[0] = dy;
    418       N[1] = -dx;
    419     }
    420     C = -f2_dot(N, v0); /* N.v0 + C = 0 */
    421 
    422     tmp = v0[0]*v1[1] - v0[1]*v1[0]; /* Cross product */
    423     tmp = fabs(tmp); /* 2 * area of the triangle */
    424     area2 += C > 0 ? tmp : -tmp;
    425   }
    426   return (float)(area2 * 0.5);
    427 }
    428 
    429 res_T
    430 line_segments_setup_indexed_vertices
    431   (struct line_segments* lines,
    432    const unsigned nsegments,
    433    void (*get_indices)(const unsigned isegment, unsigned ids[2], void* ctx),
    434    const unsigned nverts,
    435    struct s2d_vertex_data attribs[],
    436    const unsigned nattribs,
    437    void* data)
    438 {
    439   unsigned iattr = 0;
    440   int has_position = 0;
    441   res_T res = RES_OK;
    442   ASSERT(lines);
    443 
    444   if(!nsegments || !nverts || !attribs || !nattribs) {
    445     res = RES_BAD_ARG;
    446     goto error;
    447   }
    448 
    449   /* Check indices description */
    450   if(get_indices == S2D_KEEP) {
    451     if(!lines->indices) { /* No indice was previously set */
    452       res = RES_BAD_ARG;
    453       goto error;
    454     } else {
    455       const size_t nsegments_prev = line_segments_get_nsegments(lines);
    456       if(nsegments_prev != nsegments) { /* Inconsistant data */
    457         res = RES_BAD_ARG;
    458         goto error;
    459       }
    460     }
    461   }
    462 
    463   /* Check the vertex data description */
    464   iattr = 0;
    465   has_position = 0;
    466   FOR_EACH(iattr, 0, nattribs) {
    467     if((unsigned)attribs[iattr].usage >= S2D_ATTRIBS_COUNT__) {
    468       res = RES_BAD_ARG;
    469       goto error;
    470     }
    471     if(attribs[iattr].get == S2D_KEEP) {
    472       const enum s2d_attrib_usage attr_usage = attribs[iattr].usage;
    473       const enum s2d_type type = attribs[iattr].type;
    474       if(!lines->attribs[attr_usage]) { /* The vertex attrib was not set */
    475         res = RES_BAD_ARG;
    476         goto error;
    477       } else {
    478         const enum s2d_type type_prev = lines->attribs_type[attr_usage];
    479         const struct darray_float* attr = &lines->attribs[attr_usage]->data;
    480         size_t nverts_prev = darray_float_size_get(attr);
    481         nverts_prev /= s2d_type_get_dimension(type_prev);
    482         if(type_prev != type || nverts_prev != nverts) { /* Inconsistant data */
    483           res = RES_BAD_ARG;
    484           goto error;
    485         }
    486       }
    487     }
    488     if(attribs[iattr].usage == S2D_POSITION)
    489       has_position = 1;
    490   }
    491 
    492   if(!has_position) { /* The vertex must have a position */
    493     res = RES_BAD_ARG;
    494     goto error;
    495   }
    496 
    497   line_setup_indices(lines, nsegments, get_indices, nverts, data);
    498 
    499   /* Setup vertex data */
    500   FOR_EACH(iattr, 0, nattribs) {
    501     if(attribs[iattr].usage == S2D_POSITION) {
    502       line_setup_positions(lines, nverts, attribs + iattr, data);
    503     } else {
    504       line_setup_attribs(lines, nverts, attribs + iattr, data);
    505     }
    506   }
    507 
    508 exit:
    509   return res;
    510 error:
    511   goto exit;
    512 }
    513 
    514 void
    515 line_segments_compute_aabb
    516   (struct line_segments* lines,
    517    float lower[2],
    518    float upper[2])
    519 {
    520   float* pos;
    521   size_t ivert, nverts;
    522   ASSERT(lines && lower && upper);
    523 
    524   f2_splat(lower, FLT_MAX);
    525   f2_splat(upper,-FLT_MAX);
    526 
    527   nverts = line_segments_get_nverts(lines);
    528   if(!nverts) return;
    529 
    530   pos = line_segments_get_pos(lines);
    531   FOR_EACH(ivert, 0, nverts) {
    532     const size_t ipos = ivert * 2/*#coords per vertex*/;
    533     f2_min(lower, lower, pos + ipos);
    534     f2_max(upper, upper, pos + ipos);
    535   }
    536 }
    537 
    538 void
    539 line_segments_copy_indexed_vertices
    540   (const struct line_segments* src,
    541    struct line_segments* dst)
    542 {
    543   size_t nsegments_src;
    544   size_t nsegments_dst;
    545   size_t nverts_src;
    546   size_t nverts_dst;
    547   int i;
    548   ASSERT(src && dst);
    549 
    550   nsegments_src = line_segments_get_nsegments(src);
    551   nsegments_dst = line_segments_get_nsegments(dst);
    552   nverts_src = line_segments_get_nsegments(src);
    553   nverts_dst = line_segments_get_nsegments(dst);
    554 
    555   /* Setup indexe buffer masks */
    556   if(nsegments_src == nsegments_dst) {
    557     dst->update_mask = (INDEX_BUFFER & !dst->resize_mask);
    558   } else {
    559     dst->resize_mask |=  INDEX_BUFFER;
    560     dst->update_mask &= !INDEX_BUFFER;
    561   }
    562 
    563   /* Release the previous index buffer of dst */
    564   if(dst->indices) {
    565     index_buffer_ref_put(dst->indices);
    566     dst->indices = NULL;
    567   }
    568   /* Get a reference onto the index buffer of src */
    569   if(src->indices) {
    570     index_buffer_ref_get(src->indices);
    571     dst->indices = src->indices;
    572   }
    573 
    574   /* Setup the vertex buffer masks */
    575   if(nverts_src == nverts_dst) {
    576     dst->update_mask = (VERTEX_BUFFER & ~dst->resize_mask);
    577   } else {
    578     dst->resize_mask |=  VERTEX_BUFFER;
    579     dst->update_mask &= ~VERTEX_BUFFER;
    580   }
    581 
    582   FOR_EACH(i, 0, S2D_ATTRIBS_COUNT__) {
    583     /* Release the previous vertex buffers of dst */
    584     if(dst->attribs[i]) {
    585       vertex_buffer_ref_put(dst->attribs[i]);
    586       dst->attribs[i] = NULL;
    587     }
    588     /* Get a reference onto the vertex buffers of src */
    589     if(src->attribs[i]) {
    590       vertex_buffer_ref_get(src->attribs[i]);
    591       dst->attribs[i] = src->attribs[i];
    592       dst->attribs_type[i] = src->attribs_type[i];
    593     }
    594   }
    595 }
    596