star-enclosures-3d

Extract enclosures from 3D geometry
git clone git://git.meso-star.fr/star-enclosures-3d.git
Log | Files | Refs | README | LICENSE

senc3d_scene.c (11585B)


      1 /* Copyright (C) 2018-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 "senc3d.h"
     17 #include "senc3d_device_c.h"
     18 #include "senc3d_scene_c.h"
     19 #include "senc3d_scene_analyze_c.h"
     20 
     21 #include <rsys/rsys.h>
     22 #include <rsys/double3.h>
     23 #include <rsys/mem_allocator.h>
     24 
     25 #include <limits.h>
     26 
     27 /******************************************************************************
     28  * Helper function
     29  *****************************************************************************/
     30 static void
     31 scene_release(ref_T * ref)
     32 {
     33   struct senc3d_device* dev = NULL;
     34   struct senc3d_scene* scn = NULL;
     35   ASSERT(ref);
     36   scn = CONTAINER_OF(ref, struct senc3d_scene, ref);
     37   dev = scn->dev;
     38   darray_triangle_in_release(&scn->triangles_in);
     39   darray_position_release(&scn->vertices);
     40   darray_side_range_release(&scn->media_use);
     41 
     42   darray_triangle_enc_release(&scn->analyze.triangles_enc);
     43   darray_enclosure_release(&scn->analyze.enclosures);
     44   darray_enc_ids_array_release(&scn->analyze.enc_ids_array_by_medium);
     45   darray_frontier_edge_release(&scn->analyze.frontiers);
     46   darray_trg_id_release(&scn->analyze.overlapping_ids);
     47 
     48   MEM_RM(dev->allocator, scn);
     49   SENC3D(device_ref_put(dev));
     50 }
     51 
     52 static INLINE int
     53 compatible_medium
     54   (const medium_id_t m1,
     55    const medium_id_t m2)
     56 {
     57   if(m1 == SENC3D_UNSPECIFIED_MEDIUM || m2 == SENC3D_UNSPECIFIED_MEDIUM) return 1;
     58   return (m1 == m2);
     59 }
     60 
     61 /******************************************************************************
     62  * Exported functions
     63  *****************************************************************************/
     64 res_T
     65 senc3d_scene_create
     66   (struct senc3d_device* dev,
     67    const int conv,
     68    const trg_id_t ntris,
     69    void(*indices)(const trg_id_t, vrtx_id_t*, void*),
     70    void(*media)(const trg_id_t, medium_id_t*, void*),
     71    const vrtx_id_t nverts,
     72    void(*position)(const vrtx_id_t, double*, void* ctx),
     73    void* ctx,
     74    struct senc3d_scene** out_scn)
     75 {
     76   struct senc3d_scene* scn = NULL;
     77   /* Tables to detect duplicates */
     78   struct htable_vrtx unique_vertices;
     79   struct htable_trg unique_triangles;
     80   vrtx_id_t nv;
     81   trg_id_t nt;
     82   res_T res = RES_OK;
     83 
     84   if(!dev || !out_scn || !indices || !position || !nverts || !ntris
     85     /* Convention must be set both regarding FRONT/BACK and INSIDE/OUTSIDE */
     86     || !(conv & (SENC3D_CONVENTION_NORMAL_FRONT | SENC3D_CONVENTION_NORMAL_BACK))
     87     || !(conv & (SENC3D_CONVENTION_NORMAL_INSIDE | SENC3D_CONVENTION_NORMAL_OUTSIDE)))
     88     return RES_BAD_ARG;
     89 
     90   htable_vrtx_init(dev->allocator, &unique_vertices);
     91   htable_trg_init(dev->allocator, &unique_triangles);
     92 
     93   scn = MEM_CALLOC(dev->allocator, 1, sizeof(struct senc3d_scene));
     94   if(!scn) {
     95     log_err(dev, LIB_NAME":%s: could not allocate the star_enclosures-3d scene.\n",
     96       FUNC_NAME);
     97     res = RES_MEM_ERR;
     98     goto error;
     99   }
    100   ref_init(&scn->ref);
    101   SENC3D(device_ref_get(dev));
    102   scn->dev = dev;
    103   scn->convention = conv;
    104   scn->ntris = ntris;
    105   scn->nverts = nverts;
    106   darray_triangle_in_init(dev->allocator, &scn->triangles_in);
    107   darray_position_init(dev->allocator, &scn->vertices);
    108   darray_side_range_init(dev->allocator, &scn->media_use);
    109 
    110   darray_triangle_enc_init(scn->dev->allocator, &scn->analyze.triangles_enc);
    111   darray_enclosure_init(scn->dev->allocator, &scn->analyze.enclosures);
    112   darray_enc_ids_array_init(scn->dev->allocator,
    113     &scn->analyze.enc_ids_array_by_medium);
    114   darray_frontier_edge_init(scn->dev->allocator, &scn->analyze.frontiers);
    115   /* Enclosure 0 is always defined for infinite */
    116   OK(darray_enclosure_resize(&scn->analyze.enclosures, 1));
    117   scn->analyze.enclosures_count = 1;
    118   darray_trg_id_init(scn->dev->allocator, &scn->analyze.overlapping_ids);
    119 
    120   OK(darray_position_reserve(&scn->vertices, scn->nverts));
    121   OK(darray_triangle_in_reserve(&scn->triangles_in, scn->ntris));
    122   OK(htable_vrtx_reserve(&unique_vertices, scn->nverts));
    123   OK(htable_trg_reserve(&unique_triangles, scn->ntris));
    124 
    125   /* Get vertices */
    126   FOR_EACH(nv, 0, nverts) {
    127     vrtx_id_t* p_vrtx;
    128     union double3 tmp;
    129     /* API: position needs an api_t */
    130     position(nv, tmp.vec, ctx);
    131     p_vrtx = htable_vrtx_find(&unique_vertices, &tmp);
    132     if(p_vrtx) {
    133       /* Duplicate vertex */
    134       log_err(scn->dev, LIB_NAME":%s: vertex "PRTF_VRTX" is a duplicate.\n",
    135         FUNC_NAME, nv);
    136       res = RES_BAD_ARG;
    137       goto error;
    138     }
    139     /* New vertex */
    140     ASSERT(nv == htable_vrtx_size_get(&unique_vertices));
    141     OK(darray_position_push_back(&scn->vertices, &tmp));
    142     OK(htable_vrtx_set(&unique_vertices, &tmp, &nv));
    143   }
    144   /* Get triangles */
    145   FOR_EACH(nt, 0, ntris) {
    146     int j, dg, s;
    147     medium_id_t med[2]
    148       = { SENC3D_UNSPECIFIED_MEDIUM, SENC3D_UNSPECIFIED_MEDIUM };
    149     vrtx_id_t ind[3];
    150     union vrtx_id3 trg_key;
    151     struct triangle_in tmp;
    152     trg_id_t* p_trg;
    153     indices(nt, ind, ctx);
    154     FOR_EACH(j, 0, 3) {
    155       if(ind[j] >= nverts) {
    156         log_err(scn->dev,
    157           LIB_NAME":%s: triangle "PRTF_TRG" uses invalid vertex id "PRTF_VRTX".\n",
    158           FUNC_NAME, nt, ind[j]);
    159         res = RES_BAD_ARG;
    160         goto error;
    161       }
    162       ASSERT(ind[j] <= VRTX_MAX__);
    163       tmp.vertice_id[j] = (vrtx_id_t)ind[j];
    164     }
    165     dg = tmp.vertice_id[0] == tmp.vertice_id[1]
    166       || tmp.vertice_id[0] == tmp.vertice_id[2]
    167       || tmp.vertice_id[1] == tmp.vertice_id[2];
    168     if(!dg) {
    169       double edge1[3], edge2[3], n[3];
    170       const union double3* vertices = darray_position_cdata_get(&scn->vertices);
    171       d3_sub(edge1, vertices[tmp.vertice_id[1]].vec, vertices[tmp.vertice_id[0]].vec);
    172       d3_sub(edge2, vertices[tmp.vertice_id[2]].vec, vertices[tmp.vertice_id[0]].vec);
    173       d3_cross(n, edge1, edge2);
    174       dg = d3_len(n) == 0;
    175     }
    176     if(dg) {
    177       log_err(scn->dev, LIB_NAME":%s: triangle "PRTF_TRG" is degenerated.\n",
    178         FUNC_NAME, nt);
    179       res = RES_BAD_ARG;
    180       goto error;
    181     }
    182     /* Get media */
    183     if(media) {
    184       media(nt, med, ctx);
    185       FOR_EACH(s, 0, 2) {
    186         if(med[s] == SENC3D_UNSPECIFIED_MEDIUM || med[s] <= MEDIUM_MAX__)
    187           continue;
    188         res = RES_BAD_ARG;
    189         goto error;
    190       }
    191     }
    192     trg_make_key(&trg_key, tmp.vertice_id);
    193     p_trg = htable_trg_find(&unique_triangles, &trg_key);
    194     if(p_trg) {
    195       /* Duplicate triangle */
    196       log_err(scn->dev, LIB_NAME":%s: triangle "PRTF_TRG" is a duplicate.\n",
    197         FUNC_NAME, nt);
    198       res = RES_BAD_ARG;
    199       goto error;
    200     }
    201     /* New triangle */
    202     ASSERT(nt == htable_trg_size_get(&unique_triangles));
    203     OK(htable_trg_set(&unique_triangles, &trg_key, &nt));
    204     FOR_EACH(s, 0, 2) {
    205       const enum senc3d_side side[2] = { SENC3D_FRONT, SENC3D_BACK };
    206       struct side_range* media_use;
    207       size_t m_idx = medium_id_2_medium_idx(med[s]);
    208       tmp.medium[s] = med[s];
    209       if(m_idx >= darray_side_range_size_get(&scn->media_use)) {
    210         OK(darray_side_range_resize(&scn->media_use, 1 + m_idx));
    211       }
    212       /* media_use 0 is for SENC3D_UNSPECIFIED_MEDIUM */
    213       media_use = darray_side_range_data_get(&scn->media_use) + m_idx;
    214       media_use->first =
    215         MMIN(media_use->first, TRGIDxSIDE_2_TRGSIDE((trg_id_t)nt, side[s]));
    216       ASSERT(media_use->first < 2 * (scn->ntris + 1));
    217       media_use->last =
    218         MMAX(media_use->last, TRGIDxSIDE_2_TRGSIDE((trg_id_t)nt, side[s]));
    219       ASSERT(media_use->last < 2 * (scn->ntris + 1));
    220       ASSERT(media_use->first <= media_use->last);
    221     }
    222     OK(darray_triangle_in_push_back(&scn->triangles_in, &tmp));
    223   }
    224 
    225   OK(darray_enc_ids_array_resize(&scn->analyze.enc_ids_array_by_medium,
    226     darray_side_range_size_get(&scn->media_use)));
    227   /* Proceed to the analyze */
    228   OK(scene_analyze(scn));
    229 
    230 exit:
    231   htable_vrtx_release(&unique_vertices);
    232   htable_trg_release(&unique_triangles);
    233   if(scn) *out_scn = scn;
    234   return res;
    235 
    236 error:
    237   if(scn) {
    238     SENC3D(scene_ref_put(scn));
    239     scn = NULL;
    240   }
    241   goto exit;
    242 }
    243 
    244 res_T
    245 senc3d_scene_get_convention
    246   (const struct senc3d_scene* scn,
    247    int* convention)
    248 {
    249   if(!scn || !convention) return RES_BAD_ARG;
    250   *convention = scn->convention;
    251   return RES_OK;
    252 }
    253 
    254 res_T
    255 senc3d_scene_get_triangles_count
    256   (const struct senc3d_scene* scn,
    257    trg_id_t* count)
    258 {
    259   if(!scn || !count) return RES_BAD_ARG;
    260   *count = scn->ntris;
    261   return RES_OK;
    262 }
    263 
    264 res_T
    265 senc3d_scene_get_triangle
    266   (const struct senc3d_scene* scn,
    267    const trg_id_t itri,
    268    vrtx_id_t indices[3])
    269 {
    270   const struct triangle_in* trg;
    271   int i;
    272   if(!scn || !indices
    273     || itri >= darray_triangle_in_size_get(&scn->triangles_in))
    274     return RES_BAD_ARG;
    275   trg = darray_triangle_in_cdata_get(&scn->triangles_in) + itri;
    276   FOR_EACH(i, 0, 3) indices[i] = trg->vertice_id[i];
    277   return RES_OK;
    278 }
    279 
    280 res_T
    281 senc3d_scene_get_triangle_media
    282   (const struct senc3d_scene* scn,
    283    const trg_id_t itri,
    284    medium_id_t media[2])
    285 {
    286   const struct triangle_in* trg;
    287   int i;
    288   if(!scn || !media
    289     || itri >= darray_triangle_in_size_get(&scn->triangles_in))
    290     return RES_BAD_ARG;
    291   trg = darray_triangle_in_cdata_get(&scn->triangles_in) + itri;
    292   FOR_EACH(i, 0, 2) media[i] = trg->medium[i];
    293   return RES_OK;
    294 }
    295 
    296 res_T
    297 senc3d_scene_get_vertices_count
    298   (const struct senc3d_scene* scn,
    299    vrtx_id_t* count)
    300 {
    301   if(!scn || !count) return RES_BAD_ARG;
    302   *count = scn->nverts;
    303   return RES_OK;
    304 }
    305 
    306 res_T
    307 senc3d_scene_get_vertex
    308   (const struct senc3d_scene* scn,
    309    const vrtx_id_t ivert,
    310    double coord[3])
    311 {
    312   const union double3* v;
    313   if(!scn || !coord
    314     || ivert >= darray_position_size_get(&scn->vertices))
    315     return RES_BAD_ARG;
    316   v = darray_position_cdata_get(&scn->vertices) + ivert;
    317   d3_set(coord, v->vec);
    318   return RES_OK;
    319 }
    320 
    321 res_T
    322 senc3d_scene_dump_enclosure_obj
    323   (struct senc3d_scene* scn,
    324    const unsigned enc,
    325    const char* name)
    326 {
    327   struct senc3d_enclosure* enclosure = NULL;
    328   struct senc3d_enclosure_header header;
    329   FILE* stream = NULL;
    330   unsigned count, i;
    331   res_T res = RES_OK;
    332 
    333   if(!scn || !name) {
    334     res = RES_BAD_ARG;
    335     goto error;
    336   }
    337 
    338   OK(senc3d_scene_get_enclosure_count(scn, &count));
    339   if(enc >= count) {
    340     res = RES_BAD_ARG;
    341     goto error;
    342   }
    343 
    344   OK(senc3d_scene_get_enclosure(scn, enc, &enclosure));
    345   OK(senc3d_enclosure_get_header(enclosure, &header));
    346 
    347   stream = fopen(name, "w");
    348   if(!stream) {
    349     res = RES_BAD_ARG;
    350     goto error;
    351   }
    352 
    353   FOR_EACH(i, 0, header.vertices_count) {
    354     double tmp[3];
    355     OK(senc3d_enclosure_get_vertex(enclosure, i, tmp));
    356     fprintf(stream, "v %g %g %g\n", SPLIT3(tmp));
    357   }
    358   FOR_EACH(i, 0, header.primitives_count) {
    359     unsigned indices[3];
    360     OK(senc3d_enclosure_get_triangle(enclosure, i, indices));
    361     fprintf(stream, "f %u %u %u\n",
    362       1+indices[0], 1+indices[1], 1+indices[2]);
    363   }
    364 
    365 exit:
    366   if(enclosure) SENC3D(enclosure_ref_put(enclosure));
    367   if(stream) fclose(stream);
    368   return res;
    369 error:
    370   goto exit;
    371 }
    372 
    373 res_T
    374 senc3d_scene_ref_get(struct senc3d_scene* scn)
    375 {
    376   if(!scn) return RES_BAD_ARG;
    377   ref_get(&scn->ref);
    378   return RES_OK;
    379 }
    380 
    381 res_T
    382 senc3d_scene_ref_put(struct senc3d_scene* scn)
    383 {
    384   if(!scn) return RES_BAD_ARG;
    385   ref_put(&scn->ref, scene_release);
    386   return RES_OK;
    387 }