star-2d

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

test_s2d_scene_view2.c (9809B)


      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.h"
     17 #include "test_s2d_utils.h"
     18 
     19 #include <rsys/float2.h>
     20 #include <rsys/stretchy_array.h>
     21 
     22 #include <string.h>
     23 
     24 #define NSAMPS 10000
     25 
     26 struct ray_data {
     27   struct s2d_primitive prim;
     28   float ray_org[2];
     29   float ray_dir[2];
     30   float ray_range[2];
     31 };
     32 
     33 static INLINE float*
     34 ran_semi_disk_cos_local(float samp[2])
     35 {
     36   samp[0] = rand_canonic() * 2.f - 1.f;
     37   samp[1] = (float)sqrt(1 - samp[0]*samp[0]);
     38   return samp;
     39 }
     40 
     41 static INLINE float*
     42 ran_semi_disk_cos(const float up[2], float samp[2])
     43 {
     44   float tmp[2], v[2];
     45   CHK(f2_is_normalized(samp) == 1);
     46   ran_semi_disk_cos_local(tmp);
     47   v[0] = -up[1] * tmp[0] + up[0]*tmp[1];
     48   v[1] =  up[0] * tmp[0] + up[1]*tmp[1];;
     49   CHK(f2_is_normalized(v) == 1);
     50   return f2_set(samp, v);
     51 }
     52 
     53 static int
     54 discard_self_hit
     55   (const struct s2d_hit* hit,
     56    const float org[2],
     57    const float dir[2],
     58    const float range[2],
     59    void* ray_data,
     60    void* filter_data)
     61 {
     62   struct ray_data* data = ray_data;
     63   CHK(hit != NULL);
     64   CHK(org != NULL);
     65   CHK(dir != NULL);
     66   CHK(range != NULL);
     67   CHK((intptr_t)filter_data == (intptr_t)0xDECAFBAD);
     68   if(!ray_data) return 0;
     69   CHK(f2_eq(data->ray_org, org));
     70   CHK(f2_eq(data->ray_dir, dir));
     71   CHK(f2_eq(data->ray_range, range));
     72   return S2D_PRIMITIVE_EQ(&data->prim, &hit->prim);
     73 }
     74 
     75 static struct s2d_shape*
     76 create_circle_shape
     77   (struct s2d_device* dev,
     78    const float radius,
     79    const float center[2],
     80    const unsigned nsteps)
     81 {
     82   float* positions = NULL;
     83   unsigned* indices = NULL;
     84   struct s2d_shape* shape;
     85   struct s2d_vertex_data vdata;
     86   struct line_segments_desc desc = { NULL, NULL };
     87   const double step = 2.0*PI/(double)nsteps;
     88   unsigned i;
     89 
     90   CHK(s2d_shape_create_line_segments(dev, &shape) == RES_OK);
     91   CHK(s2d_line_segments_set_hit_filter_function
     92     (shape, discard_self_hit, (void*)0xDECAFBAD) == RES_OK);
     93 
     94   CHK(nsteps > 4);
     95   CHK(center != NULL);
     96   CHK(sa_add(positions, nsteps*2/*#coords per vertex*/) != NULL);
     97   CHK(sa_add(indices, nsteps*2/*#ids per segment*/) != NULL);
     98 
     99   FOR_EACH(i, 0, nsteps) {
    100     const double theta = i*step;
    101     const double x = radius*cos(theta) + center[0];
    102     const double y = radius*sin(theta) + center[1];
    103     positions[i*2 + 0] = (float)x;
    104     positions[i*2 + 1] = (float)y;
    105   }
    106 
    107   FOR_EACH(i, 0, nsteps) {
    108     indices[i*2 + 0] = i;
    109     indices[i*2 + 1] = (i+1) % nsteps;
    110   }
    111 
    112   desc.vertices = positions;
    113   desc.indices = indices;
    114 
    115   vdata.type = S2D_FLOAT2;
    116   vdata.usage = S2D_POSITION;
    117   vdata.get = line_segments_get_position;
    118 
    119   CHK(s2d_line_segments_setup_indexed_vertices
    120     (shape, nsteps, line_segments_get_ids, nsteps, &vdata, 1, (void*)&desc)
    121     == RES_OK);
    122 
    123   CHK(s2d_shape_flip_contour(shape) == RES_OK);
    124 
    125   sa_release(positions);
    126   sa_release(indices);
    127   return shape;
    128 }
    129 
    130 int
    131 main(int argc, char** argv)
    132 {
    133   struct mem_allocator allocator;
    134   struct ray_data ray_data;
    135   struct s2d_device* dev;
    136   struct s2d_scene* scn;
    137   struct s2d_scene_view* scnview;
    138   struct s2d_shape* shape;
    139   struct s2d_primitive prim;
    140   unsigned shape_nsteps[3] = { 16, 8, 5 };
    141   unsigned shape_ids[3];
    142   int* shape_prims[3] ={ NULL, NULL, NULL };
    143   int* scene_prims = NULL;
    144   size_t i;
    145   size_t nprims;
    146   float tmp[2];
    147   float sum, sum_sqr;
    148   float E, V, SE;
    149   float area, length;
    150   int ishape;
    151   (void)argc, (void)argv;
    152 
    153   mem_init_proxy_allocator(&allocator, &mem_default_allocator);
    154 
    155   CHK(s2d_device_create(NULL, &allocator, 0, &dev) == RES_OK);
    156   CHK(s2d_scene_create(dev, &scn) == RES_OK);
    157 
    158   CHK(sa_add(shape_prims[0], shape_nsteps[0]) != NULL);
    159   CHK(sa_add(shape_prims[1], shape_nsteps[1]) != NULL);
    160   CHK(sa_add(shape_prims[2], shape_nsteps[2]) != NULL);
    161   CHK(sa_add
    162     (scene_prims, shape_nsteps[0]+shape_nsteps[1]+shape_nsteps[2]) != NULL);
    163 
    164   shape = create_circle_shape(dev, 1.f, f2(tmp, 0.f, 0.f), shape_nsteps[0]);
    165   CHK(s2d_shape_get_id(shape, &shape_ids[0]) == RES_OK);
    166   CHK(shape_ids[0] != S2D_INVALID_ID);
    167   CHK(s2d_scene_attach_shape(scn, shape) == RES_OK);
    168   CHK(s2d_shape_ref_put(shape) == RES_OK);
    169 
    170   shape = create_circle_shape(dev, 0.5f, f2(tmp, 2.f, 0.5f), shape_nsteps[1]);
    171   CHK(s2d_shape_get_id(shape, &shape_ids[1]) == RES_OK);
    172   CHK(shape_ids[1] != S2D_INVALID_ID);
    173   CHK(shape_ids[1] != shape_ids[0]);
    174   CHK(s2d_scene_attach_shape(scn, shape) == RES_OK);
    175   CHK(s2d_shape_ref_put(shape) == RES_OK);
    176 
    177   shape = create_circle_shape(dev, 0.25f, f2(tmp, 1.5f, -0.25f), shape_nsteps[2]);
    178   CHK(s2d_shape_get_id(shape, &shape_ids[2]) == RES_OK);
    179   CHK(shape_ids[2] != S2D_INVALID_ID);
    180   CHK(shape_ids[2] != shape_ids[0]);
    181   CHK(shape_ids[2] != shape_ids[1]);
    182   CHK(s2d_scene_attach_shape(scn, shape) == RES_OK);
    183   CHK(s2d_shape_ref_put(shape) == RES_OK);
    184 
    185   CHK(s2d_scene_view_create
    186     (scn, S2D_SAMPLE|S2D_GET_PRIMITIVE|S2D_TRACE, &scnview) == RES_OK);
    187 
    188   /* Test sampling */
    189   memset(shape_prims[0], 0, sa_size(shape_prims[0])*sizeof(shape_prims[0][0]));
    190   memset(shape_prims[1], 0, sa_size(shape_prims[1])*sizeof(shape_prims[1][0]));
    191   memset(shape_prims[2], 0, sa_size(shape_prims[2])*sizeof(shape_prims[2][0]));
    192   memset(scene_prims, 0, sa_size(scene_prims)*sizeof(scene_prims[0]));
    193   FOR_EACH(i, 0, 1024) {
    194     struct s2d_attrib attr;
    195     float s;
    196     CHK(s2d_scene_view_sample(scnview, rand_canonic(), rand_canonic(), &prim, &s)
    197       == RES_OK);
    198     CHK(s2d_primitive_get_attrib(&prim, S2D_POSITION, s, &attr) == RES_OK);
    199     CHK(attr.type == S2D_FLOAT2);
    200 
    201     FOR_EACH(ishape, 0, 3) if(prim.geom_id == shape_ids[ishape]) break;
    202     CHK(ishape != 3);
    203 
    204     /* Mark the shape primitive as sampled */
    205     CHK(prim.prim_id < shape_nsteps[ishape]);
    206     shape_prims[ishape][prim.prim_id] = 1;
    207 
    208     /* Mark the scene primitive as sampled */
    209     CHK(prim.scene_prim_id < sa_size(scene_prims));
    210     scene_prims[prim.scene_prim_id] = 1;
    211   }
    212 
    213   /* Check that all primitives were sampled */
    214   FOR_EACH(i, 0, sa_size(shape_prims[0])) CHK(shape_prims[0][i] == 1);
    215   FOR_EACH(i, 0, sa_size(shape_prims[1])) CHK(shape_prims[1][i] == 1);
    216   FOR_EACH(i, 0, sa_size(shape_prims[2])) CHK(shape_prims[2][i] == 1);
    217   FOR_EACH(i, 0, sa_size(scene_prims)) CHK(scene_prims[i] == 1);
    218 
    219   /* Check iteration */
    220   memset(shape_prims[0], 0, sa_size(shape_prims[0])*sizeof(shape_prims[0][0]));
    221   memset(shape_prims[1], 0, sa_size(shape_prims[1])*sizeof(shape_prims[1][0]));
    222   memset(shape_prims[2], 0, sa_size(shape_prims[2])*sizeof(shape_prims[2][0]));
    223   memset(scene_prims, 0, sa_size(scene_prims)*sizeof(scene_prims[0]));
    224   CHK(s2d_scene_view_primitives_count(scnview, &nprims) == RES_OK);
    225   CHK(sa_size(scene_prims) == nprims);
    226   FOR_EACH(i, 0, nprims) {
    227 
    228     CHK(s2d_scene_view_get_primitive(scnview, (unsigned)i, &prim) == RES_OK);
    229     FOR_EACH(ishape, 0, 3) if(prim.geom_id == shape_ids[ishape]) break;
    230     CHK(ishape != 3);
    231 
    232     /* Mark the shape primitive as visited */
    233     CHK(prim.prim_id < shape_nsteps[ishape]);
    234     shape_prims[ishape][prim.prim_id] = 1;
    235 
    236     /* Mark the scene primitive as visited */
    237     CHK(prim.scene_prim_id < sa_size(scene_prims));
    238     scene_prims[prim.scene_prim_id] = 1;
    239   }
    240 
    241   /* Check that all primitives were visited */
    242   FOR_EACH(i, 0, sa_size(shape_prims[0])) CHK(shape_prims[0][i] == 1);
    243   FOR_EACH(i, 0, sa_size(shape_prims[1])) CHK(shape_prims[1][i] == 1);
    244   FOR_EACH(i, 0, sa_size(shape_prims[2])) CHK(shape_prims[2][i] == 1);
    245   FOR_EACH(i, 0, sa_size(scene_prims)) CHK(scene_prims[i] == 1);
    246 
    247   /* Check the ray tracing by numerically compute PI*S/P aka 4V/S in 2D */
    248   sum = sum_sqr = 0;
    249   FOR_EACH(i, 0, NSAMPS) {
    250     const float range[2] = { 0.f, FLT_MAX };
    251     struct s2d_attrib attr;
    252     struct s2d_hit hit;
    253     float P[2], N[2];
    254     float s;
    255 
    256     CHK(s2d_scene_view_sample
    257       (scnview, rand_canonic(), rand_canonic(), &prim, &s) == RES_OK);
    258 
    259     CHK(s2d_primitive_get_attrib(&prim, S2D_POSITION, s, &attr) == RES_OK);
    260     CHK(attr.type == S2D_FLOAT2);
    261     f2_set(P, attr.value);
    262 
    263     CHK(s2d_primitive_get_attrib(&prim, S2D_GEOMETRY_NORMAL, s, &attr) == RES_OK);
    264     CHK(attr.type == S2D_FLOAT2);
    265     CHK(f2_normalize(N, attr.value) != 0.f);
    266 
    267     f2_normalize(tmp, f2(tmp, 1, 1));
    268     ran_semi_disk_cos(N, tmp);
    269 
    270     f2_set(ray_data.ray_org, P);
    271     f2_set(ray_data.ray_dir, tmp);
    272     f2_set(ray_data.ray_range, range);
    273     ray_data.prim = prim;
    274     CHK(s2d_scene_view_trace_ray(scnview, P, tmp, range, &ray_data, &hit) == RES_OK);
    275     CHK(S2D_HIT_NONE(&hit) == 0);
    276 
    277     sum += hit.distance;
    278     sum_sqr += hit.distance*hit.distance;
    279   }
    280 
    281   CHK(s2d_scene_view_compute_contour_length(scnview, &length) == RES_OK);
    282   CHK(s2d_scene_view_compute_area(scnview, &area) == RES_OK);
    283 
    284   E = sum / (float)NSAMPS;
    285   V = sum_sqr / (float)NSAMPS - E*E;
    286   SE = (float)sqrt(V/(float)NSAMPS);
    287   printf("PI*S / P = %g ~ %g +/- %g\n",(float)PI*area / length, E, SE);
    288   CHK(eq_epsf((float)PI*area / length, E, 3*SE) == 1);
    289 
    290   CHK(s2d_scene_view_ref_put(scnview) == RES_OK);
    291 
    292   CHK(s2d_scene_ref_put(scn) == RES_OK);
    293   CHK(s2d_device_ref_put(dev) == RES_OK);
    294 
    295   sa_release(scene_prims);
    296   sa_release(shape_prims[0]);
    297   sa_release(shape_prims[1]);
    298   sa_release(shape_prims[2]);
    299 
    300   check_memory_allocator(&allocator);
    301   mem_shutdown_proxy_allocator(&allocator);
    302   CHK(mem_allocated_size() == 0);
    303 
    304   return 0;
    305 }
    306