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:
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;
+}
+