s3dut_super_shape.c (4492B)
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 #include <rsys/double2.h> 20 #include <rsys/double3.h> 21 #include <math.h> 22 23 struct spherical { 24 double r; 25 double theta; 26 double phi; 27 }; 28 29 /******************************************************************************* 30 * Helper functions 31 ******************************************************************************/ 32 static FINLINE void 33 cartesian_to_spherical(const double* xyz, struct spherical* spherical) 34 { 35 ASSERT(spherical && xyz); 36 spherical->r = d3_len(xyz); 37 spherical->phi = asin(xyz[2]/spherical->r); 38 /* Map the atan results in [-PI, PI] to ensure that theta lies in [-PI,2PI] 39 * rather than [-PI/2, 3PI/2] that would violate the super formula 40 * constraints */ 41 spherical->theta = xyz[0] == 0 ? PI/2 : atan(xyz[1]/xyz[0]) - PI/2; 42 if(xyz[0] < 0) spherical->theta += PI; 43 } 44 45 static FINLINE double 46 super_formula_eval(const struct s3dut_super_formula* form, const double angle) 47 { 48 double m, k, g; 49 ASSERT(form); 50 m = fabs(cos(form->M * angle / 4.0)) / form->A; 51 k = fabs(sin(form->M * angle / 4.0)) / form->B; 52 g = pow(m, form->N1) + pow(k, form->N2); 53 return pow(g, (-1.0/form->N0)); 54 } 55 56 /******************************************************************************* 57 * Exported functions 58 ******************************************************************************/ 59 res_T 60 s3dut_create_super_shape 61 (struct mem_allocator* allocator, /* May be NULL <=> use default allocator */ 62 const struct s3dut_super_formula* formula0, 63 const struct s3dut_super_formula* formula1, 64 const double radius, /* In [0, INF) */ 65 const unsigned nslices, /* # subdivisions around Z axis in [3, INF) */ 66 const unsigned nstacks, /* # subdivisions along Z axis in [2, INF) */ 67 struct s3dut_mesh** super_shape) 68 { 69 return s3dut_create_thick_truncated_super_shape(allocator, formula0, 70 formula1, radius, 0, nslices, nstacks, NULL, 0, super_shape); 71 } 72 73 res_T 74 s3dut_create_thick_truncated_super_shape 75 (struct mem_allocator* allocator, 76 const struct s3dut_super_formula* formula0, 77 const struct s3dut_super_formula* formula1, 78 const double radius, 79 const double thickness, 80 const unsigned nslices, 81 const unsigned nstacks, 82 const double z_range[2], 83 const int cap_mask, 84 struct s3dut_mesh** mesh) 85 { 86 struct s3dut_mesh* sshape = NULL; 87 size_t nverts; 88 size_t ivert; 89 res_T res = RES_OK; 90 91 if(!formula0 || !formula1 || !mesh) { 92 res = RES_BAD_ARG; 93 goto error; 94 } 95 96 if(thickness == 0) { 97 res = s3dut_create_truncated_sphere(allocator, radius, nslices, nstacks, 98 z_range, cap_mask, &sshape); 99 } else { 100 res = s3dut_create_thick_truncated_sphere(allocator, radius, thickness, 101 nslices, nstacks, z_range, cap_mask, &sshape); 102 } 103 if(res != RES_OK) goto error; 104 105 /* Positioned the sphere vertices wrt to the super formulas */ 106 nverts = darray_double_size_get(&sshape->coords) / 3u; 107 FOR_EACH(ivert, 0, nverts) { 108 double* pos = darray_double_data_get(&sshape->coords) + ivert*3; 109 struct spherical spherical; 110 double uv[2]; 111 double cos_theta, cos_phi; 112 double sin_theta, sin_phi; 113 114 cartesian_to_spherical(pos, &spherical); 115 116 uv[0] = super_formula_eval(formula0, spherical.theta); 117 uv[1] = super_formula_eval(formula1, spherical.phi); 118 119 cos_theta = cos(spherical.theta); 120 sin_theta = sin(spherical.theta); 121 cos_phi = cos(spherical.phi); 122 sin_phi = sin(spherical.phi); 123 124 pos[0] = uv[0] * cos_theta * uv[1] * cos_phi * spherical.r; 125 pos[1] = uv[0] * sin_theta * uv[1] * cos_phi * spherical.r; 126 pos[2] = uv[1] * sin_phi * spherical.r; 127 } 128 129 sshape->type = S3DUT_MESH_SUPER_SHAPE; 130 131 exit: 132 if(mesh) *mesh = sshape; 133 return res; 134 error: 135 if(sshape) { S3DUT(mesh_ref_put(sshape)); sshape = NULL; } 136 goto exit; 137 } 138