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 }