solstice

Compute collected power and efficiencies of a solar plant
git clone git://git.meso-star.com/solstice.git
Log | Files | Refs | README | LICENSE

solparser_geometry.c (47207B)


      1 /* Copyright (C) 2018-2026 |Meso|Star> (contact@meso-star.com)
      2  * Copyright (C) 2016-2018 CNRS
      3  *
      4  * This program is free software: you can redistribute it and/or modify
      5  * it under the terms of the GNU General Public License as published by
      6  * the Free Software Foundation, either version 3 of the License, or
      7  * (at your option) any later version.
      8  *
      9  * This program is distributed in the hope that it will be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     12  * GNU General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU General Public License
     15  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     16 
     17 #define _POSIX_C_SOURCE 200112L /* nextafter support */
     18 
     19 #include "solparser_c.h"
     20 #include <rsys/double2.h>
     21 #include <math.h> /* nextafter */
     22 
     23 /*******************************************************************************
     24  * Helper functions
     25  ******************************************************************************/
     26 static res_T
     27 parse_clip_op
     28   (struct solparser* parser,
     29    const yaml_node_t* op,
     30    enum solparser_clip_op* clip_op)
     31 {
     32   res_T res = RES_OK;
     33   ASSERT(op && clip_op);
     34 
     35   if(op->type != YAML_SCALAR_NODE) {
     36     log_err(parser, op, "expect a clipping operation.\n");
     37     res = RES_BAD_ARG;
     38     goto error;
     39   }
     40 
     41   if(!strcmp((char*)op->data.scalar.value, "AND")) {
     42     *clip_op = SOLPARSER_CLIP_OP_AND;
     43   } else if(!strcmp((char*)op->data.scalar.value, "SUB")) {
     44     *clip_op = SOLPARSER_CLIP_OP_SUB;
     45   } else {
     46     log_err(parser, op, "unknown clipping operation `%s'.\n",
     47       op->data.scalar.value);
     48     res = RES_BAD_ARG;
     49     goto error;
     50   }
     51 
     52 exit:
     53   return res;
     54 error:
     55   goto exit;
     56 }
     57 
     58 static res_T
     59 parse_vertices
     60   (struct solparser* parser,
     61    yaml_document_t* doc,
     62    const yaml_node_t* vertices,
     63    struct darray_double* coords)
     64 {
     65   intptr_t i, n;
     66   res_T res = RES_OK;
     67   ASSERT(doc && vertices && coords);
     68 
     69   if(vertices->type != YAML_SEQUENCE_NODE) {
     70     log_err(parser, vertices, "expect a list of vertices.\n");
     71     res = RES_BAD_ARG;
     72     goto error;
     73   }
     74 
     75   n = vertices->data.sequence.items.top - vertices->data.sequence.items.start;
     76   if(n < 3) {
     77     log_err(parser, vertices, "expect at least 3 vertices.\n");
     78     res = RES_BAD_ARG;
     79     goto error;
     80   }
     81 
     82   res = darray_double_resize(coords, (size_t)n*2/*#coords per vertex*/);
     83   if(res != RES_OK) {
     84     log_err(parser, vertices, "could not allocate the array of vertices.\n");
     85     goto error;
     86   }
     87 
     88   FOR_EACH(i, 0, n) {
     89     yaml_node_t* vertex;
     90     double* real2 = darray_double_data_get(coords) + i*2/*#coords per vertex*/;
     91 
     92     vertex = yaml_document_get_node(doc, vertices->data.sequence.items.start[i]);
     93     res = parse_real2(parser, doc, vertex, -DBL_MAX, DBL_MAX, real2);
     94     if(res != RES_OK) goto error;
     95   }
     96 
     97 exit:
     98   return res;
     99 error:
    100   darray_double_clear(coords);
    101   goto exit;
    102 }
    103 
    104 static res_T
    105 parse_circle
    106   (struct solparser* parser,
    107    yaml_document_t* doc,
    108    const yaml_node_t* circle,
    109    struct solparser_circleclip* clip)
    110 {
    111   enum { RADIUS, CENTER, SEGMENTS };
    112   intptr_t i, n;
    113   int mask = 0; /* Register the parsed attributes */
    114   res_T res = RES_OK;
    115   ASSERT(doc && circle && clip);
    116 
    117   if(circle->type != YAML_MAPPING_NODE) {
    118     log_err(parser, circle,
    119       "expect a mapping of clipping circles parameters.\n");
    120     res = RES_BAD_ARG;
    121     goto error;
    122   }
    123 
    124   n = circle->data.mapping.pairs.top - circle->data.mapping.pairs.start;
    125   clip->segments = 64; /* default value */
    126   d2_splat(clip->center, 0); /* default value */
    127   FOR_EACH(i, 0, n) {
    128     yaml_node_t* key;
    129     yaml_node_t* val;
    130 
    131     key = yaml_document_get_node(doc, circle->data.mapping.pairs.start[i].key);
    132     val = yaml_document_get_node(doc, circle->data.mapping.pairs.start[i].value);
    133     if (key->type != YAML_SCALAR_NODE) {
    134       log_err(parser, key, "expect a clipping circle parameter.\n");
    135       res = RES_BAD_ARG;
    136       goto error;
    137     }
    138 
    139     #define SETUP_MASK(Flag, Name) {                                           \
    140       if(mask & BIT(Flag)) {                                                   \
    141         log_err(parser, key,                                                   \
    142           "the clipping circle parameter `"Name"' is already defined.\n");     \
    143         res = RES_BAD_ARG;                                                     \
    144         goto error;                                                            \
    145       }                                                                        \
    146       mask |= BIT(Flag);                                                       \
    147     } (void)0
    148     if(!strcmp((char*)key->data.scalar.value, "radius")) {
    149       SETUP_MASK(RADIUS, "radius");
    150       res = parse_real(parser, val, nextafter(0, 1), DBL_MAX, &clip->radius);
    151     }
    152     else if(!strcmp((char*)key->data.scalar.value, "center")) {
    153       SETUP_MASK(CENTER, "center");
    154       res = parse_real2(parser, doc, val, -DBL_MAX, DBL_MAX, clip->center);
    155     }
    156     else if(!strcmp((char*)key->data.scalar.value, "segments")) {
    157       SETUP_MASK(SEGMENTS, "segments");
    158       res = parse_integer(parser, val, 3, 4096, &clip->segments);
    159     } else {
    160       log_err(parser, key, "unknown clipping circle parameter `%s'.\n",
    161         key->data.scalar.value);
    162       res = RES_BAD_ARG;
    163       goto error;
    164     }
    165     if(res != RES_OK) {
    166       log_node(parser, key);
    167       goto error;
    168     }
    169   #undef SETUP_MASK
    170   }
    171 
    172   #define CHECK_PARAM(Flag, Name)                                              \
    173     if(!(mask & BIT(Flag))) {                                                  \
    174       log_err(parser, circle,                                                  \
    175         "the clipping circle parameter `"Name"' is missing.\n");               \
    176       res = RES_BAD_ARG;                                                       \
    177       goto error;                                                              \
    178     } (void)0
    179   CHECK_PARAM(RADIUS, "radius");
    180   #undef CHECK_PARAM
    181 
    182 exit:
    183   return res;
    184 error:
    185   goto exit;
    186 }
    187 
    188 static res_T
    189 parse_polyclip
    190   (struct solparser* parser,
    191    yaml_document_t* doc,
    192    const yaml_node_t* polyclip,
    193    struct solparser_polyclip* clip)
    194 {
    195   enum { OPERATION, CONTOUR };
    196   intptr_t i, n;
    197   int mask = 0; /* Register the parsed attributes */
    198   res_T res = RES_OK;
    199   ASSERT(doc && polyclip && clip);
    200 
    201   if(polyclip->type != YAML_MAPPING_NODE) {
    202     log_err(parser, polyclip,
    203       "expect a mapping of clipping polygon parameters.\n");
    204     res = RES_OK;
    205     goto error;
    206   }
    207 
    208   n = polyclip->data.mapping.pairs.top - polyclip->data.mapping.pairs.start;
    209   FOR_EACH(i, 0, n) {
    210     yaml_node_t* key;
    211     yaml_node_t* val;
    212 
    213     key = yaml_document_get_node(doc, polyclip->data.mapping.pairs.start[i].key);
    214     val = yaml_document_get_node(doc, polyclip->data.mapping.pairs.start[i].value);
    215     if(key->type != YAML_SCALAR_NODE) {
    216       log_err(parser, key, "expect a clipping polygon parameter.\n");
    217       res = RES_BAD_ARG;
    218       goto error;
    219     }
    220 
    221     #define SETUP_MASK(Flag, Name) {                                           \
    222       if(mask & BIT(Flag)) {                                                   \
    223         log_err(parser, key,                                                   \
    224           "the clipping polygon parameter `"Name"' is already defined.\n");    \
    225         res = RES_BAD_ARG;                                                     \
    226         goto error;                                                            \
    227       }                                                                        \
    228       mask |= BIT(Flag);                                                       \
    229     } (void)0
    230     if(!strcmp((char*)key->data.scalar.value, "operation")) {
    231       SETUP_MASK(OPERATION, "operation");
    232       res = parse_clip_op(parser, val, &clip->op);
    233     } else if(!strcmp((char*)key->data.scalar.value, "vertices")) {
    234       SETUP_MASK(CONTOUR, "contour");
    235       clip->contour_type = SOLPARSER_CLIP_CONTOUR_POLY;
    236       res = parse_vertices(parser, doc, val, &clip->vertices);
    237     }
    238     else if(!strcmp((char*)key->data.scalar.value, "circle")) {
    239       SETUP_MASK(CONTOUR, "contour");
    240       clip->contour_type = SOLPARSER_CLIP_CONTOUR_CIRCLE;
    241       res = parse_circle(parser, doc, val, &clip->circle);
    242     } else {
    243       log_err(parser, key, "unknown clipping polygon parameter `%s'.\n",
    244         key->data.scalar.value);
    245       res = RES_BAD_ARG;
    246       goto error;
    247     }
    248     if(res != RES_OK) {
    249       log_node(parser, key);
    250       goto error;
    251     }
    252     #undef SETUP_MASK
    253   }
    254 
    255   #define CHECK_PARAM(Flag, Name)                                              \
    256     if(!(mask & BIT(Flag))) {                                                  \
    257       log_err(parser, polyclip,                                                \
    258         "the clipping polygon parameter `"Name"' is missing.\n");              \
    259       res = RES_BAD_ARG;                                                       \
    260       goto error;                                                              \
    261     } (void)0
    262   CHECK_PARAM(OPERATION, "operation");
    263   CHECK_PARAM(CONTOUR, "contour");
    264   #undef CHECK_PARAM
    265 
    266 exit:
    267   return res;
    268 error:
    269   goto exit;
    270 }
    271 
    272 static res_T
    273 parse_clip
    274   (struct solparser* parser,
    275    yaml_document_t* doc,
    276    const yaml_node_t* clip,
    277    struct darray_polyclip* polyclips)
    278 {
    279   intptr_t i, n;
    280   res_T res = RES_OK;
    281   ASSERT(doc && clip && polyclips);
    282 
    283   if(clip->type != YAML_SEQUENCE_NODE) {
    284     log_err(parser, clip, "expect a list of clipping polygons.\n");
    285     res = RES_BAD_ARG;
    286     goto error;
    287   }
    288 
    289   n = clip->data.sequence.items.top - clip->data.sequence.items.start;
    290 
    291   /* Allocate the clipping polygons */
    292   res = darray_polyclip_resize(polyclips, (size_t)n);
    293   if(res != RES_OK) {
    294     log_err(parser, clip, "could not allocate the list of clipping polygons.\n");
    295     goto error;
    296   }
    297 
    298   FOR_EACH(i, 0, n) {
    299     yaml_node_t* node;
    300     struct solparser_polyclip* polyclip = darray_polyclip_data_get(polyclips) + i;
    301 
    302     node = yaml_document_get_node(doc, clip->data.sequence.items.start[i]);
    303     res = parse_polyclip(parser, doc, node, polyclip);
    304     if(res != RES_OK) goto error;
    305   }
    306 
    307 exit:
    308   return res;
    309 error:
    310   goto exit;
    311 }
    312 
    313 
    314 static res_T
    315 parse_cuboid
    316   (struct solparser* parser,
    317    yaml_document_t* doc,
    318    const yaml_node_t* cuboid,
    319    struct solparser_shape_cuboid_id* out_ishape)
    320 {
    321   enum { SIZE };
    322   struct solparser_shape_cuboid* shape = NULL;
    323   size_t ishape = SIZE_MAX;
    324   intptr_t i, n;
    325   int mask = 0; /* Register the parsed attributes */
    326   res_T res = RES_OK;
    327   ASSERT(doc && cuboid && out_ishape);
    328 
    329   if(cuboid->type != YAML_MAPPING_NODE) {
    330     log_err(parser, cuboid, "expect a mapping of cuboid parameters.\n");
    331     res = RES_BAD_ARG;
    332     goto error;
    333   }
    334 
    335   /* Allocate a cuboid */
    336   ishape = darray_cuboid_size_get(&parser->cuboids);
    337   res = darray_cuboid_resize(&parser->cuboids, ishape + 1);
    338   if(res != RES_OK) {
    339     log_err(parser, cuboid, "could not allocate the cuboid shape.\n");
    340     goto exit;
    341   }
    342   shape = darray_cuboid_data_get(&parser->cuboids) + ishape;
    343 
    344   n = cuboid->data.mapping.pairs.top - cuboid->data.mapping.pairs.start;
    345   FOR_EACH(i, 0, n) {
    346     yaml_node_t* key;
    347     yaml_node_t* val;
    348 
    349     key = yaml_document_get_node(doc, cuboid->data.mapping.pairs.start[i].key);
    350     val = yaml_document_get_node(doc, cuboid->data.mapping.pairs.start[i].value);
    351     if(key->type != YAML_SCALAR_NODE) {
    352       log_err(parser, key, "expect cuboid parameters.\n");
    353       res = RES_BAD_ARG;
    354       goto error;
    355     }
    356     if(!strcmp((char*)key->data.scalar.value, "size")) {
    357       if(mask & BIT(SIZE)) {
    358         log_err(parser, key, "the cuboid size is already defined.\n");
    359         res = RES_BAD_ARG;
    360         goto error;
    361       }
    362       mask |= BIT(SIZE);
    363       res = parse_real3(parser, doc, val, nextafter(0, 1), DBL_MAX, shape->size);
    364     } else {
    365       log_err(parser, key, "unknown cuboid parameter `%s'.\n",
    366         key->data.scalar.value);
    367       res = RES_BAD_ARG;
    368       goto error;
    369     }
    370     if(res != RES_OK) {
    371       log_node(parser, key);
    372       goto error;
    373     }
    374   }
    375 
    376   if(!(mask & BIT(SIZE))) {
    377     log_err(parser, cuboid, "the size of the cuboid is missing.\n");
    378     res = RES_BAD_ARG;
    379     goto error;
    380   }
    381 
    382 exit:
    383   out_ishape->i = ishape;
    384   return res;
    385 error:
    386   if(shape) {
    387     darray_cuboid_pop_back(&parser->cuboids);
    388     ishape = SIZE_MAX;
    389   }
    390   goto exit;
    391 }
    392 
    393 static res_T
    394 parse_cylinder
    395   (struct solparser* parser,
    396    yaml_document_t* doc,
    397    const yaml_node_t* cylinder,
    398    struct solparser_shape_cylinder_id* out_ishape)
    399 {
    400   enum { HEIGHT, RADIUS, SLICES, STACKS };
    401   struct solparser_shape_cylinder* shape = NULL;
    402   size_t ishape = SIZE_MAX;
    403   intptr_t i, n;
    404   int mask = 0; /* Register the parsed attributes */
    405   res_T res = RES_OK;
    406   ASSERT(doc && cylinder && out_ishape);
    407 
    408   if(cylinder->type != YAML_MAPPING_NODE) {
    409     log_err(parser, cylinder, "expect a mapping of cylinder parameters.\n");
    410     res = RES_BAD_ARG;
    411     goto error;
    412   }
    413 
    414   /* Allocate a cylinder */
    415   ishape = darray_cylinder_size_get(&parser->cylinders);
    416   res = darray_cylinder_resize(&parser->cylinders, ishape + 1);
    417   if(res != RES_OK) {
    418     log_err(parser, cylinder, "could not alocate the cylinder shape.\n");
    419     goto exit;
    420   }
    421   shape = darray_cylinder_data_get(&parser->cylinders) + ishape;
    422 
    423   n = cylinder->data.mapping.pairs.top - cylinder->data.mapping.pairs.start;
    424   shape->nslices = 16; /* default value */
    425   shape->nstacks = 1; /* default value */
    426   FOR_EACH(i, 0, n) {
    427     yaml_node_t* key;
    428     yaml_node_t* val;
    429 
    430     key = yaml_document_get_node(doc, cylinder->data.mapping.pairs.start[i].key);
    431     val = yaml_document_get_node(doc, cylinder->data.mapping.pairs.start[i].value);
    432     if(key->type != YAML_SCALAR_NODE) {
    433       log_err(parser, key, "expect cylinder parameters.\n");
    434       res = RES_BAD_ARG;
    435       goto error;
    436     }
    437     #define SETUP_MASK(Flag, Name) {                                           \
    438       if(mask & BIT(Flag)) {                                                   \
    439         log_err(parser, key,                                                   \
    440           "the cylinder parameter `"Name"' is already defined.\n");            \
    441         res = RES_BAD_ARG;                                                     \
    442         goto error;                                                            \
    443       }                                                                        \
    444       mask |= BIT(Flag);                                                       \
    445     } (void)0
    446     if(!strcmp((char*)key->data.scalar.value, "height")) {
    447       SETUP_MASK(HEIGHT, "height");
    448       res = parse_real(parser, val, nextafter(0, 1), DBL_MAX, &shape->height);
    449     } else if(!strcmp((char*)key->data.scalar.value, "radius")) {
    450       SETUP_MASK(RADIUS, "radius");
    451       res = parse_real(parser, val, nextafter(0, 1), DBL_MAX, &shape->radius);
    452     } else if(!strcmp((char*)key->data.scalar.value, "slices")) {
    453       SETUP_MASK(SLICES, "slices");
    454       res = parse_integer(parser, val, 4, 4096, &shape->nslices);
    455     }
    456     else if(!strcmp((char*)key->data.scalar.value, "stacks")) {
    457       SETUP_MASK(STACKS, "stacks");
    458       res = parse_integer(parser, val, 1, 4096, &shape->nstacks);
    459     } else {
    460       log_err(parser, key, "unknown cylinder parameter `%s'.\n",
    461         key->data.scalar.value);
    462       res = RES_BAD_ARG;
    463       goto error;
    464     }
    465     if(res != RES_OK) {
    466       log_node(parser, key);
    467       goto error;
    468     }
    469     #undef SETUP_MASK
    470   }
    471 
    472   #define CHECK_PARAM(Flag, Name)                                              \
    473     if(!(mask & BIT(Flag))) {                                                  \
    474       log_err(parser, cylinder,                                                \
    475         "the cylinder parameter `"Name"' is missing.\n");                      \
    476       res = RES_BAD_ARG;                                                       \
    477       goto error;                                                              \
    478     } (void)0
    479   CHECK_PARAM(HEIGHT, "height");
    480   CHECK_PARAM(RADIUS, "radius");
    481   #undef CHECK_PARAM
    482 
    483 exit:
    484   out_ishape->i = ishape;
    485   return res;
    486 error:
    487   if(shape) {
    488     darray_cylinder_pop_back(&parser->cylinders);
    489     ishape = SIZE_MAX;
    490   }
    491   goto exit;
    492 }
    493 
    494 static res_T
    495 parse_imported_geometry
    496   (struct solparser* parser,
    497    yaml_document_t* doc,
    498    const yaml_node_t* geom,
    499    const enum solparser_shape_type type,
    500    struct solparser_shape_imported_geometry_id* out_ishape)
    501 {
    502   enum { PATH };
    503   struct solparser_shape_imported_geometry* shape = NULL;
    504   size_t ishape = SIZE_MAX;
    505   const char* name;
    506   struct darray_impgeom* impgeoms;
    507   intptr_t i, n;
    508   int mask = 0; /* Register the parsed attributes */
    509   res_T res = RES_OK;
    510   ASSERT(doc && geom && out_ishape);
    511 
    512   switch(type) {
    513     case SOLPARSER_SHAPE_OBJ: name = "obj"; impgeoms = &parser->objs; break;
    514     case SOLPARSER_SHAPE_STL: name = "stl"; impgeoms = &parser->stls; break;
    515     default: FATAL("Unreachable code.\n"); break;
    516   }
    517 
    518   if(geom->type != YAML_MAPPING_NODE) {
    519     log_err(parser, geom, "expect a mapping of %s parameters.\n", name);
    520     res = RES_BAD_ARG;
    521     goto error;
    522   }
    523 
    524   /* Allocate an imported geometry */
    525   ishape = darray_impgeom_size_get(impgeoms);
    526   res = darray_impgeom_resize(impgeoms, ishape + 1);
    527   if(res != RES_OK) {
    528     log_err(parser, geom, "could not allocate the %s shape.\n", name);
    529     goto error;
    530   }
    531   shape = darray_impgeom_data_get(impgeoms) + ishape;
    532 
    533   n = geom->data.mapping.pairs.top - geom->data.mapping.pairs.start;
    534   FOR_EACH(i, 0, n) {
    535     yaml_node_t* key;
    536     yaml_node_t* val;
    537 
    538     key = yaml_document_get_node(doc, geom->data.mapping.pairs.start[i].key);
    539     val = yaml_document_get_node(doc, geom->data.mapping.pairs.start[i].value);
    540     if(key->type != YAML_SCALAR_NODE) {
    541       log_err(parser, key, "expect %s parameters.\n", name);
    542       res = RES_BAD_ARG;
    543       goto error;
    544     }
    545     if(!strcmp((char*)key->data.scalar.value, "path")) {
    546       if(mask & BIT(PATH)) {
    547         log_err(parser, key, "the %s path is already defined.\n", name);
    548         res = RES_BAD_ARG;
    549         goto error;
    550       }
    551       mask |= BIT(PATH);
    552       res = parse_string(parser, val, &shape->filename);
    553     } else {
    554       log_err(parser, key, "unknown %s parameter `%s'.\n",
    555         name, key->data.scalar.value);
    556       res = RES_BAD_ARG;
    557       goto error;
    558     }
    559     if(res != RES_OK) {
    560       log_node(parser, key);
    561       goto error;
    562     }
    563   }
    564 
    565   if(!(mask & BIT(PATH))) {
    566     log_err(parser, geom, "the path of the %s geometry is missing.\n", name);
    567     res = RES_BAD_ARG;
    568     goto error;
    569   }
    570 
    571 exit:
    572   out_ishape->i = ishape;
    573   return res;
    574 error:
    575   if(shape) {
    576     darray_impgeom_pop_back(impgeoms);
    577     ishape = SIZE_MAX;
    578   }
    579   goto exit;
    580 }
    581 
    582 static res_T
    583 parse_paraboloid
    584   (struct solparser* parser,
    585    yaml_document_t* doc,
    586    const yaml_node_t* paraboloid,
    587    const enum solparser_shape_type type,
    588    struct solparser_shape_paraboloid_id* out_ishape)
    589 {
    590   enum { CLIP, FOCAL, SLICES };
    591   struct solparser_shape_paraboloid* shape = NULL;
    592   struct darray_paraboloid* paraboloids;
    593   const char* name;
    594   size_t ishape = SIZE_MAX;
    595   intptr_t i, n;
    596   int mask = 0; /* Register the parsed attributes */
    597   res_T res = RES_OK;
    598   ASSERT(doc && paraboloid && out_ishape);
    599 
    600   switch(type) {
    601     case SOLPARSER_SHAPE_PARABOL:
    602       name = "parabol";
    603       paraboloids = &parser->parabols;
    604       break;
    605     case SOLPARSER_SHAPE_PARABOLIC_CYLINDER:
    606       name = "parabolic cylinder";
    607       paraboloids = &parser->parabolic_cylinders;
    608       break;
    609     default: FATAL("Unreachable code.\n"); break;
    610   }
    611 
    612   if(paraboloid->type != YAML_MAPPING_NODE) {
    613     log_err(parser, paraboloid, "expect a mapping of %s parameters.\n", name);
    614     res = RES_BAD_ARG;
    615     goto error;
    616   }
    617 
    618   /* Allocate a paraboloid shape */
    619   ishape = darray_paraboloid_size_get(paraboloids);
    620   res = darray_paraboloid_resize(paraboloids, ishape + 1);
    621   if(res != RES_OK) {
    622     log_err(parser, paraboloid, "could not allocate the %s shape.\n", name);
    623     goto error;
    624   }
    625   shape = darray_paraboloid_data_get(paraboloids) + ishape;
    626 
    627   n = paraboloid->data.mapping.pairs.top - paraboloid->data.mapping.pairs.start;
    628   FOR_EACH(i, 0, n) {
    629     yaml_node_t* key;
    630     yaml_node_t* val;
    631 
    632     key = yaml_document_get_node(doc, paraboloid->data.mapping.pairs.start[i].key);
    633     val = yaml_document_get_node(doc, paraboloid->data.mapping.pairs.start[i].value);
    634     if(key->type != YAML_SCALAR_NODE) {
    635       log_err(parser, key, "expect %s parameters.\n", name);
    636       res = RES_BAD_ARG;
    637       goto error;
    638     }
    639     #define SETUP_MASK(Flag, Name) {                                           \
    640       if(mask & BIT(Flag)) {                                                   \
    641         log_err(parser, key,                                                   \
    642           "the %s parameter `"Name"' is already defined.\n", name);            \
    643         res = RES_BAD_ARG;                                                     \
    644         goto error;                                                            \
    645       }                                                                        \
    646       mask |= BIT(Flag);                                                       \
    647     } (void)0
    648     if(!strcmp((char*)key->data.scalar.value, "clip")) {
    649       SETUP_MASK(CLIP, "clip");
    650       res = parse_clip(parser, doc, val, &shape->polyclips);
    651     } else if(!strcmp((char*)key->data.scalar.value, "focal")) {
    652       SETUP_MASK(FOCAL, "focal");
    653       res = parse_real(parser, val, nextafter(0, 1), DBL_MAX, &shape->focal);
    654     } else if(!strcmp((char*)key->data.scalar.value, "slices")) {
    655       SETUP_MASK(SLICES, "slices");
    656       res = parse_integer(parser, val, 4, 4096, &shape->nslices);
    657     } else {
    658       log_err(parser, key, "unknown %s parameter `%s'.\n",
    659         name, key->data.scalar.value);
    660       res = RES_BAD_ARG;
    661       goto error;
    662     }
    663     if(res != RES_OK) {
    664       log_node(parser, key);
    665       goto error;
    666     }
    667     #undef SETUP_MASK
    668   }
    669   #define CHECK_PARAM(Flag, Name)                                              \
    670     if(!(mask & BIT(Flag))) {                                                  \
    671       log_err(parser, paraboloid,                                              \
    672         "the %s parameter `"Name"' is missing.\n", name);                      \
    673       res = RES_BAD_ARG;                                                       \
    674       goto error;                                                              \
    675     } (void)0
    676   CHECK_PARAM(CLIP, "clip");
    677   CHECK_PARAM(FOCAL, "focal");
    678   #undef CHECK_PARAM
    679 
    680 exit:
    681   out_ishape->i = ishape;
    682   return res;
    683 error:
    684   if(shape) {
    685     darray_paraboloid_pop_back(paraboloids);
    686     ishape = SIZE_MAX;
    687   }
    688   goto exit;
    689 }
    690 
    691 static res_T
    692 parse_hyperboloid
    693   (struct solparser* parser,
    694    yaml_document_t* doc,
    695    const yaml_node_t* hyperboloid,
    696    struct solparser_shape_hyperboloid_id* out_ishape)
    697 {
    698   enum { CLIP, FOCAL, SLICES };
    699   struct solparser_shape_hyperboloid* shape = NULL;
    700   size_t ishape = SIZE_MAX;
    701   intptr_t i, n;
    702   int mask = 0; /* Register the parsed attributes */
    703   res_T res = RES_OK;
    704   ASSERT(doc && hyperboloid && out_ishape);
    705 
    706   if(hyperboloid->type != YAML_MAPPING_NODE) {
    707     log_err(parser, hyperboloid, "expect a mapping of hyperbol parameters.\n");
    708     res = RES_BAD_ARG;
    709     goto error;
    710   }
    711 
    712   /* Allocate a hyperboloid shape */
    713   ishape = darray_hyperboloid_size_get(&parser->hyperbols);
    714   res = darray_hyperboloid_resize(&parser->hyperbols, ishape + 1);
    715   if(res != RES_OK) {
    716     log_err(parser, hyperboloid, "could not allocate the hyperbol shape.\n");
    717     goto error;
    718   }
    719   shape = darray_hyperboloid_data_get(&parser->hyperbols) + ishape;
    720 
    721   n = hyperboloid->data.mapping.pairs.top - hyperboloid->data.mapping.pairs.start;
    722   FOR_EACH(i, 0, n) {
    723     yaml_node_t* key;
    724     yaml_node_t* val;
    725 
    726     key = yaml_document_get_node(doc, hyperboloid->data.mapping.pairs.start[i].key);
    727     val = yaml_document_get_node(doc, hyperboloid->data.mapping.pairs.start[i].value);
    728     if(key->type != YAML_SCALAR_NODE) {
    729       log_err(parser, key, "expect hyperbol parameters.\n");
    730       res = RES_BAD_ARG;
    731       goto error;
    732     }
    733     #define SETUP_MASK(Flag, Name) {                                           \
    734       if(mask & BIT(Flag)) {                                                   \
    735         log_err(parser, key,                                                   \
    736           "the hyperbol parameter `"Name"' is already defined.\n");            \
    737         res = RES_BAD_ARG;                                                     \
    738         goto error;                                                            \
    739       }                                                                        \
    740       mask |= BIT(Flag);                                                       \
    741     } (void)0
    742     if(!strcmp((char*) key->data.scalar.value, "clip")) {
    743       SETUP_MASK(CLIP, "clip");
    744       res = parse_clip(parser, doc, val, &shape->polyclips);
    745     } else if(!strcmp((char*)key->data.scalar.value, "focals")) {
    746       SETUP_MASK(FOCAL, "focals");
    747       res = parse_focals_description(parser, doc, val, &shape->focals);
    748     } else if(!strcmp((char*)key->data.scalar.value, "slices")) {
    749       SETUP_MASK(SLICES, "slices");
    750       res = parse_integer(parser, val, 4, 4096, &shape->nslices);
    751     } else {
    752       log_err(parser, key, "unknown hyperbol parameter `%s'.\n",
    753         key->data.scalar.value);
    754       res = RES_BAD_ARG;
    755       goto error;
    756     }
    757     if(res != RES_OK) {
    758       log_node(parser, key);
    759       goto error;
    760     }
    761     #undef SETUP_MASK
    762   }
    763   #define CHECK_PARAM(Flag, Name)                                              \
    764     if(!(mask & BIT(Flag))) {                                                  \
    765       log_err(parser, hyperboloid,                                             \
    766         "the hyperbol parameter `"Name"' is missing.\n");                      \
    767       res = RES_BAD_ARG;                                                       \
    768       goto error;                                                              \
    769     } (void)0
    770   CHECK_PARAM(CLIP, "clip");
    771   CHECK_PARAM(FOCAL, "focals");
    772   #undef CHECK_PARAM
    773 
    774 exit :
    775   out_ishape->i = ishape;
    776   return res;
    777 error:
    778   if(shape) {
    779     darray_hyperboloid_pop_back(&parser->hyperbols);
    780     ishape = SIZE_MAX;
    781   }
    782   goto exit;
    783 }
    784 
    785 static res_T
    786 parse_hemisphere
    787   (struct solparser* parser,
    788    yaml_document_t* doc,
    789    const yaml_node_t* hemisphere,
    790    struct solparser_shape_hemisphere_id* out_ishape)
    791 {
    792   enum { CLIP, RADIUS, SLICES };
    793   struct solparser_shape_hemisphere* shape = NULL;
    794   size_t ishape = SIZE_MAX;
    795   intptr_t i, n;
    796   int mask = 0; /* Register the parsed attributes */
    797   res_T res = RES_OK;
    798   ASSERT(doc && hemisphere && out_ishape);
    799 
    800   if(hemisphere->type != YAML_MAPPING_NODE) {
    801     log_err(parser, hemisphere, "expect a mapping of hemisphere parameters.\n");
    802     res = RES_BAD_ARG;
    803     goto error;
    804   }
    805 
    806   /* Allocate a hemispheric shape */
    807   ishape = darray_hemisphere_size_get(&parser->hemispheres);
    808   res = darray_hemisphere_resize(&parser->hemispheres, ishape + 1);
    809   if(res != RES_OK) {
    810     log_err(parser, hemisphere, "could not allocate the hemisphere shape.\n");
    811     goto error;
    812   }
    813   shape = darray_hemisphere_data_get(&parser->hemispheres) + ishape;
    814 
    815   n = hemisphere->data.mapping.pairs.top - hemisphere->data.mapping.pairs.start;
    816   FOR_EACH(i, 0, n) {
    817     yaml_node_t* key;
    818     yaml_node_t* val;
    819 
    820     key = yaml_document_get_node(doc, hemisphere->data.mapping.pairs.start[i].key);
    821     val = yaml_document_get_node(doc, hemisphere->data.mapping.pairs.start[i].value);
    822     if(key->type != YAML_SCALAR_NODE) {
    823       log_err(parser, key, "expect hemisphere parameters.\n");
    824       res = RES_BAD_ARG;
    825       goto error;
    826     }
    827     #define SETUP_MASK(Flag, Name) {                                           \
    828       if(mask & BIT(Flag)) {                                                   \
    829         log_err(parser, key,                                                   \
    830           "the hemisphere parameter `"Name"' is already defined.\n");          \
    831         res = RES_BAD_ARG;                                                     \
    832         goto error;                                                            \
    833       }                                                                        \
    834       mask |= BIT(Flag);                                                       \
    835     } (void)0
    836     if(!strcmp((char*)key->data.scalar.value, "clip")) {
    837       SETUP_MASK(CLIP, "clip");
    838       res = parse_clip(parser, doc, val, &shape->polyclips);
    839     }
    840     else if(!strcmp((char*)key->data.scalar.value, "radius")) {
    841       SETUP_MASK(RADIUS, "radius");
    842       res = parse_real(parser, val, nextafter(0, 1), DBL_MAX, &shape->radius);
    843     }
    844     else if(!strcmp((char*)key->data.scalar.value, "slices")) {
    845       SETUP_MASK(SLICES, "slices");
    846       res = parse_integer(parser, val, 4, 4096, &shape->nslices);
    847     }
    848     else {
    849       log_err(parser, key, "unknown hemisphere parameter `%s'.\n",
    850         key->data.scalar.value);
    851       res = RES_BAD_ARG;
    852       goto error;
    853     }
    854     if (res != RES_OK) {
    855       log_node(parser, key);
    856       goto error;
    857     }
    858   #undef SETUP_MASK
    859   }
    860   #define CHECK_PARAM(Flag, Name)                                              \
    861     if(!(mask & BIT(Flag))) {                                                  \
    862       log_err(parser, hemisphere,                                              \
    863         "the hemisphere parameter `"Name"' is missing.\n");                    \
    864       res = RES_BAD_ARG;                                                       \
    865       goto error;                                                              \
    866     } (void)0
    867   CHECK_PARAM(RADIUS, "radius");
    868   #undef CHECK_PARAM
    869 
    870 exit :
    871   out_ishape->i = ishape;
    872   return res;
    873 error:
    874   if(shape) {
    875     darray_hemisphere_pop_back(&parser->hemispheres);
    876     ishape = SIZE_MAX;
    877   }
    878   goto exit;
    879 }
    880 
    881 static res_T
    882 parse_plane
    883   (struct solparser* parser,
    884    yaml_document_t* doc,
    885    const yaml_node_t* plane,
    886    struct solparser_shape_plane_id* out_ishape)
    887 {
    888   enum { CLIP, SLICES };
    889   struct solparser_shape_plane* shape = NULL;
    890   size_t ishape = SIZE_MAX;
    891   intptr_t i, n;
    892   int mask = 0; /* Register the parsed attributes */
    893   res_T res = RES_OK;
    894   ASSERT(doc && plane && out_ishape);
    895 
    896   if(plane->type != YAML_MAPPING_NODE) {
    897     log_err(parser, plane, "expect a mapping of plane parameters.\n");
    898     res = RES_BAD_ARG;
    899     goto error;
    900   }
    901 
    902   /* Allocate a plane shape */
    903   ishape = darray_plane_size_get(&parser->planes);
    904   res = darray_plane_resize(&parser->planes, ishape + 1);
    905   if(res != RES_OK) {
    906     log_err(parser, plane, "could not allocate the plane shape.\n");
    907     goto error;
    908   }
    909   shape = darray_plane_data_get(&parser->planes) + ishape;
    910 
    911   n = plane->data.mapping.pairs.top - plane->data.mapping.pairs.start;
    912   shape->nslices = 1; /* default value */
    913   FOR_EACH(i, 0, n) {
    914     yaml_node_t* key;
    915     yaml_node_t* val;
    916 
    917     key = yaml_document_get_node(doc, plane->data.mapping.pairs.start[i].key);
    918     val = yaml_document_get_node(doc, plane->data.mapping.pairs.start[i].value);
    919     if(key->type != YAML_SCALAR_NODE) {
    920       log_err(parser, key, "expect plane parameters.\n");
    921       res = RES_BAD_ARG;
    922       goto error;
    923     }
    924     #define SETUP_MASK(Flag, Name) {                                           \
    925       if(mask & BIT(Flag)) {                                                   \
    926         log_err(parser, key,                                                   \
    927           "the plane parameter `"Name"' is already defined.\n");               \
    928         res = RES_BAD_ARG;                                                     \
    929         goto error;                                                            \
    930       }                                                                        \
    931       mask |= BIT(Flag);                                                       \
    932     } (void)0
    933 
    934     if(!strcmp((char*)key->data.scalar.value, "clip")) {
    935       SETUP_MASK(CLIP, "clip");
    936       res = parse_clip(parser, doc, val, &shape->polyclips);
    937     } else if(!strcmp((char*)key->data.scalar.value, "slices")) {
    938       SETUP_MASK(SLICES, "slices");
    939       res = parse_integer(parser, val, 1, 4096, &shape->nslices);
    940     } else {
    941       log_err(parser, key, "unknown plane parameter `%s'.\n",
    942         key->data.scalar.value);
    943       res = RES_BAD_ARG;
    944       goto error;
    945     }
    946     if(res != RES_OK) {
    947       log_node(parser, key);
    948       goto error;
    949     }
    950     #undef SETUP_MASK
    951   }
    952   if(!(mask & BIT(CLIP))) {
    953     log_err(parser, plane, "the plane parameter `clip' is missing.\n");
    954     res = RES_BAD_ARG;
    955     goto error;
    956   }
    957 
    958 exit:
    959   out_ishape->i = ishape;
    960   return res;
    961 error:
    962   if(shape) {
    963     darray_plane_pop_back(&parser->planes);
    964     ishape = SIZE_MAX;
    965   }
    966   goto exit;
    967 }
    968 
    969 static res_T
    970 parse_sphere
    971   (struct solparser* parser,
    972    yaml_document_t* doc,
    973    const yaml_node_t* sphere,
    974    struct solparser_shape_sphere_id* out_ishape)
    975 {
    976   enum { RADIUS, SLICES, STACKS };
    977   struct solparser_shape_sphere* shape = NULL;
    978   size_t ishape = SIZE_MAX;
    979   intptr_t i, n;
    980   int mask = 0; /* Register the parsed attributes */
    981   res_T res = RES_OK;
    982   ASSERT(doc && sphere && out_ishape);
    983 
    984   if(sphere->type != YAML_MAPPING_NODE) {
    985     log_err(parser, sphere, "expect a mapping of sphere parameters.\n");
    986     res = RES_BAD_ARG;
    987     goto error;
    988   }
    989 
    990   /* Allocate a shpere shape */
    991   ishape = darray_sphere_size_get(&parser->spheres);
    992   res = darray_sphere_resize(&parser->spheres, ishape + 1);
    993   if(res != RES_OK) {
    994     log_err(parser, sphere, "could not allocate the sphere shape.\n");
    995     goto error;
    996   }
    997   shape = darray_sphere_data_get(&parser->spheres) + ishape;
    998 
    999   n = sphere->data.mapping.pairs.top - sphere->data.mapping.pairs.start;
   1000   shape->nslices = 16; /* default value */
   1001   shape->nstacks = 8; /* initial default value */
   1002   FOR_EACH(i, 0, n) {
   1003     yaml_node_t* key;
   1004     yaml_node_t* val;
   1005 
   1006     key = yaml_document_get_node(doc, sphere->data.mapping.pairs.start[i].key);
   1007     val = yaml_document_get_node(doc, sphere->data.mapping.pairs.start[i].value);
   1008     if(key->type != YAML_SCALAR_NODE) {
   1009       log_err(parser, key, "expect sphere parameters.\n");
   1010       res = RES_BAD_ARG;
   1011       goto error;
   1012     }
   1013     #define SETUP_MASK(Flag, Name) {                                           \
   1014       if(mask & BIT(Flag)) {                                                   \
   1015         log_err(parser, key,                                                   \
   1016           "the sphere parameter `"Name"' is already defined.\n");              \
   1017         res = RES_BAD_ARG;                                                     \
   1018         goto error;                                                            \
   1019       }                                                                        \
   1020       mask |= BIT(Flag);                                                       \
   1021     } (void)0
   1022     if(!strcmp((char*)key->data.scalar.value, "radius")) {
   1023       SETUP_MASK(RADIUS, "radius");
   1024       res = parse_real(parser, val, nextafter(0, 1), DBL_MAX, &shape->radius);
   1025     } else if(!strcmp((char*)key->data.scalar.value, "slices")) {
   1026       SETUP_MASK(SLICES, "slices");
   1027       res = parse_integer(parser, val, 4, 4096, &shape->nslices);
   1028       if(!(mask & BIT(STACKS)))
   1029         shape->nstacks = shape->nslices / 2; /* if unset, new default value */
   1030     } else if(!strcmp((char*)key->data.scalar.value, "stacks")) {
   1031       SETUP_MASK(STACKS, "stacks");
   1032       res = parse_integer(parser, val, 2, 4096, &shape->nstacks);
   1033     } else {
   1034       log_err(parser, key, "unknown sphere parameter `%s'.\n",
   1035         key->data.scalar.value);
   1036       res = RES_BAD_ARG;
   1037       goto error;
   1038     }
   1039     if(res != RES_OK) {
   1040       log_node(parser, key);
   1041       goto error;
   1042     }
   1043     #undef SETUP_MASK
   1044   }
   1045 
   1046   #define CHECK_PARAM(Flag, Name)                                              \
   1047     if(!(mask & BIT(Flag))) {                                                  \
   1048       log_err(parser, sphere,                                                  \
   1049         "the sphere parameter `"Name"' is missing.\n");                        \
   1050       res = RES_BAD_ARG;                                                       \
   1051       goto error;                                                              \
   1052     } (void)0
   1053   CHECK_PARAM(RADIUS, "radius");
   1054   #undef CHECK_PARAM
   1055 
   1056 exit:
   1057   out_ishape->i = ishape;
   1058   return res;
   1059 error:
   1060   if(shape) {
   1061     darray_sphere_pop_back(&parser->spheres);
   1062     ishape = SIZE_MAX;
   1063   }
   1064   goto exit;
   1065 }
   1066 
   1067 
   1068 static res_T
   1069 parse_object
   1070   (struct solparser* parser,
   1071    yaml_document_t* doc,
   1072    yaml_node_t* object,
   1073    struct solparser_object_id* out_iobj)
   1074 {
   1075   enum { MATERIAL, SHAPE, TRANSFORM };
   1076   struct solparser_object* obj = NULL;
   1077   struct solparser_shape* shape = NULL;
   1078   size_t iobj = SIZE_MAX;
   1079   size_t ishape = SIZE_MAX;
   1080   intptr_t i, n;
   1081   int mask = 0; /* Register the parsed attributes */
   1082   res_T res = RES_OK;
   1083   ASSERT(doc && object && out_iobj);
   1084 
   1085   if(object->type != YAML_MAPPING_NODE) {
   1086     log_err(parser, object, "expect an object definition.\n");
   1087     res = RES_BAD_ARG;
   1088     goto error;
   1089   }
   1090 
   1091   /* Allocate an object */
   1092   iobj = darray_object_size_get(&parser->objects);
   1093   res = darray_object_resize(&parser->objects, iobj + 1);
   1094   if(res != RES_OK) {
   1095     log_err(parser, object, "could not allocate the object.\n");
   1096     goto error;
   1097   }
   1098   obj = darray_object_data_get(&parser->objects) + iobj;
   1099 
   1100   /* Allocate a shape */
   1101   ishape = darray_shape_size_get(&parser->shapes);
   1102   res = darray_shape_resize(&parser->shapes, ishape + 1);
   1103   if(res != RES_OK) {
   1104     log_err(parser, object, "could not allocate the object shape.\n");
   1105     goto error;
   1106   }
   1107   shape = darray_shape_data_get(&parser->shapes) + ishape;
   1108   obj->shape.i = ishape;
   1109 
   1110   /* Setup default object transformation */
   1111   d3_splat(obj->translation, 0);
   1112   d3_splat(obj->rotation, 0);
   1113 
   1114   n = object->data.mapping.pairs.top - object->data.mapping.pairs.start;
   1115   FOR_EACH(i, 0, n) {
   1116     yaml_node_t* key;
   1117     yaml_node_t* val;
   1118 
   1119     key = yaml_document_get_node(doc, object->data.mapping.pairs.start[i].key);
   1120     val = yaml_document_get_node(doc, object->data.mapping.pairs.start[i].value);
   1121     if(key->type != YAML_SCALAR_NODE) {
   1122       log_err(parser, key, "expect an object parameter.\n");
   1123       res = RES_BAD_ARG;
   1124       goto error;
   1125     }
   1126 
   1127     #define SETUP_MASK(Flag, Name) {                                           \
   1128       if(mask & BIT(Flag)) {                                                   \
   1129         log_err(parser, key,                                                   \
   1130           "the object "Name" is already defined.\n");                          \
   1131         res = RES_BAD_ARG;                                                     \
   1132         goto error;                                                            \
   1133       }                                                                        \
   1134       mask |= BIT(Flag);                                                       \
   1135     } (void)0
   1136     if(!strcmp((char*)key->data.scalar.value, "material")) {
   1137       SETUP_MASK(MATERIAL, "material");
   1138       res = parse_material(parser, doc, val, &obj->mtl2);
   1139     } else if(!strcmp((char*)key->data.scalar.value, "cuboid")) {
   1140       SETUP_MASK(SHAPE, "shape");
   1141       shape->type = SOLPARSER_SHAPE_CUBOID;
   1142       res = parse_cuboid(parser, doc, val, &shape->data.cuboid);
   1143     } else if(!strcmp((char*)key->data.scalar.value, "cylinder")) {
   1144       SETUP_MASK(SHAPE, "shape");
   1145       shape->type = SOLPARSER_SHAPE_CYLINDER;
   1146       res = parse_cylinder(parser, doc, val, &shape->data.cylinder);
   1147     } else if(!strcmp((char*)key->data.scalar.value, "obj")) {
   1148       SETUP_MASK(SHAPE, "shape");
   1149       shape->type = SOLPARSER_SHAPE_OBJ;
   1150       res = parse_imported_geometry
   1151         (parser, doc, val, shape->type, &shape->data.obj);
   1152     } else if(!strcmp((char*)key->data.scalar.value, "parabol")) {
   1153       SETUP_MASK(SHAPE, "shape");
   1154       shape->type = SOLPARSER_SHAPE_PARABOL;
   1155       res = parse_paraboloid
   1156         (parser, doc, val, shape->type, &shape->data.parabol);
   1157     } else if(!strcmp((char*)key->data.scalar.value, "parabolic-cylinder")) {
   1158       SETUP_MASK(SHAPE, "shape");
   1159       shape->type = SOLPARSER_SHAPE_PARABOLIC_CYLINDER;
   1160       res = parse_paraboloid
   1161         (parser, doc, val, shape->type, &shape->data.parabolic_cylinder);
   1162     } else if(!strcmp((char*) key->data.scalar.value, "hyperbol")) {
   1163       SETUP_MASK(SHAPE, "shape");
   1164       shape->type = SOLPARSER_SHAPE_HYPERBOL;
   1165       res = parse_hyperboloid(parser, doc, val, &shape->data.hyperbol);
   1166     }
   1167     else if(!strcmp((char*)key->data.scalar.value, "hemisphere")) {
   1168       SETUP_MASK(SHAPE, "shape");
   1169       shape->type = SOLPARSER_SHAPE_HEMISPHERE;
   1170       res = parse_hemisphere(parser, doc, val, &shape->data.hemisphere);
   1171     } else if(!strcmp((char*)key->data.scalar.value, "plane")) {
   1172       SETUP_MASK(SHAPE, "shape");
   1173       shape->type = SOLPARSER_SHAPE_PLANE;
   1174       res = parse_plane(parser, doc, val, &shape->data.plane);
   1175     } else if(!strcmp((char*)key->data.scalar.value, "sphere")) {
   1176       SETUP_MASK(SHAPE, "shape");
   1177       shape->type = SOLPARSER_SHAPE_SPHERE;
   1178       res = parse_sphere(parser, doc, val, &shape->data.sphere);
   1179     } else if(!strcmp((char*)key->data.scalar.value, "stl")) {
   1180       SETUP_MASK(SHAPE, "shape");
   1181       shape->type = SOLPARSER_SHAPE_STL;
   1182       res = parse_imported_geometry
   1183         (parser, doc, val, shape->type, &shape->data.stl);
   1184     } else if(!strcmp((char*)key->data.scalar.value, "transform")) {
   1185       SETUP_MASK(TRANSFORM, "transform");
   1186       res = parse_transform(parser, doc, val, obj->translation, obj->rotation);
   1187     } else {
   1188       log_err(parser, key, "unknown object parameter `%s'.\n",
   1189         key->data.scalar.value);
   1190       res = RES_BAD_ARG;
   1191       goto error;
   1192     }
   1193     if(res != RES_OK) {
   1194       log_node(parser, key);
   1195       goto error;
   1196     }
   1197     #undef SETUP_MASK
   1198   }
   1199 
   1200   #define CHECK_PARAM(Flag, Name)                                              \
   1201     if(!(mask & BIT(Flag))) {                                                  \
   1202       log_err(parser, object, "the object "Name" is missing.\n");              \
   1203       res = RES_BAD_ARG;                                                       \
   1204       goto error;                                                              \
   1205     } (void)0
   1206   CHECK_PARAM(MATERIAL, "material");
   1207   CHECK_PARAM(SHAPE, "shape");
   1208   #undef CHECK_PARAM
   1209 
   1210 exit:
   1211   out_iobj->i = iobj;
   1212   return res;
   1213 error:
   1214   if(obj) {
   1215     if(shape) darray_shape_pop_back(&parser->shapes);
   1216     darray_object_pop_back(&parser->objects);
   1217     obj = NULL;
   1218   }
   1219   goto exit;
   1220 }
   1221 
   1222 /*******************************************************************************
   1223  * Local functions
   1224  ******************************************************************************/
   1225 res_T
   1226 parse_focals_description
   1227   (struct solparser* parser,
   1228    yaml_document_t* doc,
   1229    const yaml_node_t* desc,
   1230    struct solparser_hyperboloid_focals* focals)
   1231 {
   1232   enum { REAL, IMAGE };
   1233   intptr_t i, n;
   1234   int mask = 0; /* Register the parsed attributes */
   1235   res_T res = RES_OK;
   1236   ASSERT(doc && desc && focals);
   1237 
   1238   if(desc->type != YAML_MAPPING_NODE) {
   1239     log_err(parser, desc, "expect a mapping of focal parameters.\n");
   1240     res = RES_BAD_ARG;
   1241     goto error;
   1242   }
   1243 
   1244   n = desc->data.mapping.pairs.top - desc->data.mapping.pairs.start;
   1245   FOR_EACH(i, 0, n) {
   1246     yaml_node_t* key;
   1247     yaml_node_t* val;
   1248 
   1249     key = yaml_document_get_node(doc, desc->data.mapping.pairs.start[i].key);
   1250     val = yaml_document_get_node(doc, desc->data.mapping.pairs.start[i].value);
   1251     if(key->type != YAML_SCALAR_NODE) {
   1252       log_err(parser, key, "expect focal parameters.\n");
   1253       res = RES_BAD_ARG;
   1254       goto error;
   1255     }
   1256     #define SETUP_MASK(Flag, Name) {                                           \
   1257       if(mask & BIT(Flag)) {                                                   \
   1258         log_err(parser, key,                                                   \
   1259           "the focal parameter `"Name"' is already defined.\n");               \
   1260         res = RES_BAD_ARG;                                                     \
   1261         goto error;                                                            \
   1262       }                                                                        \
   1263       mask |= BIT(Flag);                                                       \
   1264     } (void)0
   1265     if(!strcmp((char*) key->data.scalar.value, "real")) {
   1266       SETUP_MASK(REAL, "real");
   1267       res = parse_real(parser, val, nextafter(0, 1), DBL_MAX, &focals->real);
   1268     } else if(!strcmp((char*) key->data.scalar.value, "image")) {
   1269       SETUP_MASK(IMAGE, "image");
   1270       res = parse_real(parser, val, nextafter(0, 1), DBL_MAX, &focals->image);
   1271     }
   1272     if(res != RES_OK) {
   1273       log_node(parser, key);
   1274       goto error;
   1275     }
   1276   }
   1277   #undef SETUP_MASK
   1278   #define CHECK_PARAM(Flag, Name)                                              \
   1279     if(!(mask & BIT(Flag))) {                                                  \
   1280       log_err(parser, desc,                                                    \
   1281         "the focal parameter `"Name"' is missing.\n");                         \
   1282       res = RES_BAD_ARG;                                                       \
   1283       goto error;                                                              \
   1284     } (void)0
   1285   CHECK_PARAM(REAL, "real");
   1286   CHECK_PARAM(IMAGE, "image");
   1287   #undef CHECK_PARAM
   1288 
   1289 exit:
   1290   return res;
   1291 error:
   1292   goto exit;
   1293 }
   1294 
   1295 res_T
   1296 parse_geometry
   1297   (struct solparser* parser,
   1298    yaml_document_t* doc,
   1299    yaml_node_t* geometry,
   1300    struct solparser_geometry_id* out_isolgeom)
   1301 {
   1302   struct solparser_geometry* solgeom = NULL;
   1303   size_t* pisolgeom;
   1304   size_t isolgeom = SIZE_MAX;
   1305   intptr_t i, n;
   1306   res_T res = RES_OK;
   1307   ASSERT(doc && geometry && out_isolgeom);
   1308 
   1309   if(geometry->type != YAML_SEQUENCE_NODE) {
   1310     log_err(parser, geometry, "expect a list of objects.\n");
   1311     res = RES_BAD_ARG;
   1312     goto error;
   1313   }
   1314 
   1315   /* Check whether or not the YAML descriptor alias an already created Solstice
   1316    * geometry */
   1317   pisolgeom = htable_yaml2sols_find(&parser->yaml2geoms, &geometry);
   1318   if(pisolgeom) {
   1319     isolgeom = *pisolgeom;
   1320     goto exit;
   1321   }
   1322 
   1323   /* Allocate the geometry */
   1324   isolgeom = darray_geometry_size_get(&parser->geometries);
   1325   res = darray_geometry_resize(&parser->geometries, isolgeom + 1);
   1326   if(res != RES_OK) {
   1327     log_err(parser, geometry, "could not allocate the geometry.\n");
   1328     goto error;
   1329   }
   1330   solgeom = darray_geometry_data_get(&parser->geometries) + isolgeom;
   1331 
   1332   n = geometry->data.sequence.items.top - geometry->data.sequence.items.start;
   1333   res = darray_object_id_resize(&solgeom->objects, (size_t)n);
   1334   if(res != RES_OK) {
   1335     log_err(parser, geometry, "could not allocate the objects list.\n");
   1336     goto error;
   1337   }
   1338 
   1339   FOR_EACH(i, 0, n) {
   1340     struct solparser_object_id* obj_id;
   1341     yaml_node_t* obj;
   1342 
   1343     obj_id = darray_object_id_data_get(&solgeom->objects) + i;
   1344     obj = yaml_document_get_node(doc, geometry->data.sequence.items.start[i]);
   1345     res = parse_object(parser, doc, obj, obj_id);
   1346     if(res != RES_OK) goto error;
   1347   }
   1348 
   1349   /* Cache the geometry */
   1350   res = htable_yaml2sols_set(&parser->yaml2geoms, &geometry, &isolgeom);
   1351   if(res != RES_OK) {
   1352     log_err(parser, geometry, "could not register the geometry.\n");
   1353     goto error;
   1354   }
   1355 
   1356 exit:
   1357   out_isolgeom->i = isolgeom;
   1358   return res;
   1359 error:
   1360   if(solgeom) {
   1361     darray_geometry_pop_back(&parser->geometries);
   1362     isolgeom = SIZE_MAX;
   1363   }
   1364   goto exit;
   1365 }
   1366 
   1367 
   1368