star-3dut

Generate meshes of simple geometric shapes
git clone git://git.meso-star.fr/star-3dut.git
Log | Files | Refs | README | LICENSE

s3dut_sphere.c (14105B)


      1 /* Copyright (C) 2016, 2017, 2020, 2021, 2023 |Méso|Star> (contact@meso-star.com)
      2  *
      3  * This program is free software: you can redistribute it and/or modify
      4  * it under the terms of the GNU General Public License as published by
      5  * the Free Software Foundation, either version 3 of the License, or
      6  * (at your option) any later version.
      7  *
      8  * This program is distributed in the hope that it will be useful,
      9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
     11  * GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License
     14  * along with this program. If not, see <http://www.gnu.org/licenses/>. */
     15 
     16 #include "s3dut.h"
     17 #include "s3dut_mesh.h"
     18 
     19 /*******************************************************************************
     20  * Helper functions
     21  ******************************************************************************/
     22 static res_T
     23 setup_sphere_coords
     24   (struct mem_allocator* allocator,
     25    double** coords_ptr,
     26    const double radius,
     27    const double z_range[2],
     28    const unsigned nslices, /* # subdivisions around the Z axis */
     29    const unsigned nstacks, /* # subdivisions along the Z axis */
     30    const int close_ends)
     31 {
     32   enum { SIN, COS };
     33   double* coords;
     34   struct darray_double sincos_theta;
     35   struct darray_double sincos_phi;
     36   double step_theta;
     37   const int top_truncated = z_range && z_range[1] < +radius;
     38   const int bottom_truncated = z_range && z_range[0] > -radius;
     39   const unsigned nb_truncated = (unsigned)(top_truncated + bottom_truncated);
     40   const int close_top = top_truncated && (close_ends & S3DUT_CAP_POS_Z);
     41   const int close_bottom = bottom_truncated && (close_ends & S3DUT_CAP_NEG_Z);
     42   const unsigned nrings = nstacks - 1 + nb_truncated;
     43   const double phi_z_min
     44     = bottom_truncated ? asin(CLAMP(z_range[0] / radius, -1, 1)) : -PI / 2;
     45   const double phi_z_max
     46     = top_truncated ? asin(CLAMP(z_range[1] / radius, -1, 1)) : +PI / 2;
     47   double step_phi
     48     = (phi_z_max - phi_z_min) / (double)(nrings + 1 - nb_truncated);
     49   size_t itheta;
     50   size_t iphi;
     51   size_t i = 0;
     52   res_T res = RES_OK;
     53   ASSERT(coords_ptr && *coords_ptr && radius > 0);
     54 
     55   coords = *coords_ptr;
     56   darray_double_init(allocator, &sincos_theta);
     57   darray_double_init(allocator, &sincos_phi);
     58 
     59   res = darray_double_resize(&sincos_theta, nslices*2/*sin & cos*/);
     60   if(res != RES_OK) goto error;
     61   res = darray_double_resize(&sincos_phi, nrings*2/*sin & cos*/);
     62   if(res != RES_OK) goto error;
     63 
     64   /* Precompute the sinus/cosine of the theta/phi angles */
     65   step_theta = 2*PI / (double)nslices;
     66   FOR_EACH(itheta, 0, nslices) {
     67     const double theta = -PI + (double)itheta * step_theta;
     68     darray_double_data_get(&sincos_theta)[itheta*2 + SIN] = sin(theta);
     69     darray_double_data_get(&sincos_theta)[itheta*2 + COS] = cos(theta);
     70   }
     71   FOR_EACH(iphi, 0, nrings) {
     72     const double phi
     73       = phi_z_min + (double)(iphi + !bottom_truncated) * step_phi;
     74     darray_double_data_get(&sincos_phi)[iphi*2 + SIN] = sin(phi);
     75     darray_double_data_get(&sincos_phi)[iphi*2 + COS] = cos(phi);
     76   }
     77 
     78   /* Setup the contour vertices */
     79   i = 0;
     80   FOR_EACH(itheta, 0, nslices) {
     81     const double* theta = darray_double_cdata_get(&sincos_theta) + itheta*2;
     82     FOR_EACH(iphi, 0, nrings) {
     83       const double* phi = darray_double_cdata_get(&sincos_phi) + iphi*2;
     84       coords[i++] = radius * COS[theta] * COS[phi];
     85       coords[i++] = radius * SIN[theta] * COS[phi];
     86       coords[i++] = radius * SIN[phi];
     87     }
     88   }
     89 
     90   if(close_bottom || !bottom_truncated) {
     91     /* Setup the bottom polar vertex */
     92     coords[i++] = 0;
     93     coords[i++] = 0;
     94     coords[i++] = bottom_truncated ? z_range[0] : -radius;
     95   }
     96 
     97   if(close_top || !top_truncated) {
     98     /* Setup the top polar vertex */
     99     coords[i++] = 0;
    100     coords[i++] = 0;
    101     coords[i++] = top_truncated ? z_range[1] : +radius;
    102   }
    103 
    104 exit:
    105   darray_double_release(&sincos_theta);
    106   darray_double_release(&sincos_phi);
    107   *coords_ptr = coords + i;
    108   return res;
    109 error:
    110   goto exit;
    111 }
    112 
    113 static size_t*
    114 setup_sphere_indices
    115   (size_t* ids,
    116    const size_t offset,
    117    const unsigned nslices, /* # subdivisions around the Z axis */
    118    const unsigned nstacks, /* # subdivisions along the Z axis */
    119    const double radius,
    120    const double z_range[2],
    121    const int close_ends,
    122    const int cw_out)
    123 {
    124   const int top_truncated = z_range && z_range[1] < +radius;
    125   const int bottom_truncated = z_range && z_range[0] > -radius;
    126   const int close_top = top_truncated && (close_ends & S3DUT_CAP_POS_Z);
    127   const int close_bottom = bottom_truncated && (close_ends & S3DUT_CAP_NEG_Z);
    128   const unsigned nb_truncated = (unsigned)(top_truncated + bottom_truncated);
    129   const unsigned nrings = nstacks - 1 + nb_truncated;
    130   size_t ibottom;
    131   size_t itop;
    132   size_t i, itheta, iphi;
    133   ASSERT(ids && nslices && nstacks);
    134 
    135   /* Define the indices of the contour primitives */
    136   i = 0;
    137   FOR_EACH(itheta, 0, nslices) {
    138     const size_t itheta0 = offset + itheta * nrings;
    139     const size_t itheta1 = offset + ((itheta + 1) % nslices) * nrings;
    140     FOR_EACH(iphi, 0, nrings-1) {
    141       const size_t iphi0 = iphi + 0;
    142       const size_t iphi1 = iphi + 1;
    143 
    144       ids[i] = itheta0 + iphi0;
    145       ids[cw_out?i+1:i+2] = itheta0 + iphi1;
    146       ids[cw_out?i+2:i+1] = itheta1 + iphi0;
    147       i += 3;
    148 
    149       ids[i] = itheta1 + iphi0;
    150       ids[cw_out?i+1:i+2] = itheta0 + iphi1;
    151       ids[cw_out?i+2:i+1] = itheta1 + iphi1;
    152       i += 3;
    153     }
    154   }
    155 
    156   /* Define the indices of the polar primitives */
    157   FOR_EACH(itheta, 0, nslices) {
    158     const size_t itheta0 = offset + itheta * nrings;
    159     const size_t itheta1 = offset + ((itheta + 1) % nslices) * nrings;
    160 
    161     if(close_bottom || !bottom_truncated) {
    162       ibottom = nslices * nrings;
    163       ids[i] = offset + ibottom;
    164       ids[cw_out?i+1:i+2] = itheta0;
    165       ids[cw_out?i+2:i+1] = itheta1;
    166       i += 3;
    167     }
    168 
    169     if(close_top || !top_truncated) {
    170       itop = (close_bottom || !bottom_truncated)
    171         ? nslices * nrings + 1 : nslices * nrings;
    172       ids[i] = offset + itop;
    173       ids[cw_out?i+1:i+2] = itheta1 + (nrings - 1);
    174       ids[cw_out?i+2:i+1] = itheta0 + (nrings - 1);
    175       i += 3;
    176     }
    177   }
    178   return ids + i;
    179 }
    180 
    181 static size_t*
    182 close_wall
    183   (size_t* ids,
    184    const size_t fst_id_out,
    185    const size_t fst_id_in,
    186    const unsigned nslices,
    187    const unsigned external_nrings,
    188    const unsigned internal_nrings,
    189    const int bottom)
    190 {
    191   size_t islice;
    192   size_t i = 0;
    193   ASSERT(ids && nslices >= 3 && external_nrings >= 1 && internal_nrings >= 1);
    194 
    195   FOR_EACH(islice, 0, nslices) {
    196     ids[i] = fst_id_out + islice * external_nrings;
    197     ids[bottom?i+1:i+2] = fst_id_in + ((islice+1) % nslices) * internal_nrings;
    198     ids[bottom?i+2:i+1] = fst_id_in + islice * internal_nrings;
    199     i += 3;
    200 
    201     ids[i] = fst_id_out + islice * external_nrings;
    202     ids[bottom?i+1:i+2] = fst_id_out + ((islice+1) % nslices) * external_nrings;
    203     ids[bottom?i+2:i+1] = fst_id_in + ((islice+1) % nslices) * internal_nrings;
    204     i += 3;
    205   }
    206   return ids + i;
    207 }
    208 
    209 static void
    210 sphere_accum_counts
    211   (const double radius,
    212    const unsigned nslices,
    213    const unsigned nstacks,
    214    const double z_range[2],
    215    const int close_ends,
    216    unsigned* nrings,
    217    size_t* ntris,
    218    size_t* nverts)
    219 {
    220   const int top_truncated = z_range && z_range[1] < +radius;
    221   const int bottom_truncated = z_range && z_range[0] > -radius;
    222   const int close_top = top_truncated && (close_ends & S3DUT_CAP_POS_Z);
    223   const int close_bottom = bottom_truncated && (close_ends & S3DUT_CAP_NEG_Z);
    224   const unsigned nb_truncated = (unsigned)(top_truncated + bottom_truncated);
    225   const unsigned nb_closed_ends = (close_top ? 1u : 0) + (close_bottom ? 1u : 0);
    226   const unsigned nb_pole_vrtx = nb_closed_ends + (2 - nb_truncated);
    227   ASSERT(nrings && ntris && nverts);
    228   *nrings = nstacks - 1 + nb_truncated;;
    229   *nverts += nslices*(*nrings) /* #contour verts*/ + nb_pole_vrtx; /*polar verts*/
    230   *ntris += 2 * nslices * (*nrings -1) /* #contour tris */
    231     + (2 - nb_truncated + nb_closed_ends) * nslices; /* #polar tris */
    232 }
    233 
    234 /*******************************************************************************
    235  * Exported function
    236  ******************************************************************************/
    237 res_T
    238 s3dut_create_sphere
    239   (struct mem_allocator* allocator,
    240    const double radius,
    241    const unsigned nslices,
    242    const unsigned nstacks,
    243    struct s3dut_mesh** mesh)
    244 {
    245   return s3dut_create_truncated_sphere
    246     (allocator, radius, nslices, nstacks, NULL, 0, mesh);
    247 }
    248 
    249 res_T
    250 s3dut_create_hemisphere
    251   (struct mem_allocator* allocator,
    252    const double radius,
    253    const unsigned nslices,
    254    const unsigned nstacks,
    255    struct s3dut_mesh** mesh)
    256 {
    257   double z_range[2];
    258   z_range[0] = 0;
    259   z_range[1] = +radius;
    260   return s3dut_create_truncated_sphere
    261     (allocator, radius, nslices, nstacks, z_range, 0, mesh);
    262 }
    263 
    264 res_T
    265 s3dut_create_truncated_sphere
    266   (struct mem_allocator* allocator,
    267    const double radius,
    268    const unsigned nslices,
    269    const unsigned nstacks,
    270    const double z_range[2],
    271    const int close_ends,
    272    struct s3dut_mesh** mesh)
    273 {
    274   struct s3dut_mesh* sphere = NULL;
    275   const int top_truncated = z_range && z_range[1] < +radius;
    276   const int bottom_truncated = z_range && z_range[0] > -radius;
    277   const int close_top = top_truncated && (close_ends & S3DUT_CAP_POS_Z);
    278   const int close_bottom = bottom_truncated && (close_ends & S3DUT_CAP_NEG_Z);
    279   const unsigned nb_truncated = (unsigned)(top_truncated + bottom_truncated);
    280   const unsigned nb_closed_ends = (close_top ? 1u : 0) + (close_bottom ? 1u : 0);
    281   const unsigned nb_pole_vrtx = nb_closed_ends + (2 - nb_truncated);
    282   const unsigned nrings = nstacks - 1 + nb_truncated;
    283   const size_t nverts = nslices*nrings/* #contour verts*/
    284     + nb_pole_vrtx/*polar verts*/;
    285   const size_t ntris = 2 * nslices*(nrings - 1)/* #contour tris*/
    286     + nb_pole_vrtx*nslices/* #polar tris*/;
    287   double* coords;
    288   res_T res = RES_OK;
    289   ASSERT(nb_truncated <= 2);
    290 
    291   if(radius <= 0 || nslices < 3 || nstacks < 2 || !mesh
    292     || (z_range && z_range[0] >= z_range[1])) {
    293     res = RES_BAD_ARG;
    294     goto error;
    295   }
    296 
    297   res = mesh_create(allocator, S3DUT_MESH_SPHERE, nverts, ntris, &sphere);
    298   if(res != RES_OK) goto error;
    299 
    300   coords = darray_double_data_get(&sphere->coords);
    301   res = setup_sphere_coords(allocator, &coords, radius, z_range, nslices,
    302     nstacks, close_ends);
    303   if(res != RES_OK) goto error;
    304 
    305   setup_sphere_indices(darray_size_t_data_get(&sphere->ids), 0,
    306     nslices, nstacks, radius, z_range, close_ends, 1);
    307 
    308 exit:
    309   if(mesh) *mesh = sphere;
    310   return res;
    311 error:
    312   if(sphere) {
    313     S3DUT(mesh_ref_put(sphere));
    314     sphere = NULL;
    315   }
    316   goto exit;
    317 }
    318 
    319 res_T
    320 s3dut_create_thick_truncated_sphere
    321   (struct mem_allocator* allocator,
    322    const double radius,
    323    const double thickness,
    324    const unsigned nslices,
    325    const unsigned nstacks,
    326    const double z_range[2],
    327    const int c_e,
    328    struct s3dut_mesh** mesh)
    329 {
    330   struct s3dut_mesh* sphere = NULL;
    331   int close_ends = c_e;
    332   double* coords = NULL;
    333   double* prev_coords;
    334   size_t* ids_out = NULL;
    335   size_t* ids_in = NULL;
    336   size_t* ids_walls = NULL;
    337   size_t id_offset;
    338   const double internal_radius = radius - thickness;
    339   const int top_truncated = z_range && (z_range[1] < +radius);
    340   const int bottom_truncated = z_range && (z_range[0] > -radius);
    341   const int close_top = top_truncated && (close_ends & S3DUT_CAP_POS_Z);
    342   const int close_bottom = bottom_truncated && (close_ends & S3DUT_CAP_NEG_Z);
    343   const int top_seam = z_range && (z_range[1] < internal_radius) && !close_top;
    344   const int bottom_seam
    345     = z_range && (z_range[0] > -internal_radius) && ! close_bottom;
    346   unsigned external_nrings;
    347   unsigned internal_nrings;
    348   const unsigned nb_seams = (unsigned)(top_seam + bottom_seam);
    349   size_t nverts = 0;
    350   size_t ntris = 0;
    351   double z_internal_range[2];
    352   res_T res = RES_OK;
    353 
    354   if(radius <= thickness || thickness <= 0 || nslices < 3 || nstacks < 2
    355   || !mesh || (z_range && z_range[0] >= z_range[1])) {
    356     return RES_BAD_ARG;
    357   }
    358 
    359   /* Special case when a single sphere is truncated */
    360   if(top_truncated && (z_range[1] >= internal_radius)) {
    361     close_ends |= S3DUT_CAP_POS_Z; /* close the external sphere's top end */
    362   }
    363   if(bottom_truncated && (z_range[0] <= -internal_radius)) {
    364     close_ends |= S3DUT_CAP_NEG_Z; /* close the external sphere's bottom end */
    365   }
    366   z_internal_range[0] = (bottom_truncated && !close_bottom)
    367     ? z_range[0] : (z_range ? z_range[0] : -radius) + thickness;
    368   z_internal_range[1] = (top_truncated && !close_top) ?
    369     z_range[1] : (z_range ? z_range[1] : +radius) - thickness;
    370   sphere_accum_counts(radius, nslices, nstacks, z_range, close_ends,
    371     &external_nrings, &ntris, &nverts);
    372   sphere_accum_counts(radius-thickness, nslices, nstacks, z_internal_range,
    373     close_ends, &internal_nrings, &ntris, &nverts);
    374   ntris += 2 * nb_seams * nslices; /* # seam tris */
    375   res = mesh_create(allocator, S3DUT_MESH_THICK_SPHERE, nverts, ntris, &sphere);
    376   if(res != RES_OK) goto error;
    377 
    378   coords = darray_double_data_get(&sphere->coords);
    379   ids_out = darray_size_t_data_get(&sphere->ids);
    380   /* External sphere */
    381   prev_coords = coords;
    382   setup_sphere_coords
    383     (allocator, &coords, radius, z_range, nslices, nstacks, close_ends);
    384   ids_in = setup_sphere_indices(ids_out, 0, nslices, nstacks, radius, z_range,
    385     close_ends, 1);
    386   /* Internal sphere */
    387   id_offset = (size_t)(coords - prev_coords);
    388   ASSERT(id_offset % 3 == 0);
    389   id_offset /= 3;
    390   setup_sphere_coords(allocator, &coords, radius - thickness, z_internal_range,
    391     nslices, nstacks, close_ends);
    392   ids_walls = setup_sphere_indices (ids_in, id_offset, nslices,
    393     nstacks, radius - thickness, z_internal_range, close_ends, 0);
    394   if(bottom_seam) {
    395     ids_walls = close_wall(ids_walls, 0, id_offset, nslices, external_nrings,
    396       internal_nrings, 1);
    397   }
    398   if(top_seam) {
    399     ids_walls = close_wall(ids_walls, nstacks, id_offset +  nstacks, nslices,
    400       external_nrings, internal_nrings, 0);
    401   }
    402 
    403 exit:
    404   if(mesh) *mesh = sphere;
    405   return res;
    406 error:
    407   if(sphere) {
    408     S3DUT(mesh_ref_put(sphere));
    409     sphere = NULL;
    410   }
    411   goto exit;
    412 }