star-3dut

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

commit b192356b48a4ee7bab9b6e21bc4316dfc0f8c9e9
parent 57db410e881295c3fbfa1846ece9ce0a5364067a
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Mon,  9 Oct 2017 16:01:39 +0200

Add and test s3dut_create_thick_truncated_sphere function.

Diffstat:
Mcmake/CMakeLists.txt | 1+
Msrc/s3dut.h | 22++++++++++++++++++++++
Msrc/s3dut_mesh.h | 3++-
Msrc/s3dut_sphere.c | 218++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Asrc/test_s3dut_thick_truncated_sphere.c | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 266 insertions(+), 31 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -86,6 +86,7 @@ if(NOT NO_TEST) new_test(test_s3dut_hemisphere) new_test(test_s3dut_sphere) new_test(test_s3dut_truncated_sphere) + new_test(test_s3dut_thick_truncated_sphere) rcmake_copy_runtime_libraries(test_s3dut_cuboid) endif() diff --git a/src/s3dut.h b/src/s3dut.h @@ -157,4 +157,26 @@ s3dut_create_truncated_sphere Meaningless if no truncation. */ struct s3dut_mesh** sphere); +/* Create a triangulated thick UV sphere centered in 0 discretized in `nslices' + * around the Z axis and `nstacks' along the Z axis, with walls of thickness + * `thickness'. The top and the bottom of the sphere can be truncated at some + * specified z, according to the `z_range' parameter. If truncated, the top and + * the bottom of the sphere can be closed or not, according to the `close_ends' + * argument bit mask. Closed ends are closed by a wall of thickness `thickness' + * made of 2 triangle fans centered on the Z axis. Face vertices are CCW + * ordered with respect to the walls' inside, i.e. they are CW ordered from any + * point of view outside of the walls. */ +S3DUT_API res_T +s3dut_create_thick_truncated_sphere + (struct mem_allocator* allocator, /* May be NULL <=> use default allocator */ + const double radius, /* In ]thickness, INF); exterior radius */ + const double thickness, /* In ]0, INF) */ + const unsigned nslices, /* # subdivisions around Z axis in [3, INF) */ + const unsigned nstacks, /* # subdivisions along Z axis in [2, INF) */ + const double z_range[2], /* Mesh only the sphere in the z_range range; + can be NULL => keep the whole sphere */ + const unsigned close_ends, /* Close truncated ends of the sphere? + Meaningless if no truncation. */ + struct s3dut_mesh** sphere); + #endif /* S3DUT_H */ diff --git a/src/s3dut_mesh.h b/src/s3dut_mesh.h @@ -25,7 +25,8 @@ enum s3dut_mesh_type { S3DUT_MESH_CYLINDER, S3DUT_MESH_THICK_CYLINDER, S3DUT_MESH_HEMISPHERE, - S3DUT_MESH_SPHERE + S3DUT_MESH_SPHERE, + S3DUT_MESH_THICK_SPHERE }; struct s3dut_mesh { diff --git a/src/s3dut_sphere.c b/src/s3dut_sphere.c @@ -22,7 +22,7 @@ static res_T setup_sphere_coords (struct mem_allocator* allocator, - double* coords, + double** coords_ptr, const double radius, const double z_range[2], const unsigned nslices, /* # subdivisions around the Z axis */ @@ -30,15 +30,16 @@ setup_sphere_coords const unsigned close_ends) { enum { SIN, COS }; + double* coords; struct darray_double sincos_theta; struct darray_double sincos_phi; double step_theta; const int top_truncated = z_range && z_range[1] < +radius; const int bottom_truncated = z_range && z_range[0] > -radius; - const int nb_truncated = top_truncated + bottom_truncated; + const unsigned nb_truncated = top_truncated + bottom_truncated; const int close_top = top_truncated && (close_ends & S3DUT_END_TOP); const int close_bottom = bottom_truncated && (close_ends & S3DUT_END_BOTTOM); - const int nrings = nstacks - 1 + nb_truncated; + const unsigned nrings = nstacks - 1 + nb_truncated; const double phi_z_min = bottom_truncated ? asin(CLAMP(z_range[0] / radius, -1, 1)) : -PI / 2; const double phi_z_max @@ -47,10 +48,11 @@ setup_sphere_coords = (phi_z_max - phi_z_min) / (double)(nrings + 1 - nb_truncated); size_t itheta; size_t iphi; - size_t i; + size_t i = 0; res_T res = RES_OK; - ASSERT(coords && radius > 0); + ASSERT(coords_ptr && *coords_ptr && radius > 0); + coords = *coords_ptr; darray_double_init(allocator, &sincos_theta); darray_double_init(allocator, &sincos_phi); @@ -102,25 +104,29 @@ setup_sphere_coords exit: darray_double_release(&sincos_theta); darray_double_release(&sincos_phi); + *coords_ptr = coords + i; return res; error: goto exit; } -static void +static size_t* setup_sphere_indices (size_t* ids, + const size_t offset, const unsigned nslices, /* # subdivisions around the Z axis */ const unsigned nstacks, /* # subdivisions along the Z axis */ - const int top_truncated, - const int bottom_truncated, + const double radius, + const double z_range[2], const unsigned close_ends, const int cw_out) { - const int nb_truncated = top_truncated + bottom_truncated; + const int top_truncated = z_range && z_range[1] < +radius; + const int bottom_truncated = z_range && z_range[0] > -radius; + const unsigned nb_truncated = top_truncated + bottom_truncated; const int close_top = top_truncated && (close_ends & S3DUT_END_TOP); const int close_bottom = bottom_truncated && (close_ends & S3DUT_END_BOTTOM); - const int nrings = nstacks - 1 + nb_truncated; + const unsigned nrings = nstacks - 1 + nb_truncated; size_t ibottom; size_t itop; size_t i, itheta, iphi; @@ -129,8 +135,8 @@ setup_sphere_indices /* Define the indices of the contour primitives */ i = 0; FOR_EACH(itheta, 0, nslices) { - const size_t itheta0 = itheta * nrings; - const size_t itheta1 = ((itheta + 1) % nslices) * nrings; + const size_t itheta0 = offset + itheta * nrings; + const size_t itheta1 = offset + ((itheta + 1) % nslices) * nrings; FOR_EACH(iphi, 0, nrings-1) { const size_t iphi0 = iphi + 0; const size_t iphi1 = iphi + 1; @@ -149,12 +155,12 @@ setup_sphere_indices /* Define the indices of the polar primitives */ FOR_EACH(itheta, 0, nslices) { - const size_t itheta0 = itheta * nrings; - const size_t itheta1 = ((itheta + 1) % nslices) * nrings; + const size_t itheta0 = offset + itheta * nrings; + const size_t itheta1 = offset + ((itheta + 1) % nslices) * nrings; if(close_bottom || !bottom_truncated) { ibottom = nslices * nrings; - ids[i] = ibottom; + ids[i] = offset + ibottom; ids[cw_out?i+1:i+2] = itheta0; ids[cw_out?i+2:i+1] = itheta1; i += 3; @@ -163,12 +169,66 @@ setup_sphere_indices if(close_top || !top_truncated) { itop = (close_bottom || !bottom_truncated) ? nslices * nrings + 1 : nslices * nrings; - ids[i] = itop; + ids[i] = offset + itop; ids[cw_out?i+1:i+2] = itheta1 + (nrings - 1); ids[cw_out?i+2:i+1] = itheta0 + (nrings - 1); i += 3; } } + return ids + i; +} + +static size_t* +close_wall + (size_t* ids, + const size_t fst_id_out, + const size_t fst_id_in, + const unsigned nslices, + const unsigned external_nrings, + const unsigned internal_nrings, + const int bottom) +{ + size_t islice; + size_t i = 0; + ASSERT(ids && nslices >= 3 && external_nrings >= 1 && internal_nrings >= 1); + + FOR_EACH(islice, 0, nslices) { + ids[i] = fst_id_out + islice * external_nrings; + ids[bottom?i+1:i+2] = fst_id_in + ((islice+1) % nslices) * internal_nrings; + ids[bottom?i+2:i+1] = fst_id_in + islice * internal_nrings; + i += 3; + + ids[i] = fst_id_out + islice * external_nrings; + ids[bottom?i+1:i+2] = fst_id_out + ((islice+1) % nslices) * external_nrings; + ids[bottom?i+2:i+1] = fst_id_in + ((islice+1) % nslices) * internal_nrings; + i += 3; + } + return ids + i; +} + +static void +sphere_accum_counts + (const double radius, + const unsigned nslices, + const unsigned nstacks, + const double z_range[2], + const unsigned close_ends, + unsigned* nrings, + size_t* ntris, + size_t* nverts) +{ + const int top_truncated = z_range && z_range[1] < +radius; + const int bottom_truncated = z_range && z_range[0] > -radius; + const unsigned nb_truncated = top_truncated + bottom_truncated; + const int close_top = top_truncated && (close_ends & S3DUT_END_TOP); + const int close_bottom = bottom_truncated && (close_ends & S3DUT_END_BOTTOM); + const unsigned nb_closed_ends = (close_top ? 1 : 0) + (close_bottom ? 1 : 0); + const unsigned nb_pole_vrtx = nb_closed_ends + (2 - nb_truncated); + ASSERT(z_range && nrings && ntris && nverts); + *nrings = nstacks - 1 + nb_truncated;; + *nverts += nslices*(*nrings) /* #contour verts*/ + nb_pole_vrtx; /*polar verts*/ + *ntris += 2 * nslices * (*nrings -1) /* #contour tris */ + + (2 - nb_truncated + nb_closed_ends) * nslices; /* #polar tris */ } /******************************************************************************* @@ -188,17 +248,17 @@ s3dut_create_sphere res_T s3dut_create_hemisphere -(struct mem_allocator* allocator, - const double radius, - const unsigned nslices, - const unsigned nstacks, - struct s3dut_mesh** mesh) + (struct mem_allocator* allocator, + const double radius, + const unsigned nslices, + const unsigned nstacks, + struct s3dut_mesh** mesh) { double z_range[2]; z_range[0] = 0; z_range[1] = +radius; return s3dut_create_truncated_sphere - (allocator, radius, nslices, nstacks, z_range, 0, mesh); + (allocator, radius, nslices, nstacks, z_range, 0, mesh); } res_T @@ -214,16 +274,17 @@ s3dut_create_truncated_sphere struct s3dut_mesh* sphere = NULL; const int top_truncated = z_range && z_range[1] < +radius; const int bottom_truncated = z_range && z_range[0] > -radius; - const int nb_truncated = top_truncated + bottom_truncated; + const unsigned nb_truncated = top_truncated + bottom_truncated; const int close_top = top_truncated && (close_ends & S3DUT_END_TOP); const int close_bottom = bottom_truncated && (close_ends & S3DUT_END_BOTTOM); - const int nb_closed_ends = (close_top ? 1 : 0) + (close_bottom ? 1 : 0); - const int nb_pole_vrtx = nb_closed_ends + (2 - nb_truncated); - const int nrings = nstacks - 1 + nb_truncated; + const unsigned nb_closed_ends = (close_top ? 1 : 0) + (close_bottom ? 1 : 0); + const unsigned nb_pole_vrtx = nb_closed_ends + (2 - nb_truncated); + const unsigned nrings = nstacks - 1 + nb_truncated; const size_t nverts = nslices*nrings/* #contour verts*/ + nb_pole_vrtx/*polar verts*/; const size_t ntris = 2 * nslices*(nrings - 1)/* #contour tris*/ + nb_pole_vrtx*nslices/* #polar tris*/; + double* coords; res_T res = RES_OK; ASSERT(0 <= nb_truncated && nb_truncated <= 2); @@ -236,12 +297,13 @@ s3dut_create_truncated_sphere res = mesh_create(allocator, S3DUT_MESH_SPHERE, nverts, ntris, &sphere); if(res != RES_OK) goto error; - res = setup_sphere_coords(allocator, darray_double_data_get(&sphere->coords), - radius, z_range, nslices, nstacks, close_ends); + coords = darray_double_data_get(&sphere->coords); + res = setup_sphere_coords(allocator, &coords, radius, z_range, nslices, + nstacks, close_ends); if(res != RES_OK) goto error; - setup_sphere_indices(darray_size_t_data_get(&sphere->ids), - nslices, nstacks, top_truncated, bottom_truncated, close_ends, 1); + setup_sphere_indices(darray_size_t_data_get(&sphere->ids), 0, + nslices, nstacks, radius, z_range, close_ends, 1); exit: if(mesh) *mesh = sphere; @@ -253,3 +315,98 @@ error: } goto exit; } + +res_T +s3dut_create_thick_truncated_sphere + (struct mem_allocator* allocator, + const double radius, + const double thickness, + const unsigned nslices, + const unsigned nstacks, + const double z_range[2], + const unsigned c_e, + struct s3dut_mesh** mesh) +{ + struct s3dut_mesh* sphere = NULL; + unsigned close_ends = c_e; + double* coords = NULL; + double* prev_coords; + size_t* ids_out = NULL; + size_t* ids_in = NULL; + size_t* ids_walls = NULL; + size_t id_offset; + const double internal_radius = radius - thickness; + const int top_truncated = z_range && (z_range[1] < +radius); + const int bottom_truncated = z_range && (z_range[0] > -radius); + const int close_top = top_truncated && (close_ends & S3DUT_END_TOP); + const int close_bottom = bottom_truncated && (close_ends & S3DUT_END_BOTTOM); + const int top_seam = z_range && (z_range[1] < internal_radius) && !close_top; + const int bottom_seam + = z_range && (z_range[0] > -internal_radius) && ! close_bottom; + unsigned external_nrings; + unsigned internal_nrings; + const unsigned nb_seams = top_seam + bottom_seam; + size_t nverts = 0; + size_t ntris = 0; + double z_internal_range[2]; + res_T res = RES_OK; + + if(radius <= thickness || thickness <= 0 || nslices < 3 || nstacks < 2 + || !mesh || (z_range && z_range[0] >= z_range[1])) { + return RES_BAD_ARG; + } + + /* Special case when a single sphere is truncated */ + if (top_truncated && (z_range[1] >= internal_radius)) { + close_ends |= S3DUT_END_TOP; /* close the external sphere's top end */ + } + if (bottom_truncated && (z_range[0] <= -internal_radius)) { + close_ends |= S3DUT_END_BOTTOM; /* close the external sphere's bottom end */ + } + z_internal_range[0] + = (bottom_truncated && !close_bottom) ? z_range[0] : z_range[0] + thickness; + z_internal_range[1] + = (top_truncated && !close_top) ? z_range[1] : z_range[1] - thickness; + sphere_accum_counts(radius, nslices, nstacks, z_range, close_ends, + &external_nrings, &ntris, &nverts); + sphere_accum_counts(radius-thickness, nslices, nstacks, z_internal_range, + close_ends, &internal_nrings, &ntris, &nverts); + ntris += 2 * nb_seams * nslices; /* # seam tris */ + res = mesh_create(allocator, S3DUT_MESH_THICK_SPHERE, nverts, ntris, &sphere); + if (res != RES_OK) goto error; + + coords = darray_double_data_get(&sphere->coords); + ids_out = darray_size_t_data_get(&sphere->ids); + /* External sphere */ + prev_coords = coords; + setup_sphere_coords + (allocator, &coords, radius, z_range, nslices, nstacks, close_ends); + ids_in = setup_sphere_indices(ids_out, 0, nslices, nstacks, radius, z_range, + close_ends, 1); + /* Internal sphere */ + id_offset = coords - prev_coords; + ASSERT(id_offset % 3 == 0); + id_offset /= 3; + setup_sphere_coords(allocator, &coords, radius - thickness, z_internal_range, + nslices, nstacks, close_ends); + ids_walls = setup_sphere_indices (ids_in, id_offset, nslices, + nstacks, radius - thickness, z_internal_range, close_ends, 0); + if(bottom_seam) { + ids_walls = close_wall(ids_walls, 0, id_offset, nslices, external_nrings, + internal_nrings, 1); + } + if(top_seam) { + ids_walls = close_wall(ids_walls, nstacks, id_offset + nstacks, nslices, + external_nrings, internal_nrings, 0); + } + +exit: + if(mesh) *mesh = sphere; + return res; +error: + if(sphere) { + S3DUT(mesh_ref_put(sphere)); + sphere = NULL; + } + goto exit; +} +\ No newline at end of file diff --git a/src/test_s3dut_thick_truncated_sphere.c b/src/test_s3dut_thick_truncated_sphere.c @@ -0,0 +1,53 @@ +/* Copyright (C) |Meso|Star> 2016-2017 (contact@meso-star.com) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ + +#include "s3dut.h" +#include "test_s3dut_utils.h" + +#include <rsys/double3.h> +#include <rsys/math.h> + +int +main(int argc, char** argv) +{ + struct mem_allocator allocator; + struct s3dut_mesh* msh; + struct s3dut_mesh_data data; + double z_range[2]; + (void)argc, (void)argv; + + CHECK(mem_init_proxy_allocator(&allocator, &mem_default_allocator), RES_OK); + + #define CR_TS s3dut_create_thick_truncated_sphere + + z_range[0] = 0; + z_range[1] = 0.8; + CHECK(CR_TS(&allocator, 1, 0.1, 16, 8, z_range, S3DUT_END_BOTTOM, &msh), RES_OK); + + CHECK(s3dut_mesh_get_data(msh, &data), RES_OK); + NCHECK(data.positions, NULL); + NCHECK(data.indices, NULL); + #undef CR_TS + + dump_mesh_data(stdout, &data); + + CHECK(s3dut_mesh_ref_put(msh), RES_OK); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHECK(mem_allocated_size(), 0); + return 0; +} +