htrdr

Solving radiative transfer in heterogeneous media
git clone git://git.meso-star.fr/htrdr.git
Log | Files | Refs | README | LICENSE

htrdr_args.c (21000B)


      1 /* Copyright (C) 2018-2019, 2022-2025 Centre National de la Recherche Scientifique
      2  * Copyright (C) 2020-2022 Institut Mines Télécom Albi-Carmaux
      3  * Copyright (C) 2022-2025 Institut Pierre-Simon Laplace
      4  * Copyright (C) 2022-2025 Institut de Physique du Globe de Paris
      5  * Copyright (C) 2018-2025 |Méso|Star> (contact@meso-star.com)
      6  * Copyright (C) 2022-2025 Observatoire de Paris
      7  * Copyright (C) 2022-2025 Université de Reims Champagne-Ardenne
      8  * Copyright (C) 2022-2025 Université de Versaille Saint-Quentin
      9  * Copyright (C) 2018-2019, 2022-2025 Université Paul Sabatier
     10  *
     11  * This program is free software: you can redistribute it and/or modify
     12  * it under the terms of the GNU General Public License as published by
     13  * the Free Software Foundation, either version 3 of the License, or
     14  * (at your option) any later version.
     15  *
     16  * This program is distributed in the hope that it will be useful,
     17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     19  * GNU General Public License for more details.
     20  *
     21  * You should have received a copy of the GNU General Public License
     22  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     23 
     24 #define _POSIX_C_SOURCE 2 /* strtok_r support */
     25 
     26 #include "core/htrdr.h"
     27 #include "core/htrdr_args.h"
     28 #include "core/htrdr_version.h"
     29 
     30 #include <star/scam.h>
     31 
     32 #include <rsys/cstr.h>
     33 #include <rsys/double3.h>
     34 #include <rsys/str.h>
     35 
     36 #include <string.h>
     37 
     38 /*******************************************************************************
     39  * Helper functions
     40  ******************************************************************************/
     41 static INLINE res_T
     42 parse_doubleX(const char* str, double* val, const size_t sz)
     43 {
     44   size_t len;
     45   res_T res = RES_OK;
     46   ASSERT(str && val);
     47   res = cstr_to_list_double(str, ',', val, &len, sz);
     48   if(res == RES_OK && len != sz) res = RES_BAD_ARG;
     49   return res;
     50 }
     51 
     52 static INLINE res_T
     53 parse_definition(const char* str, unsigned val[2])
     54 {
     55   size_t len;
     56   res_T res = RES_OK;
     57   ASSERT(str && val);
     58   res = cstr_to_list_uint(str, 'x', val, &len, 2);
     59   if(res != RES_OK) return res;
     60   if(len != 2) return RES_BAD_ARG;
     61   if(val[0] > 16384 || val[1] > 16384) return RES_BAD_ARG;
     62   return RES_OK;
     63 }
     64 
     65 static res_T
     66 parse_fov(const char* str, double* out_fov)
     67 {
     68   double fov;
     69   res_T res = RES_OK;
     70   ASSERT(str && out_fov);
     71 
     72   res = cstr_to_double(str, &fov);
     73   if(res != RES_OK) {
     74     fprintf(stderr, "Invalid field of view `%s'.\n", str);
     75     return RES_BAD_ARG;
     76   }
     77   if(fov <= HTRDR_ARGS_CAMERA_PERSPECTIVE_FOV_EXCLUSIVE_MIN
     78   || fov >= HTRDR_ARGS_CAMERA_PERSPECTIVE_FOV_EXCLUSIVE_MAX) {
     79     fprintf(stderr, "The field of view %g is not in ]%g, %g[.\n", fov,
     80       HTRDR_ARGS_CAMERA_PERSPECTIVE_FOV_EXCLUSIVE_MIN,
     81       HTRDR_ARGS_CAMERA_PERSPECTIVE_FOV_EXCLUSIVE_MAX);
     82     return RES_BAD_ARG;
     83   }
     84   *out_fov = fov;
     85   return RES_OK;
     86 }
     87 
     88 static res_T
     89 parse_focal_length(const char* str, double* out_length)
     90 {
     91   double length;
     92   res_T res = RES_OK;
     93   ASSERT(str && out_length);
     94 
     95   res = cstr_to_double(str, &length);
     96   if(res != RES_OK) {
     97     fprintf(stderr, "Invalid focal length `%s'.\n", str);
     98     return RES_BAD_ARG;
     99   }
    100   if(length <= 0) {
    101     fprintf(stderr, "Invalid negative or null focal length %g.\n", length);
    102     return RES_BAD_ARG;
    103   }
    104   *out_length = length;
    105   return RES_OK;
    106 }
    107 
    108 static res_T
    109 parse_lens_radius(const char* str, double* out_radius)
    110 {
    111   double radius;
    112   res_T res = RES_OK;
    113   ASSERT(str && out_radius);
    114 
    115   res = cstr_to_double(str, &radius);
    116   if(res != RES_OK) {
    117     fprintf(stderr, "Invalid lens radius `%s'.\n", str);
    118     return RES_BAD_ARG;
    119   }
    120   if(radius < 0) {
    121     fprintf(stderr, "Invalid negative lens radius %g.\n", radius);
    122     return RES_BAD_ARG;
    123   }
    124   *out_radius = radius;
    125   return RES_OK;
    126 }
    127 
    128 static res_T
    129 parse_focal_dst(const char* str, double* out_dst)
    130 {
    131   double dst;
    132   res_T res = RES_OK;
    133   ASSERT(str && out_dst);
    134 
    135   res = cstr_to_double(str, &dst);
    136   if(res != RES_OK) {
    137     fprintf(stderr, "Invalid focal distance `%s'.\n", str);
    138     return RES_BAD_ARG;
    139   }
    140   if(dst <= 0) {
    141     fprintf(stderr, "Invalid negative or null focal disrtance %g.\n", dst);
    142     return RES_BAD_ARG;
    143   }
    144   *out_dst = dst;
    145   return RES_OK;
    146 }
    147 
    148 static res_T
    149 parse_image_plane_height(const char* str, double* out_height)
    150 {
    151   double height;
    152   res_T res = RES_OK;
    153   ASSERT(str && out_height);
    154 
    155   res = cstr_to_double(str, &height);
    156   if(res != RES_OK) {
    157     fprintf(stderr,
    158       "Invalid height `%s' of the image plane of the orthographic camera.\n",
    159       str);
    160     return RES_BAD_ARG;
    161   }
    162   if(height <= 0) {
    163     fprintf(stderr,
    164       "Invalid negative or null height of the image plane "
    165       "of the orthographic camera.\n");
    166     return RES_BAD_ARG;
    167   }
    168   *out_height = height;
    169   return RES_OK;
    170 }
    171 
    172 static res_T
    173 parse_image_parameter(const char* str, void* args)
    174 {
    175   char buf[128];
    176   struct htrdr_args_image* img = args;
    177   char* key;
    178   char* val;
    179   char* ctx;
    180   res_T res = RES_OK;
    181   ASSERT(str && img);
    182 
    183   if(strlen(str) >= sizeof(buf) -1/*NULL char*/) {
    184     fprintf(stderr,
    185       "Could not duplicate the image option string `%s'.\n", str);
    186     res = RES_MEM_ERR;
    187     goto error;
    188   }
    189   strncpy(buf, str, sizeof(buf));
    190 
    191   key = strtok_r(buf, "=", &ctx);
    192   val = strtok_r(NULL, "", &ctx);
    193 
    194   if(!val) {
    195     fprintf(stderr, "Missing a value to the image option `%s'.\n", key);
    196     res = RES_BAD_ARG;
    197     goto error;
    198   }
    199 
    200   #define PARSE(Name, Func)                                                    \
    201     res = Func;                                                                \
    202     if(res != RES_OK) {                                                        \
    203       fprintf(stderr, "Invalid image "Name" `%s'.\n", val);                    \
    204       goto error;                                                              \
    205     } (void)0
    206   if(!strcmp(key, "def")) {
    207     PARSE("definition", parse_definition(val, img->definition));
    208   } else if(!strcmp(key, "spp")) {
    209     PARSE("#samples per pixel", cstr_to_uint(val, &img->spp));
    210   } else {
    211     fprintf(stderr, "Invalid image parameter `%s'.\n", key);
    212     res = RES_BAD_ARG;
    213     goto error;
    214   }
    215   #undef PARSE
    216 
    217   if(!img->definition[0] || !img->definition[1]) {
    218     fprintf(stderr, "The image definition cannot be null.\n");
    219     res = RES_BAD_ARG;
    220     goto error;
    221   }
    222   if(!img->spp) {
    223     fprintf(stderr, "The number of samples per pixel cannot be null.\n");
    224     res = RES_BAD_ARG;
    225     goto error;
    226   }
    227 
    228 exit:
    229   return res;
    230 error:
    231   goto exit;
    232 }
    233 
    234 static res_T
    235 parse_camera_perspective_parameter(const char* str, void* args)
    236 {
    237   char buf[128];
    238   struct htrdr_args_camera_perspective* cam = args;
    239   char* key;
    240   char* val;
    241   char* ctx;
    242   res_T res = RES_OK;
    243   ASSERT(cam && str);
    244 
    245   if(strlen(str) >= sizeof(buf) -1/*NULL char*/) {
    246     fprintf(stderr,
    247       "Could not duplicate the perspective camera option string `%s'.\n", str);
    248     res = RES_MEM_ERR;
    249     goto error;
    250   }
    251   strncpy(buf, str, sizeof(buf));
    252 
    253   key = strtok_r(buf, "=", &ctx);
    254   val = strtok_r(NULL, "", &ctx);
    255 
    256   if(!val) {
    257     fprintf(stderr,
    258       "Missing value to the perspective camera parameter `%s'.\n", key);
    259     res = RES_BAD_ARG;
    260     goto error;
    261   }
    262 
    263   #define PARSE(Name, Func) {                                                  \
    264     if(RES_OK != (res = Func)) {                                               \
    265       fprintf(stderr, "Invalid perspective camera "Name" `%s'.\n", val);       \
    266       goto error;                                                              \
    267     }                                                                          \
    268   } (void)0
    269   if(!strcmp(key, "pos")) {
    270     PARSE("position", parse_doubleX(val, cam->position, 3));
    271   } else if(!strcmp(key, "tgt")) {
    272     PARSE("target", parse_doubleX(val, cam->target, 3));
    273   } else if(!strcmp(key, "up")) {
    274     PARSE("up vector", parse_doubleX(val, cam->up, 3));
    275   } else if(!strcmp(key, "fov")) {
    276     PARSE("field-of-view", parse_fov(val, &cam->fov_y));
    277     cam->focal_length = -1; /* Overwrite the focal_length */
    278   } else if(!strcmp(key, "focal-length")) {
    279     PARSE("focal-length", parse_focal_length(val, &cam->focal_length));
    280     cam->fov_y = -1; /* Overwrite the fov */
    281   } else if(!strcmp(key, "lens-radius")) {
    282     PARSE("lens radius", parse_lens_radius(val, &cam->lens_radius));
    283   } else if(!strcmp(key, "focal-dst")) {
    284     PARSE("focal distance", parse_focal_dst(val, &cam->focal_dst));
    285   } else {
    286     fprintf(stderr, "Invalid perspective camera parameter `%s'.\n", key);
    287     res = RES_BAD_ARG;
    288     goto error;
    289   }
    290   #undef PARSE
    291 exit:
    292   return res;
    293 error:
    294   goto exit;
    295 }
    296 
    297 static res_T
    298 parse_camera_orthographic_parameter(const char* str, void* args)
    299 {
    300   char buf[128];
    301   struct htrdr_args_camera_orthographic* cam = args;
    302   char* key;
    303   char* val;
    304   char* ctx;
    305   res_T res = RES_OK;
    306   ASSERT(cam && str);
    307 
    308   if(strlen(str) >= sizeof(buf) - 1/*NULL char*/) {
    309     fprintf(stderr,
    310       "Could not duplicate the orthographic camera option string `%s'.\n", str);
    311     res = RES_MEM_ERR;
    312     goto error;
    313   }
    314   strncpy(buf, str, sizeof(buf));
    315 
    316   key = strtok_r(buf, "=", &ctx);
    317   val = strtok_r(NULL, "", &ctx);
    318 
    319   if(!val) {
    320     fprintf(stderr,
    321       "Missing value to the orthographic camera parameter `%s'.\n", key);
    322     res = RES_BAD_ARG;
    323     goto error;
    324   }
    325   #define PARSE(Name, Func) {                                                  \
    326     if(RES_OK != (res = Func)) {                                               \
    327       fprintf(stderr, "Invalid orthographic camera "Name" `%s'.\n", val);      \
    328       goto error;                                                              \
    329     }                                                                          \
    330   } (void)0
    331   if(!strcmp(key, "pos")) {
    332     PARSE("position", parse_doubleX(val, cam->position, 3));
    333   } else if(!strcmp(key, "tgt")) {
    334     PARSE("target", parse_doubleX(val, cam->target, 3));
    335   } else if(!strcmp(key, "up")) {
    336     PARSE("up vector", parse_doubleX(val, cam->up, 3));
    337   } else if(!strcmp(key, "height")) {
    338     PARSE("image plane height", parse_image_plane_height(val, &cam->height));
    339   } else {
    340     fprintf(stderr, "Invalid orthographic camera parameter `%s'.\n", key);
    341     res = RES_BAD_ARG;
    342     goto error;
    343   }
    344   #undef PARSE
    345 exit:
    346   return res;
    347 error:
    348   goto exit;
    349 }
    350 
    351 static res_T
    352 parse_rectangle_parameter(const char* str, void* args)
    353 {
    354   char buf[128];
    355   struct htrdr_args_rectangle* rect = args;
    356   char* key;
    357   char* val;
    358   char* ctx;
    359   res_T res = RES_OK;
    360   ASSERT(rect && str);
    361 
    362   if(strlen(str) >= sizeof(buf) -1/*NULL char*/) {
    363     fprintf(stderr,
    364       "Could not duplicate the rectangle option string `%s'.\n", str);
    365     res = RES_MEM_ERR;
    366     goto error;
    367   }
    368   strncpy(buf, str, sizeof(buf));
    369 
    370   key = strtok_r(buf, "=", &ctx);
    371   val = strtok_r(NULL, "", &ctx);
    372 
    373   if(!val) {
    374     fprintf(stderr, "Missing value to the rectangle option `%s'.\n", key);
    375     res = RES_BAD_ARG;
    376     goto error;
    377   }
    378 
    379   #define PARSE(Name, Func) {                                                  \
    380     if(RES_OK != (res = Func)) {                                               \
    381       fprintf(stderr, "Invalid rectangle "Name" `%s'.\n", val);                \
    382       goto error;                                                              \
    383     }                                                                          \
    384   } (void)0
    385   if(!strcmp(key, "pos")) {
    386     PARSE("position", parse_doubleX(val, rect->position, 3));
    387   } else if(!strcmp(key, "tgt")) {
    388     PARSE("target", parse_doubleX(val, rect->target, 3));
    389   } else if(!strcmp(key, "up")) {
    390     PARSE("up vector", parse_doubleX(val, rect->up, 3));
    391   } else if(!strcmp(key, "sz")) {
    392     PARSE("size", parse_doubleX(val, rect->size, 2));
    393   } else {
    394     fprintf(stderr, "Invalid rectangle parameter `%s'.\n", key);
    395     res = RES_BAD_ARG;
    396     goto error;
    397   }
    398   #undef PARSE
    399 exit:
    400   return res;
    401 error:
    402   goto exit;
    403 }
    404 
    405 static res_T
    406 parse_spectral_range(const char* str, double wlen_range[2])
    407 {
    408   double range[2];
    409   size_t len;
    410   res_T res = RES_OK;
    411   ASSERT(wlen_range && str);
    412 
    413   res = cstr_to_list_double(str, ',', range, &len, 2);
    414   if(res == RES_OK && len != 2) res = RES_BAD_ARG;
    415   if(res == RES_OK && range[0] > range[1]) res = RES_BAD_ARG;
    416   if(res != RES_OK) {
    417     fprintf(stderr, "Invalid spectral range `%s'.\n", str);
    418     goto error;
    419   }
    420   wlen_range[0] = range[0];
    421   wlen_range[1] = range[1];
    422 
    423 exit:
    424   return res;
    425 error:
    426   goto exit;
    427 }
    428 
    429 static res_T
    430 parse_spectral_parameter(const char* str, void* ptr)
    431 {
    432   char buf[128];
    433   struct htrdr_args_spectral* args = ptr;
    434   char* key;
    435   char* val;
    436   char* ctx;
    437   res_T res = RES_OK;
    438   ASSERT(args && str);
    439 
    440   if(strlen(str) >= sizeof(buf) -1/*NULL char*/) {
    441     fprintf(stderr,
    442       "Could not duplicate the spectral option string `%s'.\n", str);
    443     res = RES_MEM_ERR;
    444     goto error;
    445   }
    446   strncpy(buf, str, sizeof(buf));
    447 
    448   key = strtok_r(buf, "=", &ctx);
    449   val = strtok_r(NULL, "",  &ctx);
    450 
    451   if(!strcmp(key, "cie_xyz")) {
    452     args->spectral_type = HTRDR_SPECTRAL_SW_CIE_XYZ;
    453     args->wlen_range[0] = HTRDR_RAN_WLEN_CIE_XYZ_RANGE_DEFAULT[0];
    454     args->wlen_range[1] = HTRDR_RAN_WLEN_CIE_XYZ_RANGE_DEFAULT[1];
    455   } else {
    456     if(!val) {
    457       fprintf(stderr, "Missing value to the spectral option `%s'.\n", key);
    458       res = RES_BAD_ARG;
    459       goto error;
    460     }
    461     if(!strcmp(key, "sw")) {
    462       args->spectral_type = HTRDR_SPECTRAL_SW;
    463       res = parse_spectral_range(val, args->wlen_range);
    464       if(res != RES_OK) goto error;
    465     } else if(!strcmp(key, "lw")) {
    466       args->spectral_type = HTRDR_SPECTRAL_LW;
    467       res = parse_spectral_range(val, args->wlen_range);
    468       if(res != RES_OK) goto error;
    469     } else if(!strcmp(key, "Tref")) {
    470       res = cstr_to_double(val, &args->ref_temperature);
    471       if(res == RES_OK && args->ref_temperature < 0) res = RES_BAD_ARG;
    472       if(res != RES_OK) {
    473         fprintf(stderr, "Invalid reference temperature Tref=%s.\n", val);
    474         goto error;
    475       }
    476     } else {
    477       fprintf(stderr, "Invalid spectral parameter `%s'.\n", key);
    478       res = RES_BAD_ARG;
    479       goto error;
    480     }
    481   }
    482 
    483 exit:
    484   return res;
    485 error:
    486   goto exit;
    487 }
    488 
    489 static res_T
    490 parse_geometry_parameter(const char* str, void* ptr)
    491 {
    492   struct str buf;
    493   struct htrdr_args_geometry* geom = ptr;
    494   char* key;
    495   char* val;
    496   char* ctx;
    497   res_T res = RES_OK;
    498   ASSERT(geom && str);
    499 
    500   str_init(NULL, &buf);
    501   res = str_set(&buf, str);
    502   if(res != RES_OK) {
    503     fprintf(stderr,
    504       "Could not duplicate the geometry option string `%s' -- %s.\n",
    505       str, res_to_cstr(res));
    506     goto error;
    507   }
    508 
    509   key = strtok_r(str_get(&buf), "=", &ctx);
    510   val = strtok_r(NULL, "",  &ctx);
    511 
    512   if(!val) {
    513     fprintf(stderr, "Missing value to the geometry parameter `%s'.\n", key);
    514     res = RES_BAD_ARG;
    515     goto error;
    516   }
    517 
    518   #define SET_VALUE(Key, Val, Str) {                                           \
    519     const size_t len = strlen(Val) + 1;                                        \
    520     Str = mem_alloc(len);                                                      \
    521     if(!Str) {                                                                 \
    522       fprintf(stderr,                                                          \
    523         "Could not duplicate the value `%s' of the geometry parameter `%s'.\n",\
    524         (Val), (Key));                                                         \
    525         res = RES_MEM_ERR;                                                     \
    526       goto error;                                                              \
    527     }                                                                          \
    528     strncpy(Str, Val, len);                                                    \
    529   } (void)0
    530   if(!strcmp(key, "obj")) {
    531     SET_VALUE(key, val, geom->path_obj);
    532   } else if(!strcmp(key, "mats")) {
    533     SET_VALUE(key, val, geom->path_mats);
    534   } else {
    535     fprintf(stderr, "Invalid geometry parameter `%s'.\n", key);
    536     res = RES_BAD_ARG;
    537     goto error;
    538   }
    539   #undef SET_VALUE
    540 
    541 exit:
    542   str_release(&buf);
    543   return res;
    544 error:
    545   htrdr_args_geometry_free(geom);
    546   goto exit;
    547 }
    548 
    549 /*******************************************************************************
    550  * Exported functions
    551  ******************************************************************************/
    552 res_T
    553 htrdr_args_camera_perspective_parse
    554   (struct htrdr_args_camera_perspective* cam,
    555    const char* str)
    556 {
    557   res_T res = RES_OK;
    558 
    559   if(!cam || !str) {
    560     res = RES_BAD_ARG;
    561     goto error;
    562   }
    563 
    564   *cam = HTRDR_ARGS_CAMERA_PERSPECTIVE_DEFAULT;
    565 
    566   res = cstr_parse_list(str, ':', parse_camera_perspective_parameter, cam);
    567   if(res != RES_OK) goto error;
    568 
    569   if(cam->focal_length < 0) {
    570     ASSERT(cam->fov_y > 0);
    571     res = scam_field_of_view_to_focal_length
    572       (cam->lens_radius, MDEG2RAD(cam->fov_y), &cam->focal_length);
    573     if(res != RES_OK) {
    574       fprintf(stderr,
    575         "Cannot compute the focal length from the lens radius %g "
    576         "and the field of view %g -- %s.\n",
    577         cam->lens_radius,
    578         cam->fov_y,
    579         res_to_cstr(res));
    580       goto error;
    581     }
    582 
    583   } else if(cam->fov_y < 0) {
    584     ASSERT(cam->focal_length > 0);
    585     res = scam_focal_length_to_field_of_view
    586       (cam->lens_radius, cam->focal_length, &cam->fov_y);
    587     if(res != RES_OK) {
    588       fprintf(stderr,
    589         "Cannot compute the field of view from the lens radius %g "
    590         "and the focal length %g -- %s.\n",
    591         cam->lens_radius,
    592         cam->focal_length,
    593         res_to_cstr(res));
    594       goto error;
    595     }
    596     cam->fov_y = MRAD2DEG(cam->fov_y);
    597     if(cam->fov_y <= HTRDR_ARGS_CAMERA_PERSPECTIVE_FOV_EXCLUSIVE_MIN
    598     || cam->fov_y >= HTRDR_ARGS_CAMERA_PERSPECTIVE_FOV_EXCLUSIVE_MAX) {
    599       fprintf(stderr,
    600         "Invalid focal length %g regarding the lens radius %g. "
    601         "The corresponding field of view %g is not in ]%g, %g[ degrees.\n",
    602         cam->focal_length,
    603         cam->lens_radius,
    604         cam->fov_y,
    605         HTRDR_ARGS_CAMERA_PERSPECTIVE_FOV_EXCLUSIVE_MIN,
    606         HTRDR_ARGS_CAMERA_PERSPECTIVE_FOV_EXCLUSIVE_MAX);
    607       res = RES_BAD_ARG;
    608       goto error;
    609     }
    610   }
    611 
    612 exit:
    613   return res;
    614 error:
    615   goto exit;
    616 }
    617 
    618 res_T
    619 htrdr_args_camera_perspective_check
    620   (const struct htrdr_args_camera_perspective* cam)
    621 {
    622   if(!cam) return RES_BAD_ARG;
    623 
    624   /* Invalid Fov */
    625   if(cam->fov_y <= HTRDR_ARGS_CAMERA_PERSPECTIVE_FOV_EXCLUSIVE_MIN
    626   || cam->fov_y >= HTRDR_ARGS_CAMERA_PERSPECTIVE_FOV_EXCLUSIVE_MAX) {
    627     /* Is the fov defined by the focal length? */
    628     if(cam->focal_length < 0) return RES_BAD_ARG;
    629   }
    630 
    631   /* Invalid focal length */
    632   if(cam->focal_length <= 0) {
    633     /* Is the focal length defined by the fov? */
    634     if(cam->fov_y < 0) return RES_BAD_ARG;
    635   }
    636 
    637   /* Invalid lens radius */
    638   if(cam->lens_radius < 0)
    639     return RES_BAD_ARG;
    640 
    641   /* Invalid focal distance */
    642   if(cam->lens_radius > 0/*!pinhole*/ && cam->focal_dst < 0)
    643     return RES_BAD_ARG;
    644 
    645   return RES_OK;
    646 }
    647 
    648 res_T
    649 htrdr_args_camera_orthographic_parse
    650   (struct htrdr_args_camera_orthographic* cam,
    651    const char* str)
    652 {
    653   if(!cam || !str) return RES_BAD_ARG;
    654   return cstr_parse_list(str, ':', parse_camera_orthographic_parameter, cam);
    655 }
    656 
    657 res_T
    658 htrdr_args_camera_orthographic_check
    659   (const struct htrdr_args_camera_orthographic* cam)
    660 {
    661   if(!cam) return RES_BAD_ARG;
    662 
    663   /* Invalid image plane height */
    664   if(cam->height <= 0) return RES_BAD_ARG;
    665 
    666   return RES_OK;
    667 }
    668 
    669 res_T
    670 htrdr_args_rectangle_parse(struct htrdr_args_rectangle* rect, const char* str)
    671 {
    672   if(!rect || !str) return RES_BAD_ARG;
    673   return cstr_parse_list(str, ':', parse_rectangle_parameter, rect);
    674 }
    675 
    676 res_T
    677 htrdr_args_image_parse(struct htrdr_args_image* img, const char* str)
    678 {
    679   if(!img || !str) return RES_BAD_ARG;
    680   return cstr_parse_list(str, ':', parse_image_parameter, img);
    681 }
    682 
    683 res_T
    684 htrdr_args_image_check(const struct htrdr_args_image* img)
    685 {
    686   if(!img) return RES_BAD_ARG;
    687 
    688   /* Invalid definition */
    689   if(!img->definition[0] || !img->definition[1])
    690     return RES_BAD_ARG;
    691 
    692   /* Invalid number of samples per pixel */
    693   if(!img->spp)
    694     return RES_BAD_ARG;
    695 
    696   return RES_OK;
    697 }
    698 
    699 res_T
    700 htrdr_args_spectral_parse(struct htrdr_args_spectral* spectral, const char* str)
    701 {
    702   if(!spectral || !str) return RES_BAD_ARG;
    703   return cstr_parse_list(str, ':', parse_spectral_parameter, spectral);
    704 }
    705 
    706 void
    707 htrdr_args_geometry_free(struct htrdr_args_geometry* geom)
    708 {
    709   ASSERT(geom);
    710   if(geom->path_obj) mem_rm(geom->path_obj);
    711   if(geom->path_mats) mem_rm(geom->path_mats);
    712   *geom = HTRDR_ARGS_GEOMETRY_NULL;
    713 }
    714 
    715 res_T
    716 htrdr_args_geometry_parse(struct htrdr_args_geometry* geom, const char* str)
    717 {
    718   res_T res = RES_OK;
    719 
    720   if(!geom || !str) {
    721     res = RES_BAD_ARG;
    722     goto error;
    723   }
    724 
    725   htrdr_args_geometry_free(geom);
    726 
    727   res = cstr_parse_list(str, ':', parse_geometry_parameter, geom);
    728   if(res != RES_OK) goto error;
    729 
    730   if(!geom->path_obj) {
    731     fprintf(stderr, "Missing the `obj' geometry parameter.\n");
    732     res = RES_BAD_ARG;
    733     goto error;
    734   }
    735 
    736   if(!geom->path_mats) {
    737     fprintf(stderr, "Missing the `mats' geometry parameter.\n");
    738     res = RES_BAD_ARG;
    739     goto error;
    740   }
    741 
    742 exit:
    743   return res;
    744 error:
    745   if(geom) htrdr_args_geometry_free(geom);
    746   goto exit;
    747 }
    748