sphin_geometry.c (9351B)
1 /* Copyright (C) 2024-2026 Centre National de la Recherche Scientifique 2 * Copyright (C) 2024-2026 Clermont Auvergne INP 3 * Copyright (C) 2024-2026 INSA Lyon 4 * Copyright (C) 2024-2026 Institut Mines Télécom Albi-Carmaux 5 * Copyright (C) 2024-2026 Institut National Polytechnique de Toulouse 6 * Copyright (C) 2024-2026 |Méso|Star> (contact@meso-star.com) 7 * Copyright (C) 2024-2026 PhotonLyX (info@photonlyx.com) 8 * Copyright (C) 2024-2026 Université de Lorraine 9 * Copyright (C) 2024-2026 Université Paul Sabatier 10 * Copyright (C) 2024-2026 Université Toulouse - Jean Jaurès 11 * 12 * This program is free software: you can redistribute it and/or modify 13 * it under the terms of the GNU General Public License as published by 14 * the Free Software Foundation, either version 3 of the License, or 15 * (at your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License for more details. 21 * 22 * You should have received a copy of the GNU General Public License 23 * along with this program. If not, see <http://www.gnu.org/licenses/>. */ 24 #define _POSIX_C_SOURCE 200112L /* for strtok_r support */ 25 26 #include "sphin_c.h" 27 #include "sphin_geometry.h" 28 29 #include <rsys/cstr.h> 30 #include <rsys/double3.h> 31 #include <rsys/mem_allocator.h> 32 #include <rsys/ref_count.h> 33 #include <rsys/rsys.h> 34 #include <star/sstl.h> 35 36 /******************************************************************************* 37 * Helper functions 38 ******************************************************************************/ 39 static void 40 release_geometry 41 (ref_T* address) 42 { 43 struct sphin_geometry* geometry = NULL; 44 struct sphin* sphin = NULL; 45 46 ASSERT(NULL != address); 47 48 geometry = CONTAINER_OF(address, struct sphin_geometry, ref); 49 str_release(&geometry->stl_filename); 50 darray_double_release(&geometry->coords); 51 darray_size_t_release(&geometry->indices); 52 sphin = geometry->sphin; 53 MEM_RM(sphin->allocator, geometry); 54 SPHIN(ref_put(sphin)); 55 } 56 57 static res_T 58 geometry_create 59 (struct sphin* sphin, 60 const char* stl_filename, 61 struct sphin_geometry** out_geometry) 62 { 63 struct sphin_geometry* geom = NULL; 64 res_T res = RES_OK; 65 66 ASSERT(NULL != sphin); 67 ASSERT(NULL != out_geometry); 68 ASSERT(NULL != stl_filename); 69 ASSERT('\0' != stl_filename[0]); /* Name can't be empty */ 70 71 geom = MEM_CALLOC(sphin->allocator, 1, sizeof(struct sphin_geometry)); 72 if (NULL == geom) { res = RES_MEM_ERR; goto error; } 73 74 /* Init geometry ref counter and init member variables */ 75 ref_init(&geom->ref); 76 SPHIN(ref_get(sphin)); 77 geom->sphin = sphin; 78 darray_double_init(sphin->allocator, &geom->coords); 79 darray_size_t_init(sphin->allocator, &geom->indices); 80 81 str_init(sphin->allocator, &geom->stl_filename); 82 res = str_set(&geom->stl_filename, stl_filename); 83 if (RES_OK != res) { goto error; } 84 85 exit: 86 *out_geometry = geom; 87 return res; 88 error: 89 if (NULL != geom) { 90 SPHIN(geometry_ref_put(geom)); 91 geom = NULL; 92 } 93 goto exit; 94 } 95 96 /******************************************************************************* 97 * Local functions 98 ******************************************************************************/ 99 double 100 geometry_compute_area 101 (const struct sphin_geometry* geom) 102 { 103 const double* pos = NULL; 104 const size_t* ids = NULL; 105 double area = 0.0; 106 size_t id, itri, tri_count; 107 108 ASSERT(NULL != geom); 109 110 pos = darray_double_cdata_get(&geom->coords); 111 ids = darray_size_t_cdata_get(&geom->indices); 112 tri_count = darray_size_t_size_get(&geom->indices)/3; 113 if (0 == tri_count) return 0.; 114 115 FOR_EACH(itri, 0, tri_count) { 116 /* Retrieve vertex coordinates */ 117 id = itri * 3; /*#ids per vertex*/ 118 const double* v0 = pos + ids[id+0]*3; 119 const double* v1 = pos + ids[id+1]*3; 120 const double* v2 = pos + ids[id+2]*3; 121 double E0[3], E1[3], N[3]; 122 123 /* Construct vectors defining the triangle vertices */ 124 d3_sub(E0, v1, v0); 125 d3_sub(E1, v2, v0); 126 127 /* Increment total area */ 128 area += d3_len(d3_cross(N, E0, E1)); 129 } 130 return area * 0.5f; 131 } 132 133 double 134 geometry_compute_volume 135 (const struct sphin_geometry* geom, 136 const enum sphin_side side) 137 { 138 const double* pos = NULL; 139 const size_t* ids = NULL; 140 double volume = 0.0; 141 size_t id, itri, tri_count; 142 143 pos = darray_double_cdata_get(&geom->coords); 144 ids = darray_size_t_cdata_get(&geom->indices); 145 tri_count = darray_size_t_size_get(&geom->indices)/3; 146 if (0 == tri_count) return 0.; 147 148 FOR_EACH(itri, 0, tri_count) { 149 /* Retrieve vertex coordinates */ 150 id = itri * 3; /*#ids per vertex*/ 151 const double* v0 = pos + ids[id+0]*3; 152 const double* v1 = pos + ids[id+1]*3; 153 const double* v2 = pos + ids[id+2]*3; 154 double E0[3], E1[3], N[3]; 155 double b, h; 156 157 /* Construct vectors defining the triangle vertices */ 158 d3_sub(E0, v1, v0); 159 d3_sub(E1, v2, v0); 160 161 /* Compute triangle normal */ 162 if (SPHIN_SIDE_BACK == side) { 163 d3_cross(N, E1, E0); 164 } else { 165 d3_cross(N, E0, E1); 166 } 167 b = d3_normalize(N, N); /* Base area */ 168 h = -d3_dot(N, v0); /* Height from the base to the apex */ 169 170 volume += (h*b); 171 } 172 return volume / 6.0; 173 174 } 175 176 res_T 177 geometry_parse 178 (struct sphin* sphin, 179 char* value, 180 struct sphin_geometry** out_geom) 181 { 182 size_t i; 183 char* side = NULL; 184 char* filename = NULL; 185 char* token_ptr = NULL; 186 char* unit = NULL; 187 struct sphin_geometry* geom = NULL; 188 struct sstl* sstl = NULL; 189 struct sstl_desc sstl_desc; 190 double* coords = NULL; 191 double scaling_factor; 192 size_t* indices = NULL; 193 res_T res = RES_OK; 194 195 ASSERT(NULL != sphin); 196 ASSERT(NULL != out_geom); 197 198 if (NULL == value) { res = RES_BAD_ARG; goto error; } 199 200 /* Create the SSTL device using the same allocator and logger as the sphin 201 * handler. We intentionally instantiate and free the SSTL struct for 202 * each geometry, rather than creating it once at the sphin_config level. 203 * We argue that the performance loss caused by this strategy is marginal 204 * compared to the advantage of keeping the effects of this structure 205 * encapsulated to the scope of this function. */ 206 res = sstl_create(sphin->logger, sphin->allocator, sphin->verbose, &sstl); 207 if (RES_OK != res) { goto error; } 208 209 /* Parse side */ 210 side = strtok_r(value, " \t", &token_ptr); 211 if (NULL == side){ res = RES_BAD_ARG; goto error; } 212 213 /* Parse filename */ 214 filename = strtok_r(NULL, " \t", &token_ptr); 215 if (NULL == filename){ res = RES_BAD_ARG; goto error; } 216 217 /* Parse unit and evaluate scaling factor*/ 218 unit = strtok_r(NULL, " \t", &token_ptr); 219 if (NULL == unit 220 || 0 == strcmp(unit, "m")){scaling_factor = 1e+00;} 221 else if (0 == strcmp(unit, "cm")){scaling_factor = 1e-02;} 222 else if (0 == strcmp(unit, "mm")){scaling_factor = 1e-03;} 223 else if (0 == strcmp(unit, "km")){scaling_factor = 1e+03;} 224 else { res = RES_BAD_ARG; goto error; } 225 226 /* Create geometry */ 227 res = geometry_create(sphin, filename, &geom); 228 if (RES_OK != res) { goto error; } 229 230 if (0 == strcmp(side, "FRONT")){ geom->side = SPHIN_SIDE_FRONT; } 231 else if (0 == strcmp(side, "BACK")){ geom->side = SPHIN_SIDE_BACK; } 232 else { res = RES_BAD_ARG; goto error; } 233 234 /* Load the stl */ 235 res = sstl_load(sstl, filename); 236 if (RES_OK != res) { goto error; } 237 res = sstl_get_desc(sstl, &sstl_desc); 238 if (RES_OK != res) { goto error; } 239 240 /* Parse coordinates and indices */ 241 darray_double_resize(&geom->coords, sstl_desc.vertices_count*3); 242 darray_size_t_resize(&geom->indices, sstl_desc.triangles_count*3); 243 coords = darray_double_data_get(&geom->coords); 244 indices = darray_size_t_data_get(&geom->indices); 245 246 FOR_EACH(i, 0, sstl_desc.vertices_count*3) { 247 coords[i] = (double)sstl_desc.vertices[i]*scaling_factor; 248 } 249 FOR_EACH(i, 0, sstl_desc.triangles_count*3) { 250 indices[i] = (size_t)sstl_desc.indices[i]; 251 } 252 253 exit: 254 sstl_ref_put(sstl); 255 *out_geom = geom; 256 return res; 257 error: 258 if (NULL != geom) { 259 SPHIN(geometry_ref_put(geom)); 260 geom = NULL; 261 } 262 goto exit; 263 } 264 265 /******************************************************************************* 266 * Exported functions 267 ******************************************************************************/ 268 res_T 269 sphin_geometry_ref_get 270 (struct sphin_geometry* geometry) 271 { 272 if (NULL == geometry) { 273 return RES_BAD_ARG; 274 } 275 ref_get(&geometry->ref); 276 return RES_OK; 277 } 278 279 res_T 280 sphin_geometry_ref_put 281 (struct sphin_geometry* geometry) 282 { 283 if (NULL == geometry) { 284 return RES_BAD_ARG; 285 } 286 ref_put(&geometry->ref, release_geometry); 287 return RES_OK; 288 } 289 290 res_T 291 sphin_geometry_get_desc 292 (const struct sphin_geometry* geometry, 293 struct sphin_geometry_descriptor* desc) 294 { 295 char* filename = NULL; 296 struct sphin_mesh mesh = SPHIN_MESH_NULL; 297 298 if (NULL == geometry || NULL == desc) { 299 return RES_BAD_ARG; 300 } 301 302 mesh.coords = darray_double_data_get 303 ((struct darray_double*)&geometry->coords); 304 mesh.indices = darray_size_t_data_get 305 ((struct darray_size_t*)&geometry->indices); 306 mesh.vertex_count = darray_double_size_get(&geometry->coords) / 3; 307 mesh.triangle_count = darray_size_t_size_get(&geometry->indices) / 3; 308 309 filename = (char*)str_get((struct str*)&geometry->stl_filename); 310 desc->filename = filename; 311 desc->side = geometry->side; 312 desc->mesh = mesh; 313 314 return RES_OK; 315 }