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_material.c (23923B)


      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 <math.h> /* nextafter */
     21 
     22 /*******************************************************************************
     23  * Helper functions
     24  ******************************************************************************/
     25 static res_T
     26 parse_microfacet
     27   (struct solparser* parser,
     28    const yaml_node_t* microfacet,
     29    enum solparser_microfacet_distribution* distrib)
     30 {
     31   res_T res = RES_OK;
     32   ASSERT(microfacet && distrib);
     33 
     34   if(microfacet->type != YAML_SCALAR_NODE) {
     35     log_err(parser, microfacet,
     36       "expect the name of a microfacet distribution.\n");
     37     res = RES_BAD_ARG;
     38     goto error;
     39   }
     40 
     41   if(!strcmp((char*)microfacet->data.scalar.value, "BECKMANN")) {
     42     *distrib = SOLPARSER_MICROFACET_BECKMANN;
     43   } else if(!strcmp((char*)microfacet->data.scalar.value, "PILLBOX")) {
     44     *distrib = SOLPARSER_MICROFACET_PILLBOX;
     45   } else {
     46     log_err(parser, microfacet, "unknown microfacet distribution `%s'.\n",
     47       microfacet->data.scalar.value);
     48     res = RES_BAD_ARG;
     49     goto error;
     50   }
     51 exit:
     52   return res;
     53 error:
     54   goto exit;
     55 }
     56 
     57 static res_T
     58 parse_material_dielectric
     59   (struct solparser* parser,
     60    yaml_document_t* doc,
     61    const yaml_node_t* dielec,
     62    struct solparser_material_dielectric_id* out_imtl)
     63 {
     64   enum { MEDIUM_I, MEDIUM_T, NORMAL_MAP };
     65   struct solparser_material_dielectric* mtl = NULL;
     66   size_t imtl = SIZE_MAX;
     67   int mask = 0; /* Register the parsed attributes */
     68   intptr_t i, n;
     69   res_T res = RES_OK;
     70   ASSERT(doc && dielec && out_imtl);
     71 
     72   if(dielec->type != YAML_MAPPING_NODE) {
     73     log_err(parser, dielec,
     74       "expect a mapping of dielec material attributes.\n");
     75     res = RES_BAD_ARG;
     76     goto error;
     77   }
     78 
     79   /* Allocate the dielec material */
     80   imtl = darray_dielectric_size_get(&parser->dielectrics);
     81   res = darray_dielectric_resize(&parser->dielectrics, imtl + 1);
     82   if(res != RES_OK) {
     83     log_err(parser, dielec,
     84       "could not allocate the dielec material.\n");
     85     goto error;
     86   }
     87   mtl = darray_dielectric_data_get(&parser->dielectrics) + imtl;
     88 
     89   n = dielec->data.mapping.pairs.top - dielec->data.mapping.pairs.start;
     90   FOR_EACH(i, 0, n) {
     91     yaml_node_t* key;
     92     yaml_node_t* val;
     93 
     94     key = yaml_document_get_node(doc, dielec->data.mapping.pairs.start[i].key);
     95     val = yaml_document_get_node(doc, dielec->data.mapping.pairs.start[i].value);
     96     if(key->type != YAML_SCALAR_NODE) {
     97       log_err(parser, key, "expect a dielec material parameter.\n");
     98       res = RES_BAD_ARG;
     99       goto error;
    100     }
    101     #define SETUP_MASK(Flag, Name) {                                           \
    102       if(mask & BIT(Flag)) {                                                   \
    103         log_err(parser, key,                                                   \
    104           "the "Name" of the dielectric material is already defined.\n");      \
    105         res = RES_BAD_ARG;                                                     \
    106         goto error;                                                            \
    107       }                                                                        \
    108       mask |= BIT(Flag);                                                       \
    109     } (void)0
    110     if(!strcmp((char*)key->data.scalar.value, "normal_map")) {
    111       SETUP_MASK(NORMAL_MAP, "normal_map");
    112       res = parse_image(parser, doc, val, &mtl->normal_map);
    113     } else if(!strcmp((char*)key->data.scalar.value, "medium_i")) {
    114       SETUP_MASK(MEDIUM_I, "medium_i");
    115       res = parse_medium(parser, doc, val, &mtl->medium_i);
    116     } else if(!strcmp((char*)key->data.scalar.value, "medium_t")) {
    117       SETUP_MASK(MEDIUM_T, "medium_t");
    118       res = parse_medium(parser, doc, val, &mtl->medium_t);
    119     } else {
    120       log_err(parser, key, "unknown dielectric parameter `%s'.\n",
    121         key->data.scalar.value);
    122       res = RES_BAD_ARG;
    123       goto error;
    124     }
    125     if(res != RES_OK) {
    126       log_node(parser, key);
    127       goto error;
    128     }
    129     #undef SETUP_MASK
    130   }
    131 
    132   #define CHECK_PARAM(Flag, Name)                                              \
    133     if(!(mask & BIT(Flag))) {                                                  \
    134       log_err(parser, dielec,                                                  \
    135         "the "Name" of the dielectric material is missing.\n");                \
    136       res = RES_BAD_ARG;                                                       \
    137       goto error;                                                              \
    138     } (void)0
    139   CHECK_PARAM(MEDIUM_I, "medium_i");
    140   CHECK_PARAM(MEDIUM_T, "medium_t");
    141   #undef CHECK_PARAM
    142 
    143 exit:
    144   out_imtl->i = imtl;
    145   return res;
    146 error:
    147   if(imtl) {
    148     darray_dielectric_pop_back(&parser->dielectrics);
    149     imtl = SIZE_MAX;
    150   }
    151   goto exit;
    152 }
    153 
    154 static res_T
    155 parse_material_matte
    156   (struct solparser* parser,
    157    yaml_document_t* doc,
    158    const yaml_node_t* matte,
    159    struct solparser_material_matte_id* out_imtl)
    160 {
    161   enum { NORMAL_MAP, REFLECTIVITY };
    162   struct solparser_material_matte* mtl = NULL;
    163   size_t imtl = SIZE_MAX;
    164   intptr_t i, n;
    165   int mask = 0; /* Register the parsed attributes */
    166   res_T res = RES_OK;
    167   ASSERT(doc && matte && out_imtl);
    168 
    169   if(matte->type != YAML_MAPPING_NODE) {
    170     log_err(parser, matte, "expect a mapping of matte material parameters.\n");
    171     res = RES_BAD_ARG;
    172     goto error;
    173   }
    174 
    175   /* Allocate the matte material */
    176   imtl = darray_matte_size_get(&parser->mattes);
    177   res = darray_matte_resize(&parser->mattes, imtl + 1);
    178   if(res != RES_OK) {
    179     log_err(parser, matte, "could not allocate the matte material.\n");
    180     goto error;
    181   }
    182   mtl = darray_matte_data_get(&parser->mattes) + imtl;
    183 
    184   n = matte->data.mapping.pairs.top - matte->data.mapping.pairs.start;
    185   FOR_EACH(i, 0, n) {
    186     yaml_node_t* key;
    187     yaml_node_t* val;
    188 
    189     key = yaml_document_get_node(doc, matte->data.mapping.pairs.start[i].key);
    190     val = yaml_document_get_node(doc, matte->data.mapping.pairs.start[i].value);
    191     if(key->type != YAML_SCALAR_NODE) {
    192       log_err(parser, key, "expect a matte material parameter.\n");
    193       res = RES_BAD_ARG;
    194       goto error;
    195     }
    196 
    197     #define SETUP_MASK(Flag, Name) {                                           \
    198       if(mask & BIT(Flag)) {                                                   \
    199         log_err(parser, key,                                                   \
    200           "the "Name" of the matte material is already defined.\n");           \
    201         res = RES_BAD_ARG;                                                     \
    202         goto error;                                                            \
    203       }                                                                        \
    204       mask |= BIT(Flag);                                                       \
    205     } (void)0
    206     if(!strcmp((char*)key->data.scalar.value, "normal_map")) {
    207       SETUP_MASK(NORMAL_MAP, "normal_map");
    208       res = parse_image(parser, doc, val, &mtl->normal_map);
    209     } else if(!strcmp((char*)key->data.scalar.value, "reflectivity")) {
    210       SETUP_MASK(REFLECTIVITY, "reflectivity");
    211       res = parse_mtl_data(parser, doc, val, 0, 1, &mtl->reflectivity);
    212     } else {
    213       log_err(parser, key, "unknown matte parameter `%s'.\n",
    214         key->data.scalar.value);
    215       res = RES_BAD_ARG;
    216       goto error;
    217     }
    218     if(res != RES_OK) {
    219       log_node(parser, key);
    220       goto error;
    221     }
    222     #undef SETUP_MASK
    223   }
    224 
    225   if(!(mask & BIT(REFLECTIVITY))) {
    226     log_err(parser, matte, "the matte reflectivity is missing.\n");
    227     res = RES_BAD_ARG;
    228     goto error;
    229   }
    230 
    231 exit:
    232   out_imtl->i = imtl;
    233   return res;
    234 error:
    235   if(mtl) {
    236     darray_matte_pop_back(&parser->mattes);
    237     imtl = SIZE_MAX;
    238   }
    239   goto exit;
    240 }
    241 
    242 static res_T
    243 parse_material_mirror
    244   (struct solparser* parser,
    245    yaml_document_t* doc,
    246    const yaml_node_t* mirror,
    247    struct solparser_material_mirror_id* out_imtl)
    248 {
    249   enum { MICROFACET, NORMAL_MAP, REFLECTIVITY, SLOPE_ERROR };
    250   struct solparser_material_mirror* mtl = NULL;
    251   size_t imtl = SIZE_MAX;
    252   int mask = 0; /* Register the parsed attributes */
    253   intptr_t i, n;
    254   res_T res = RES_OK;
    255   ASSERT(doc && mirror && out_imtl);
    256 
    257   if(mirror->type != YAML_MAPPING_NODE) {
    258     log_err(parser, mirror,
    259       "expect a mapping of mirror material attributes.\n");
    260     res = RES_BAD_ARG;
    261     goto error;
    262   }
    263 
    264   /* Allocate the mirror material */
    265   imtl = darray_mirror_size_get(&parser->mirrors);
    266   res = darray_mirror_resize(&parser->mirrors, imtl + 1);
    267   if(res != RES_OK) {
    268     log_err(parser, mirror, "could not allocate the mirror material.\n");
    269     goto error;
    270   }
    271   mtl = darray_mirror_data_get(&parser->mirrors) + imtl;
    272 
    273   n = mirror->data.mapping.pairs.top - mirror->data.mapping.pairs.start;
    274   FOR_EACH(i, 0, n) {
    275     yaml_node_t* key;
    276     yaml_node_t* val;
    277 
    278     key = yaml_document_get_node(doc, mirror->data.mapping.pairs.start[i].key);
    279     val = yaml_document_get_node(doc, mirror->data.mapping.pairs.start[i].value);
    280     if(key->type != YAML_SCALAR_NODE) {
    281       log_err(parser, key, "expect a mirror material parameter.\n");
    282       res = RES_BAD_ARG;
    283       goto error;
    284     }
    285 
    286     #define SETUP_MASK(Flag, Name) {                                           \
    287       if(mask & BIT(Flag)) {                                                   \
    288         log_err(parser, key,                                                   \
    289           "the "Name" of the mirror material is already defined.\n");          \
    290         res = RES_BAD_ARG;                                                     \
    291         goto error;                                                            \
    292       }                                                                        \
    293       mask |= BIT(Flag);                                                       \
    294     } (void)0
    295     if(!strcmp((char*)key->data.scalar.value, "microfacet")) {
    296       SETUP_MASK(MICROFACET, "microfacet");
    297       res = parse_microfacet(parser, val, &mtl->ufacet_distrib);
    298     } else if(!strcmp((char*)key->data.scalar.value, "normal_map")) {
    299       SETUP_MASK(NORMAL_MAP, "normal_map");
    300       res = parse_image(parser, doc, val, &mtl->normal_map);
    301     } else if(!strcmp((char*)key->data.scalar.value, "reflectivity")) {
    302       SETUP_MASK(REFLECTIVITY, "reflectivity");
    303       res = parse_mtl_data(parser, doc, val, 0, 1, &mtl->reflectivity);
    304     } else if(!strcmp((char*)key->data.scalar.value, "slope_error")) {
    305       SETUP_MASK(SLOPE_ERROR, "slope_error");
    306       res = parse_mtl_data(parser, doc, val, 0, 1, &mtl->slope_error);
    307     } else {
    308       log_err(parser, key, "unknown mirror attribute `%s'.\n",
    309         key->data.scalar.value);
    310       res = RES_BAD_ARG;
    311       goto error;
    312     }
    313     if(res != RES_OK) {
    314       log_node(parser, key);
    315       goto error;
    316     }
    317     #undef SETUP_MASK
    318   }
    319 
    320   #define CHECK_PARAM(Flag, Name)                                              \
    321     if(!(mask & BIT(Flag))) {                                                  \
    322       log_err(parser, mirror, "the mirror "Name" is missing.\n");              \
    323       res = RES_BAD_ARG;                                                       \
    324       goto error;                                                              \
    325     } (void)0
    326   CHECK_PARAM(REFLECTIVITY, "reflectivity");
    327   CHECK_PARAM(SLOPE_ERROR, "slope_error");
    328   #undef CHECK_PARAM
    329 
    330 exit:
    331   out_imtl->i = imtl;
    332   return res;
    333 error:
    334   if(mtl) {
    335     darray_mirror_pop_back(&parser->mirrors);
    336     imtl = SIZE_MAX;
    337   }
    338   goto exit;
    339 }
    340 
    341 static res_T
    342 parse_material_thin_dielectric
    343   (struct solparser* parser,
    344    yaml_document_t* doc,
    345    yaml_node_t* thin,
    346    struct solparser_material_thin_dielectric_id* out_imtl)
    347 {
    348   enum { MEDIUM_I, MEDIUM_T, NORMAL_MAP, THICKNESS };
    349   struct solparser_material_thin_dielectric* mtl = NULL;
    350   size_t imtl = SIZE_MAX;
    351   int mask = 0; /* Register the parsed attributes */
    352   intptr_t i, n;
    353   res_T res = RES_OK;
    354   ASSERT(doc && thin && out_imtl);
    355 
    356   if(thin->type != YAML_MAPPING_NODE) {
    357     log_err(parser, thin,
    358       "expect a mapping of thin dielectric material attributes.\n");
    359     res = RES_BAD_ARG;
    360     goto error;
    361   }
    362 
    363   /* Allocate the thin dielectric material */
    364   imtl = darray_thin_dielectric_size_get(&parser->thin_dielectrics);
    365   res = darray_thin_dielectric_resize(&parser->thin_dielectrics, imtl + 1);
    366   if(res != RES_OK) {
    367     log_err(parser, thin,
    368       "could not allocate the thin dielectric material.\n");
    369     goto error;
    370   }
    371   mtl = darray_thin_dielectric_data_get(&parser->thin_dielectrics) + imtl;
    372 
    373   n = thin->data.mapping.pairs.top - thin->data.mapping.pairs.start;
    374   FOR_EACH(i, 0, n) {
    375     yaml_node_t* key;
    376     yaml_node_t* val;
    377 
    378     key = yaml_document_get_node(doc, thin->data.mapping.pairs.start[i].key);
    379     val = yaml_document_get_node(doc, thin->data.mapping.pairs.start[i].value);
    380     if(key->type != YAML_SCALAR_NODE) {
    381       log_err(parser, key, "expect a thin dielectric material parameter.\n");
    382       res = RES_BAD_ARG;
    383       goto error;
    384     }
    385 
    386     #define SETUP_MASK(Flag, Name) {                                           \
    387       if(mask & BIT(Flag)) {                                                   \
    388         log_err(parser, key,                                                   \
    389           "the "Name" of the thin dielectric material is already defined.\n"); \
    390         res = RES_BAD_ARG;                                                     \
    391         goto error;                                                            \
    392       }                                                                        \
    393       mask |= BIT(Flag);                                                       \
    394     } (void)0
    395     if(!strcmp((char*)key->data.scalar.value, "normal_map")) {
    396       SETUP_MASK(NORMAL_MAP, "normal_map");
    397       res = parse_image(parser, doc, val, &mtl->normal_map);
    398     } else if(!strcmp((char*)key->data.scalar.value, "medium_i")) {
    399       SETUP_MASK(MEDIUM_I, "medium_i");
    400       res = parse_medium(parser, doc, val, &mtl->medium_i);
    401     } else if(!strcmp((char*)key->data.scalar.value, "medium_t")) {
    402       SETUP_MASK(MEDIUM_T, "medium_t");
    403       res = parse_medium(parser, doc, val, &mtl->medium_t);
    404     } else if(!strcmp((char*)key->data.scalar.value, "thickness")) {
    405       SETUP_MASK(THICKNESS, "thickness");
    406       res = parse_real(parser, val, 0, DBL_MAX, &mtl->thickness);
    407     } else {
    408       log_err(parser, key, "unknown thin dielectric parameter `%s'.\n",
    409         key->data.scalar.value);
    410       res = RES_BAD_ARG;
    411       goto error;
    412     }
    413     if(res != RES_OK) {
    414       log_node(parser, key);
    415       goto error;
    416     }
    417     #undef SETUP_MASK
    418   }
    419 
    420   #define CHECK_PARAM(Flag, Name)                                              \
    421     if(!(mask & BIT(Flag))) {                                                  \
    422       log_err(parser, thin,                                                    \
    423         "the "Name" of the thin dielectric material is missing.\n");           \
    424       res = RES_BAD_ARG;                                                       \
    425       goto error;                                                              \
    426     } (void)0
    427   CHECK_PARAM(MEDIUM_I, "medium_i");
    428   CHECK_PARAM(MEDIUM_T, "medium_t");
    429   CHECK_PARAM(THICKNESS, "thickness");
    430   #undef CHECK_PARAM
    431 
    432 exit:
    433   out_imtl->i = imtl;
    434   return res;
    435 error:
    436   if(mtl) {
    437     darray_thin_dielectric_pop_back(&parser->thin_dielectrics);
    438     imtl = SIZE_MAX;
    439   }
    440   goto exit;
    441 }
    442 
    443 static res_T
    444 parse_material_virtual(struct solparser* parser, yaml_node_t* virtual)
    445 {
    446   res_T res = RES_OK;
    447   ASSERT(virtual);
    448 
    449   if(virtual->type != YAML_SCALAR_NODE
    450   || ((char*)virtual->data.scalar.value)[0] != '\0') {
    451     log_err(parser, virtual,
    452       "virtual materials can have a null scalar value only.\n");
    453     res = RES_BAD_ARG;
    454     goto error;
    455   }
    456 
    457 exit:
    458   return res;
    459 error:
    460   goto exit;
    461 }
    462 
    463 static res_T
    464 parse_material_descriptor
    465   (struct solparser* parser,
    466    yaml_document_t* doc,
    467    yaml_node_t* desc,
    468    struct solparser_material_id* out_imtl)
    469 {
    470   enum { DESCRIPTOR };
    471   struct solparser_material* mtl = NULL;
    472   intptr_t i, n;
    473   int mask = 0; /* Register the parsed attributes */
    474   size_t* pimtl;
    475   size_t imtl = SIZE_MAX;
    476   res_T res = RES_OK;
    477   ASSERT(doc && desc && out_imtl);
    478 
    479   /* Check whether or not the YAML descriptor alias an already created Solstice
    480    * material */
    481   pimtl = htable_yaml2sols_find(&parser->yaml2mtls, &desc);
    482   if(pimtl) {
    483     imtl = *pimtl;
    484     goto exit;
    485   }
    486 
    487   /* Allocate the solstice material */
    488   imtl = darray_material_size_get(&parser->mtls);
    489   res = darray_material_resize(&parser->mtls, imtl + 1);
    490   if(res != RES_OK) {
    491     log_err(parser, desc, "could not allocate the material descriptor.\n");
    492     goto error;
    493   }
    494   mtl = darray_material_data_get(&parser->mtls) + imtl;
    495 
    496   if(desc->type != YAML_MAPPING_NODE) {
    497     log_err(parser, desc, "expect a material descriptor.\n");
    498     res = RES_BAD_ARG;
    499     goto error;
    500   }
    501 
    502   n = desc->data.mapping.pairs.top - desc->data.mapping.pairs.start;
    503   FOR_EACH(i, 0, n) {
    504     yaml_node_t* key;
    505     yaml_node_t* val;
    506 
    507     key = yaml_document_get_node(doc, desc->data.mapping.pairs.start[i].key);
    508     val = yaml_document_get_node(doc, desc->data.mapping.pairs.start[i].value);
    509     if(key->type != YAML_SCALAR_NODE) {
    510       log_err(parser, key, "expect a material name.\n");
    511       res = RES_BAD_ARG;
    512       goto error;
    513     }
    514 
    515     #define SETUP_MASK(Flag, Name) {                                           \
    516       if(mask & BIT(Flag)) {                                                   \
    517         log_err(parser, key, "the material "Name" is already defined.\n");     \
    518         res = RES_BAD_ARG;                                                     \
    519         goto error;                                                            \
    520       }                                                                        \
    521       mask |= BIT(Flag);                                                       \
    522     } (void)0
    523     if(!strcmp((char*)key->data.scalar.value, "dielectric")) {
    524       SETUP_MASK(DESCRIPTOR, "descriptor");
    525       mtl->type = SOLPARSER_MATERIAL_DIELECTRIC;
    526       res = parse_material_dielectric(parser, doc, val, &mtl->data.dielectric);
    527     } else if(!strcmp((char*)key->data.scalar.value, "matte")) {
    528       SETUP_MASK(DESCRIPTOR, "descriptor");
    529       mtl->type = SOLPARSER_MATERIAL_MATTE;
    530       res = parse_material_matte(parser, doc, val, &mtl->data.matte);
    531     } else if(!strcmp((char*)key->data.scalar.value, "mirror")) {
    532       SETUP_MASK(DESCRIPTOR, "descriptor");
    533       mtl->type = SOLPARSER_MATERIAL_MIRROR;
    534       res = parse_material_mirror(parser, doc, val, &mtl->data.mirror);
    535     } else if(!strcmp((char*)key->data.scalar.value, "thin_dielectric")) {
    536       SETUP_MASK(DESCRIPTOR, "descriptor");
    537       mtl->type = SOLPARSER_MATERIAL_THIN_DIELECTRIC;
    538       res = parse_material_thin_dielectric
    539         (parser, doc, val, &mtl->data.thin_dielectric);
    540     } else if(!strcmp((char*)key->data.scalar.value, "virtual")) {
    541       SETUP_MASK(DESCRIPTOR, "descriptor");
    542       mtl->type = SOLPARSER_MATERIAL_VIRTUAL;
    543       res = parse_material_virtual(parser, val);
    544     } else {
    545       log_err(parser, key, "unknown material descriptor `%s'.\n",
    546         key->data.scalar.value);
    547       res = RES_BAD_ARG;
    548       goto error;
    549     }
    550     if(res != RES_OK) {
    551       log_node(parser, key);
    552       goto error;
    553     }
    554     #undef SETUP_MASK
    555   }
    556 
    557   if(!(mask & BIT(DESCRIPTOR))) {
    558     log_err(parser, desc, "the material descriptor is missing.\n");
    559     res = RES_BAD_ARG;
    560     goto error;
    561   }
    562 
    563   /* Cache the material */
    564   res = htable_yaml2sols_set(&parser->yaml2mtls, &desc, &imtl);
    565   if(res != RES_OK) {
    566     log_err(parser, desc, "could not register the material.\n");
    567     goto error;
    568   }
    569 
    570 exit:
    571   out_imtl->i = imtl;
    572   return res;
    573 error:
    574   if(mtl) {
    575     darray_material_pop_back(&parser->mtls);
    576     imtl = SIZE_MAX;
    577   }
    578   goto exit;
    579 }
    580 
    581 /*******************************************************************************
    582  * Local functions
    583  ******************************************************************************/
    584 res_T
    585 parse_material
    586   (struct solparser* parser,
    587    yaml_document_t* doc,
    588    yaml_node_t* mtl,
    589    struct solparser_material_double_sided_id* out_imtl2)
    590 {
    591   enum { FRONT, BACK };
    592   struct solparser_material_double_sided* mtl2 = NULL;
    593   size_t imtl2 = SIZE_MAX;
    594   intptr_t i, n;
    595   int mask = 0; /* Register the parsed attributes */
    596   res_T res = RES_OK;
    597   ASSERT(doc && mtl && out_imtl2);
    598 
    599   if(mtl->type != YAML_MAPPING_NODE) {
    600     log_err(parser, mtl, "expect a material definition.\n");
    601     res = RES_BAD_ARG;
    602     goto error;
    603   }
    604 
    605   /* Allocate the double sided material */
    606   imtl2 = darray_material2_size_get(&parser->mtls2);
    607   res = darray_material2_resize(&parser->mtls2, imtl2 + 1);
    608   if(res != RES_OK) {
    609     log_err(parser, mtl, "could not allocate the material.\n");
    610     goto error;
    611   }
    612   mtl2 = darray_material2_data_get(&parser->mtls2) + imtl2;
    613 
    614   n = mtl->data.mapping.pairs.top - mtl->data.mapping.pairs.start;
    615   FOR_EACH(i, 0, n) {
    616     yaml_node_t* key;
    617     yaml_node_t* val;
    618 
    619     key = yaml_document_get_node(doc, mtl->data.mapping.pairs.start[i].key);
    620     val = yaml_document_get_node(doc, mtl->data.mapping.pairs.start[i].value);
    621     if(key->type != YAML_SCALAR_NODE) {
    622       log_err(parser, key,
    623         "expect a material descriptor or a double sided material.\n");
    624       res = RES_BAD_ARG;
    625       goto error;
    626     }
    627 
    628     #define SETUP_MASK(Flag, Name) {                                           \
    629       if(mask & BIT(Flag)) {                                                   \
    630         log_err(parser, key,                                                   \
    631           "the "Name" material descriptor is already defined.\n");             \
    632         res = RES_BAD_ARG;                                                     \
    633         goto error;                                                            \
    634       }                                                                        \
    635       mask |= BIT(Flag);                                                       \
    636     } (void)0
    637     if(!strcmp((char*)key->data.scalar.value, "front")) {
    638       SETUP_MASK(FRONT, "front");
    639       res = parse_material_descriptor(parser, doc, val, &mtl2->front);
    640     } else if(!strcmp((char*)key->data.scalar.value, "back")) {
    641       SETUP_MASK(BACK, "back");
    642       res = parse_material_descriptor(parser, doc, val, &mtl2->back);
    643     } else {
    644       SETUP_MASK(FRONT, "front");
    645       SETUP_MASK(BACK, "back");
    646       res = parse_material_descriptor(parser, doc, mtl, &mtl2->front);
    647       mtl2->back = mtl2->front;
    648       if(res != RES_OK) goto error; /* Discard log_node */
    649     }
    650     if(res != RES_OK) {
    651       log_node(parser, key);
    652       goto error;
    653     }
    654     #undef SETUP_MASK
    655   }
    656 
    657   #define CHECK_PARAM(Flag, Name)                                             \
    658     if(!(mask & BIT(Flag))) {                                                 \
    659       log_err(parser, mtl, "the "Name" material descriptor is missing.\n");   \
    660       res = RES_BAD_ARG;                                                      \
    661       goto error;                                                             \
    662     } (void)0
    663   CHECK_PARAM(FRONT, "front");
    664   CHECK_PARAM(BACK, "back");
    665   #undef CHECK_PARAM
    666 
    667 exit:
    668   out_imtl2->i = imtl2;
    669   return res;
    670 error:
    671   if(mtl2) {
    672     darray_material2_pop_back(&parser->mtls2);
    673     imtl2 = SIZE_MAX;
    674   }
    675   goto exit;
    676 }
    677