star-3dut

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

s3dut_cylinder.c (8737B)


      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 double*
     23 setup_cylinder_coords
     24   (double* coords,
     25    const double radius,
     26    const double z_bottom,
     27    const double z_top,
     28    const unsigned nslices,
     29    const unsigned nstacks,
     30    const int close_bottom,
     31    const int close_top)
     32 {
     33   double step_theta;
     34   double step_z;
     35   const double height = z_top - z_bottom;
     36   size_t itheta, istack;
     37   size_t i = 0;
     38   ASSERT(coords && radius > 0 && height > 0 && nslices >= 3 && nstacks >= 1);
     39 
     40   /* Contour vertices */
     41   step_theta = 2*PI / (double)nslices;
     42   step_z = height / (double)nstacks;
     43   FOR_EACH(itheta, 0, nslices) {
     44     const double theta = (double)itheta * step_theta;
     45     const double x = cos(theta);
     46     const double y = sin(theta);
     47     double z = z_bottom;
     48 
     49     FOR_EACH(istack, 0, nstacks+1) {
     50       coords[i++] = x*radius;
     51       coords[i++] = y*radius;
     52       coords[i++] = z;
     53       z = (istack==nstacks) ? z_top : z+step_z; /* No rounding error! */
     54     }
     55   }
     56 
     57   /* Bottom polar vertex */
     58   if(close_bottom) {
     59     coords[i++] = 0;
     60     coords[i++] = 0;
     61     coords[i++] = z_bottom;
     62   }
     63 
     64   /* Top polar vertex */
     65   if(close_top) {
     66     coords[i++] = 0;
     67     coords[i++] = 0;
     68     coords[i++] = z_top;
     69   }
     70   return coords + i;
     71 }
     72 
     73 static size_t*
     74 setup_cylinder_indices
     75   (size_t* ids,
     76    const size_t offset,
     77    const unsigned nslices,
     78    const unsigned nstacks,
     79    const int close_bottom,
     80    const int close_top,
     81    const int cw_out)
     82 {
     83   size_t islice;
     84   size_t istack;
     85   size_t ibottom;
     86   size_t itop;
     87   size_t i = 0;
     88   ASSERT(ids && nslices && nstacks);
     89 
     90   FOR_EACH(islice, 0, nslices) {
     91     const size_t islice0 = offset + islice * (nstacks+1);
     92     const size_t islice1 = offset + ((islice+1)%nslices) * (nstacks+1);
     93     FOR_EACH(istack, 0, nstacks) {
     94       const size_t istack0 = istack + 0;
     95       const size_t istack1 = istack + 1;
     96 
     97       ids[i] = islice0 + istack0;
     98       ids[cw_out?i+1:i+2] = islice0 + istack1;
     99       ids[cw_out?i+2:i+1] = islice1 + istack0;
    100       i += 3;
    101 
    102       ids[i] = islice1 + istack0;
    103       ids[cw_out?i+1:i+2] = islice0 + istack1;
    104       ids[cw_out?i+2:i+1] = islice1 + istack1;
    105       i += 3;
    106     }
    107   }
    108 
    109   if(close_bottom) {
    110     ibottom = nslices * (nstacks+1);
    111     FOR_EACH(islice, 0, nslices) {
    112       ids[i] = offset + ibottom;
    113       ids[cw_out?i+1:i+2] = offset + islice * (nstacks+1);
    114       ids[cw_out?i+2:i+1] = offset + ((islice+1)%nslices) * (nstacks+1);
    115       i += 3;
    116     }
    117   }
    118 
    119   if(close_top) {
    120     itop = (close_bottom) ? nslices * (nstacks+1) + 1 : nslices * (nstacks+1);
    121     FOR_EACH(islice, 0, nslices) {
    122       ids[i] = offset + itop;
    123       ids[cw_out?i+1:i+2] = offset + ((islice+1)%nslices) * (nstacks+1) + nstacks;
    124       ids[cw_out?i+2:i+1] = offset + islice * (nstacks+1) + nstacks;
    125       i += 3;
    126     }
    127   }
    128   return ids + i;
    129 }
    130 
    131 static size_t*
    132 close_wall
    133   (size_t* ids,
    134    const size_t fst_id_out,
    135    const size_t fst_id_in,
    136    const unsigned nslices,
    137    const unsigned nstacks,
    138    const int bottom)
    139 {
    140   size_t islice;
    141   size_t i = 0;
    142   ASSERT(ids && nslices >= 3 && nstacks >= 1);
    143 
    144   FOR_EACH(islice, 0, nslices) {
    145     ids[i] = fst_id_out + islice * (nstacks+1);
    146     ids[bottom?i+1:i+2] = fst_id_in + ((islice+1) % nslices) * (nstacks+1);
    147     ids[bottom?i+2:i+1] = fst_id_in + islice * (nstacks+1);
    148     i += 3;
    149 
    150     ids[i] = fst_id_out + islice * (nstacks+1);
    151     ids[bottom?i+1:i+2] = fst_id_out + ((islice+1) % nslices) * (nstacks+1);
    152     ids[bottom?i+2:i+1] = fst_id_in + ((islice+1) % nslices) * (nstacks+1);
    153     i += 3;
    154   }
    155   return ids + i;
    156 }
    157 
    158 /*******************************************************************************
    159  * Exported functions
    160  ******************************************************************************/
    161 res_T
    162 s3dut_create_cylinder
    163   (struct mem_allocator* allocator,
    164    const double radius,
    165    const double height,
    166    const unsigned nslices,
    167    const unsigned nstacks,
    168    struct s3dut_mesh** mesh)
    169 {
    170   return s3dut_create_thin_cylinder(allocator, radius, height, nslices,
    171     nstacks, S3DUT_CAP_POS_Z|S3DUT_CAP_NEG_Z, mesh);
    172 }
    173 
    174 res_T
    175 s3dut_create_thin_cylinder
    176   (struct mem_allocator* allocator,
    177    const double radius,
    178    const double height,
    179    const unsigned nslices,
    180    const unsigned nstacks,
    181    const int close_ends,
    182    struct s3dut_mesh** mesh)
    183 {
    184   struct s3dut_mesh* cylinder = NULL;
    185   double* coords = NULL;
    186   size_t* ids = NULL;
    187   size_t nverts;
    188   size_t ntris;
    189   const int close_bottom = close_ends & S3DUT_CAP_NEG_Z;
    190   const int close_top = close_ends & S3DUT_CAP_POS_Z;
    191   const unsigned nb_closed_ends = (close_bottom ? 1u : 0) + (close_top ? 1u : 0);
    192   res_T res = RES_OK;
    193 
    194   if(radius <= 0 || height <= 0 || nslices < 3 || nstacks < 1 || !mesh) {
    195     res = RES_BAD_ARG;
    196     goto error;
    197   }
    198 
    199   nverts = nslices * (nstacks+1)/*#contour*/ + nb_closed_ends/*#polar*/;
    200   ntris = 2*nslices*nstacks/*#contour*/ + nb_closed_ends * nslices/*#polar*/;
    201 
    202   res = mesh_create(allocator, S3DUT_MESH_CYLINDER, nverts, ntris, &cylinder);
    203   if(res != RES_OK) goto error;
    204 
    205   coords = darray_double_data_get(&cylinder->coords);
    206   ids = darray_size_t_data_get(&cylinder->ids);
    207   setup_cylinder_coords(coords, radius, -0.5*height, +0.5*height,
    208     nslices, nstacks, close_bottom, close_top);
    209   setup_cylinder_indices(ids, 0, nslices, nstacks, close_bottom, close_top, 1);
    210 
    211 exit:
    212   if(mesh) *mesh = cylinder;
    213   return res;
    214 error:
    215   if(cylinder) {
    216     S3DUT(mesh_ref_put(cylinder));
    217     cylinder = NULL;
    218   }
    219   goto exit;
    220 }
    221 
    222 res_T
    223 s3dut_create_thick_cylinder
    224   (struct mem_allocator* allocator,
    225    const double radius,
    226    const double height,
    227    const double thickness,
    228    const unsigned nslices,
    229    const unsigned nstacks,
    230    const int close_ends,
    231    struct s3dut_mesh** mesh)
    232 {
    233   struct s3dut_mesh* cylinder = NULL;
    234   double* coords_out = NULL;
    235   double* coords_in = NULL;
    236   size_t* ids_out = NULL;
    237   size_t* ids_in = NULL;
    238   size_t* ids_walls = NULL;
    239   size_t nverts;
    240   size_t ntris;
    241   size_t id_offset;
    242   const int close_bottom = close_ends & S3DUT_CAP_NEG_Z;
    243   const int close_top = close_ends & S3DUT_CAP_POS_Z;
    244   const unsigned nb_closed_ends = (close_bottom ? 1u : 0) + (close_top ? 1u : 0);
    245   res_T res = RES_OK;
    246 
    247   if(radius <= thickness || height <= 0 || thickness <= 0
    248     || height <= (double)nb_closed_ends * thickness
    249     || nslices < 3 || nstacks < 1 || !mesh) {
    250     res = RES_BAD_ARG;
    251     goto error;
    252   }
    253 
    254   nverts = 2 * (nslices*(nstacks+1)/*#contour*/ + nb_closed_ends/*#polar*/);
    255   ntris = 2 *
    256     ( 2 * nslices*nstacks /*#contour*/
    257     + 2 * nslices/*#trg fans tris, regardless of closedness*/);
    258 
    259   res = mesh_create
    260     (allocator, S3DUT_MESH_THICK_CYLINDER, nverts, ntris, &cylinder);
    261   if(res != RES_OK) goto error;
    262 
    263   coords_out = darray_double_data_get(&cylinder->coords);
    264   ids_out = darray_size_t_data_get(&cylinder->ids);
    265   /* External cylinder */
    266   coords_in = setup_cylinder_coords(coords_out, radius, -0.5*height,
    267     +0.5*height, nslices, nstacks, close_bottom, close_top);
    268   ids_in = setup_cylinder_indices
    269     (ids_out, 0, nslices, nstacks, close_bottom, close_top, 1);
    270   /* Internal cylinder */
    271   id_offset = (size_t)(coords_in - coords_out);
    272   ASSERT(id_offset % 3 == 0);
    273   id_offset /= 3;
    274   setup_cylinder_coords(coords_in,
    275     radius - thickness,
    276     close_bottom  ? -0.5*height + thickness : -0.5*height,
    277     close_top  ? +0.5*height -thickness : +0.5*height,
    278     nslices, nstacks, close_bottom, close_top);
    279   ids_walls = setup_cylinder_indices
    280     (ids_in, id_offset, nslices, nstacks, close_bottom, close_top, 0);
    281   /* Close walls where the cylinder is open */
    282   if(!close_bottom) {
    283     ids_walls = close_wall(ids_walls, 0, id_offset, nslices, nstacks, 1);
    284   }
    285   if(!close_top) {
    286     close_wall(ids_walls, nstacks, id_offset + nstacks, nslices, nstacks, 0);
    287   }
    288 
    289 exit:
    290   if(mesh) *mesh = cylinder;
    291   return res;
    292 error:
    293   if(cylinder) {
    294     S3DUT(mesh_ref_put(cylinder));
    295     cylinder = NULL;
    296   }
    297   goto exit;
    298 }