star-3dut

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

commit 18a28a7fadcdcb0e84f50095e3d539ac60469868
parent e12cb10f6d6e1a7825ae531c6be7e2f7d3e2e492
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Fri,  6 Oct 2017 17:25:28 +0200

Add and test the s3dut_create_truncated_sphere function.

Now both s3dut_create_sphere and s3dut_create_hemisphere are implemented
on top of s3dut_create_truncated_sphere.

Diffstat:
Mcmake/CMakeLists.txt | 2+-
Msrc/s3dut.h | 20++++++++++++++++++++
Dsrc/s3dut_hemisphere.c | 172-------------------------------------------------------------------------------
Msrc/s3dut_sphere.c | 207++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Asrc/test_s3dut_truncated_sphere.c | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 239 insertions(+), 227 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -44,7 +44,6 @@ set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) set(S3DUT_FILES_SRC s3dut_cuboid.c s3dut_cylinder.c - s3dut_hemisphere.c s3dut_mesh.c s3dut_sphere.c) set(S3DUT_FILES_INC s3dut_mesh.h) @@ -86,6 +85,7 @@ if(NOT NO_TEST) new_test(test_s3dut_thick_cylinder) new_test(test_s3dut_hemisphere) new_test(test_s3dut_sphere) + new_test(test_s3dut_truncated_sphere) rcmake_copy_runtime_libraries(test_s3dut_cuboid) endif() diff --git a/src/s3dut.h b/src/s3dut.h @@ -137,4 +137,24 @@ s3dut_create_hemisphere const unsigned nstacks, /* # subdivisions along Z axis int [2, INF) */ struct s3dut_mesh** hemisphere); +/* Create a triangulated UV sphere centered in 0 discretized in `nslices' + * around the Z axis and `nstacks' along the Z axis. 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 + * with a triangle fan whose center is on the Z axis or not, according to the + * `close_ends' argument bit mask. Face vertices are CCW ordered with respect + * to the sphere center, i.e. they are CW ordered from the outside point of + * view. */ +S3DUT_API res_T +s3dut_create_truncated_sphere + (struct mem_allocator* allocator, /* May be NULL <=> use default allocator */ + const double radius, /* 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_hemisphere.c b/src/s3dut_hemisphere.c @@ -1,172 +0,0 @@ -/* 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 "s3dut_mesh.h" - -/******************************************************************************* - * Helper functions - ******************************************************************************/ -static res_T -setup_hemisphere_coords - (struct mem_allocator* allocator, - double* coords, - const double radius, - const unsigned nslices, /* # subdivisions around the Z axis */ - const unsigned nstacks) /* # subdivisions along the Z axis */ -{ - enum { SIN, COS }; - struct darray_double sincos_theta; - struct darray_double sincos_phi; - double step_theta; - double step_phi; - size_t itheta; - size_t iphi; - size_t i; - res_T res = RES_OK; - ASSERT(coords && radius > 0 && nslices >= 2 && nstacks >= 2); - - darray_double_init(allocator, &sincos_theta); - darray_double_init(allocator, &sincos_phi); - - res = darray_double_resize(&sincos_theta, nslices*2/*sin & cos*/); - if(res != RES_OK) goto error; - res = darray_double_resize(&sincos_phi, (nstacks-1)*2/*sin & cos*/); - if(res != RES_OK) goto error; - - /* Precompute the sinus/cosine of the theta/phi angles */ - step_theta = 2*PI / (double)nslices; - FOR_EACH(itheta, 0, nslices) { - const double theta = -PI + (double)itheta * step_theta; - darray_double_data_get(&sincos_theta)[itheta*2 + SIN] = sin(theta); - darray_double_data_get(&sincos_theta)[itheta*2 + COS] = cos(theta); - } - step_phi = PI/((double)nstacks * 2.0); - FOR_EACH(iphi, 0, nstacks-1) { - const double phi = (double)(iphi) * step_phi; - darray_double_data_get(&sincos_phi)[iphi*2 + SIN] = sin(phi); - darray_double_data_get(&sincos_phi)[iphi*2 + COS] = cos(phi); - } - - /* Setup the contour vertices */ - i = 0; - FOR_EACH(itheta, 0, nslices) { - const double* theta = darray_double_cdata_get(&sincos_theta) + itheta*2; - FOR_EACH(iphi, 0, nstacks-1) { - const double* phi = darray_double_cdata_get(&sincos_phi) + iphi*2; - coords[i++] = radius * COS[theta] * COS[phi]; - coords[i++] = radius * SIN[theta] * COS[phi]; - coords[i++] = radius * SIN[phi]; - } - } - - /* Setup the top polar vertex */ - coords[i++] = 0; - coords[i++] = 0; - coords[i++] = radius; - -exit: - darray_double_release(&sincos_theta); - darray_double_release(&sincos_phi); - return res; -error: - goto exit; -} - -static void -setup_hemisphere_indices - (size_t* ids, - const unsigned nslices, /* # subdivisions around the Z axis */ - const unsigned nstacks) /* # subdivisions along the Z axis */ -{ - size_t i, itheta, iphi; - ASSERT(ids && nslices && nstacks); - - /* Define the indices of the contour primitives */ - i = 0; - FOR_EACH(itheta, 0, nslices) { - const size_t itheta0 = itheta * (nstacks - 1); - const size_t itheta1 = ((itheta + 1) % nslices) * (nstacks - 1); - FOR_EACH(iphi, 0, nstacks-2) { - const size_t iphi0 = iphi + 0; - const size_t iphi1 = iphi + 1; - - ids[i++] = itheta0 + iphi0; - ids[i++] = itheta0 + iphi1; - ids[i++] = itheta1 + iphi0; - - ids[i++] = itheta1 + iphi0; - ids[i++] = itheta0 + iphi1; - ids[i++] = itheta1 + iphi1; - } - } - - /* Define the indices of the polar primitives */ - FOR_EACH(itheta, 0, nslices) { - const size_t itheta0 = itheta * (nstacks - 1); - const size_t itheta1 = ((itheta + 1) % nslices) * (nstacks - 1); - - ids[i++] = nslices * (nstacks - 1); - ids[i++] = itheta1 + (nstacks - 2); - ids[i++] = itheta0 + (nstacks - 2); - } -} - - -/******************************************************************************* - * Exported functions - ******************************************************************************/ -res_T -s3dut_create_hemisphere - (struct mem_allocator* allocator, - const double radius, - const unsigned nslices, /* # subdivisions around the Z axis */ - const unsigned nstacks, /* # subdivisions along the Z axis */ - struct s3dut_mesh** mesh) -{ - struct s3dut_mesh* hemisphere = NULL; - size_t nverts; - size_t ntris; - res_T res = RES_OK; - - if(radius <= 0 || nslices < 2 || nstacks < 2 || !mesh) { - res = RES_BAD_ARG; - goto error; - } - - nverts = nslices*(nstacks-1)/* #contour verts*/ + 1/*polar vertex*/; - ntris = 2*nslices*(nstacks-2)/* #contour tris*/ + nslices/* #polar tris*/; - - res = mesh_create(allocator, S3DUT_MESH_HEMISPHERE, nverts, ntris, &hemisphere); - if(res != RES_OK) goto error; - - res = setup_hemisphere_coords(allocator, - darray_double_data_get(&hemisphere->coords), radius, nslices, nstacks); - if(res != RES_OK) goto error; - - setup_hemisphere_indices - (darray_size_t_data_get(&hemisphere->ids), nslices, nstacks); - -exit: - if(mesh) *mesh = hemisphere; - return res; -error: - if(hemisphere) { - S3DUT(mesh_ref_put(hemisphere)); - hemisphere = NULL; - } - goto exit; -} - diff --git a/src/s3dut_sphere.c b/src/s3dut_sphere.c @@ -24,26 +24,39 @@ setup_sphere_coords (struct mem_allocator* allocator, double* coords, const double radius, + const double z_range[2], const unsigned nslices, /* # subdivisions around the Z axis */ - const unsigned nstacks) /* # subdivisions along the Z axis */ + const unsigned nstacks, /* # subdivisions along the Z axis */ + const unsigned close_ends) { enum { SIN, COS }; struct darray_double sincos_theta; struct darray_double sincos_phi; double step_theta; - double step_phi; + 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 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 double phi_z_min + = bottom_truncated ? asin(CLAMP(z_range[0] / radius, -1, 1)) : -PI / 2; + const double phi_z_max + = top_truncated ? asin(CLAMP(z_range[1] / radius, -1, 1)) : +PI / 2; + double step_phi + = (phi_z_max - phi_z_min) / (double)(nrings + 1 - nb_truncated); size_t itheta; size_t iphi; size_t i; res_T res = RES_OK; - ASSERT(coords && radius > 0 && nslices >= 3 && nstacks >= 2); + ASSERT(coords && radius > 0); darray_double_init(allocator, &sincos_theta); darray_double_init(allocator, &sincos_phi); res = darray_double_resize(&sincos_theta, nslices*2/*sin & cos*/); if(res != RES_OK) goto error; - res = darray_double_resize(&sincos_phi, (nstacks-1)*2/*sin & cos*/); + res = darray_double_resize(&sincos_phi, nrings*2/*sin & cos*/); if(res != RES_OK) goto error; /* Precompute the sinus/cosine of the theta/phi angles */ @@ -53,9 +66,9 @@ setup_sphere_coords darray_double_data_get(&sincos_theta)[itheta*2 + SIN] = sin(theta); darray_double_data_get(&sincos_theta)[itheta*2 + COS] = cos(theta); } - step_phi = PI/(double)nstacks; - FOR_EACH(iphi, 0, nstacks-1) { - const double phi = -PI/2 + (double)(iphi+1) * step_phi; + FOR_EACH(iphi, 0, nrings) { + const double phi + = phi_z_min + (double)(iphi + !bottom_truncated) * step_phi; darray_double_data_get(&sincos_phi)[iphi*2 + SIN] = sin(phi); darray_double_data_get(&sincos_phi)[iphi*2 + COS] = cos(phi); } @@ -64,7 +77,7 @@ setup_sphere_coords i = 0; FOR_EACH(itheta, 0, nslices) { const double* theta = darray_double_cdata_get(&sincos_theta) + itheta*2; - FOR_EACH(iphi, 0, nstacks-1) { + FOR_EACH(iphi, 0, nrings) { const double* phi = darray_double_cdata_get(&sincos_phi) + iphi*2; coords[i++] = radius * COS[theta] * COS[phi]; coords[i++] = radius * SIN[theta] * COS[phi]; @@ -72,15 +85,19 @@ setup_sphere_coords } } - /* Setup the bottom polar vertex */ - coords[i++] = 0; - coords[i++] = 0; - coords[i++] = -radius; + if(close_bottom || !bottom_truncated) { + /* Setup the bottom polar vertex */ + coords[i++] = 0; + coords[i++] = 0; + coords[i++] = bottom_truncated ? z_range[0] : -radius; + } - /* Setup the top polar vertex */ - coords[i++] = 0; - coords[i++] = 0; - coords[i++] = radius; + if(close_top || !top_truncated) { + /* Setup the top polar vertex */ + coords[i++] = 0; + coords[i++] = 0; + coords[i++] = top_truncated ? z_range[1] : +radius; + } exit: darray_double_release(&sincos_theta); @@ -94,77 +111,101 @@ static void setup_sphere_indices (size_t* ids, const unsigned nslices, /* # subdivisions around the Z axis */ - const unsigned nstacks) /* # subdivisions along the Z axis */ + const unsigned nstacks, /* # subdivisions along the Z axis */ + const int top_truncated, + const int bottom_truncated, + const unsigned close_ends, + const int cw_out) { + const int 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; + size_t ibottom; + size_t itop; size_t i, itheta, iphi; ASSERT(ids && nslices && nstacks); - /* Define the indices of the contour primitives */ + /* Define the indices of the contour primitives */ i = 0; FOR_EACH(itheta, 0, nslices) { - const size_t itheta0 = itheta * (nstacks - 1); - const size_t itheta1 = ((itheta + 1) % nslices) * (nstacks - 1); - FOR_EACH(iphi, 0, nstacks-2) { + const size_t itheta0 = itheta * nrings; + const size_t itheta1 = ((itheta + 1) % nslices) * nrings; + FOR_EACH(iphi, 0, nrings-1) { const size_t iphi0 = iphi + 0; const size_t iphi1 = iphi + 1; - ids[i++] = itheta0 + iphi0; - ids[i++] = itheta0 + iphi1; - ids[i++] = itheta1 + iphi0; + ids[i] = itheta0 + iphi0; + ids[cw_out?i+1:i+2] = itheta0 + iphi1; + ids[cw_out?i+2:i+1] = itheta1 + iphi0; + i += 3; - ids[i++] = itheta1 + iphi0; - ids[i++] = itheta0 + iphi1; - ids[i++] = itheta1 + iphi1; + ids[i] = itheta1 + iphi0; + ids[cw_out?i+1:i+2] = itheta0 + iphi1; + ids[cw_out?i+2:i+1] = itheta1 + iphi1; + i += 3; } } /* Define the indices of the polar primitives */ FOR_EACH(itheta, 0, nslices) { - const size_t itheta0 = itheta * (nstacks - 1); - const size_t itheta1 = ((itheta + 1) % nslices) * (nstacks - 1); + const size_t itheta0 = itheta * nrings; + const size_t itheta1 = ((itheta + 1) % nslices) * nrings; - ids[i++] = nslices * (nstacks - 1); - ids[i++] = itheta0; - ids[i++] = itheta1; + if(close_bottom || !bottom_truncated) { + ibottom = nslices * nrings; + ids[i] = ibottom; + ids[cw_out?i+1:i+2] = itheta0; + ids[cw_out?i+2:i+1] = itheta1; + i += 3; + } - ids[i++] = nslices * (nstacks - 1) + 1; - ids[i++] = itheta1 + (nstacks - 2); - ids[i++] = itheta0 + (nstacks - 2); + if(close_top || !top_truncated) { + itop = (close_bottom || !bottom_truncated) + ? nslices * nrings + 1 : nslices * nrings; + ids[i] = itop; + ids[cw_out?i+1:i+2] = itheta1 + (nrings - 1); + ids[cw_out?i+2:i+1] = itheta0 + (nrings - 1); + i += 3; + } } } -/******************************************************************************* - * Exported function - ******************************************************************************/ -res_T -s3dut_create_sphere +static res_T +s3dut_create_truncated_sphere_do (struct mem_allocator* allocator, const double radius, - const unsigned nslices, /* # subdivisions around the Z axis */ - const unsigned nstacks, /* # subdivisions along the Z axis */ + const unsigned nslices, + const unsigned nstacks, + const double z_range[2], + const unsigned close_ends, struct s3dut_mesh** mesh) { struct s3dut_mesh* sphere = NULL; - size_t nverts; - size_t ntris; + 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 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 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*/; res_T res = RES_OK; - - if(radius <= 0 || nslices < 3 || nstacks < 2 || !mesh) { - res = RES_BAD_ARG; - goto error; - } - - nverts = nslices*(nstacks-1)/* #contour verts*/ + 2/*polar verts*/; - ntris = 2*nslices*(nstacks-2)/* #contour tris*/ + 2*nslices/* #polar tris*/; + ASSERT(0 <= nb_truncated && nb_truncated <= 2); 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, nslices, nstacks); + 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); + setup_sphere_indices(darray_size_t_data_get(&sphere->ids), + nslices, nstacks, top_truncated, bottom_truncated, close_ends, 1); exit: if(mesh) *mesh = sphere; @@ -177,3 +218,61 @@ error: goto exit; } + +/******************************************************************************* + * Exported function + ******************************************************************************/ +res_T +s3dut_create_sphere + (struct mem_allocator* allocator, + const double radius, + const unsigned nslices, + const unsigned nstacks, + struct s3dut_mesh** mesh) +{ + if(radius <= 0 || nslices < 3 || nstacks < 2 || !mesh) { + return RES_BAD_ARG; + } + + return s3dut_create_truncated_sphere_do + (allocator, radius, nslices, nstacks, NULL, 0, mesh); +} + +res_T +s3dut_create_hemisphere +(struct mem_allocator* allocator, + const double radius, + const unsigned nslices, + const unsigned nstacks, + struct s3dut_mesh** mesh) +{ + double z_range[2]; + + if(radius <= 0 || nslices < 2 || nstacks < 2 || !mesh) { + return RES_BAD_ARG; + } + + z_range[0] = 0; + z_range[1] = +radius; + return s3dut_create_truncated_sphere_do + (allocator, radius, nslices, nstacks, z_range, 0, mesh); +} + +res_T +s3dut_create_truncated_sphere + (struct mem_allocator* allocator, + const double radius, + const unsigned nslices, + const unsigned nstacks, + const double z_range[2], + const unsigned close_ends, + struct s3dut_mesh** mesh) +{ + if(radius <= 0 || nslices < 3 || nstacks < 2 || !mesh + || (z_range && z_range[0] >= z_range[1])) { + return RES_BAD_ARG; + } + + return s3dut_create_truncated_sphere_do + (allocator, radius, nslices, nstacks, z_range, close_ends, mesh); +} diff --git a/src/test_s3dut_truncated_sphere.c b/src/test_s3dut_truncated_sphere.c @@ -0,0 +1,65 @@ +/* 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_truncated_sphere + z_range[0] = z_range[1] = 0; + CHECK(CR_TS(NULL, 1, 16, 8, z_range, 0, &msh), RES_BAD_ARG); + z_range[1] = -0.5; + CHECK(CR_TS(NULL, 1, 16, 8, z_range, 0, &msh), RES_BAD_ARG); + z_range[1] = +0.5; + CHECK(CR_TS(NULL, 1, 16, 8, z_range, 0, &msh), RES_OK); + CHECK(s3dut_mesh_ref_put(msh), RES_OK); + CHECK(CR_TS(NULL, 1, 16, 8, NULL, 0, &msh), RES_OK); + CHECK(s3dut_mesh_ref_put(msh), RES_OK); + + CHECK(CR_TS(&allocator, 1, 16, 8, NULL, 0, &msh), RES_OK); + CHECK(s3dut_mesh_ref_put(msh), RES_OK); + + z_range[0] = 0; + z_range[1] = 0.9; + CHECK(CR_TS(&allocator, 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; +} +