star-cad

Geometric operators for computer-aided design
git clone git://git.meso-star.fr/star-cad.git
Log | Files | Refs | README | LICENSE

commit 2fe3ce0f77d47bfa879ca9e6e8414da6a0682dc2
parent b032516cf6b787787637de02016ae648f6d05c05
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Mon,  3 Mar 2025 10:50:21 +0100

Merge branch 'release_0.5.1'

Diffstat:
MMakefile | 2++
MREADME.md | 1+
Mconfig.mk | 2+-
Msrc/scad.h | 105+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/scad_device.c | 40+++++++++++++++++++++++++++++++++++++---
Msrc/scad_device.h | 6++++++
Msrc/scad_geometry.c | 442++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Asrc/test_export2.c | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 685 insertions(+), 15 deletions(-)

diff --git a/Makefile b/Makefile @@ -128,6 +128,7 @@ lint: TEST_SRC =\ src/test_api.c\ src/test_export.c\ + src/test_export2.c\ src/test_lifetime.c\ src/test_partition.c TEST_OBJ = $(TEST_SRC:.c=.o) @@ -176,6 +177,7 @@ $(TEST_OBJ): config.mk scad-local.pc test_api \ test_export \ +test_export2 \ test_lifetime \ test_partition \ : config.mk scad-local.pc $(LIBNAME) diff --git a/README.md b/README.md @@ -30,6 +30,7 @@ Edit config.mk as needed, then run: - BugFix STL output of geometries when forcing normals - Improve C89 compliance and fix header file to be used in c++ code - Change build mechanics from cmake to POSIX makefiles +- Change manpages format from asciidoc to mdoc - Upgrade dependencies - Improve and fix some tests diff --git a/config.mk b/config.mk @@ -1,4 +1,4 @@ -VERSION = 0.5.0 +VERSION = 0.5.1 PREFIX = /usr/local LIB_TYPE = SHARED diff --git a/src/scad.h b/src/scad.h @@ -47,7 +47,7 @@ struct scad_geometry; /* Wrapping of dimTags gmsh description */ /* Verbosity levels */ enum scad_verbosity_levels { - scad_verbosSCAD_ity_fatal_errors = 0, + scad_verbosity_fatal_errors = 0, Scad_verbosity_errors = 1, Scad_verbosity_warnings = 2, Scad_verbosity_direct = 3, @@ -59,8 +59,11 @@ enum scad_verbosity_levels { /* Mesh algorithms */ enum scad_mesh_algorithm { Scad_meshAdapt = 1, + Scad_Automatic = 2, /* Delaunay on planes, meshAdapt elsewhere */ + Scad_Initial_Mesh_Only = 3, /* Avoid new point creation */ Scad_Delaunay = 5, - Scad_frontal_Delaunay = 6 + Scad_frontal_Delaunay = 6, + Scad_Quasi_Structured = 11 }; enum scad_sizes_extend_from_boundary { @@ -84,17 +87,25 @@ enum scad_log_refcounting { Scad_log_geometry = BIT(2) }; +/* A type to specify the kind of mesh size specification set by a call to the + * scad_geometries_set_mesh_size_modifier API call */ +enum scad_size_modifier_type { + Scad_absolute_size, + Scad_size_factor +}; + /* A type to specify options for the gmsh library */ struct scad_options { struct { - enum scad_mesh_algorithm Algorithm; - enum scad_sizes_extend_from_boundary MeshSizeExtendFromBoundary; double MeshSizeFactor; double MeshSizeFromCurvature; - int MeshSizeFromPoints; double MeshSizeMax; double MeshSizeMin; double Smoothing; + int MeshSizeFromPoints; + int MeshOnlyVisible; + enum scad_mesh_algorithm Algorithm; + enum scad_sizes_extend_from_boundary MeshSizeExtendFromBoundary; enum scad_stl_solids StlOneSolidPerSurface; } Mesh; struct { @@ -113,8 +124,10 @@ struct scad_options { }; #define SCAD_DEFAULT_OPTIONS__ \ - { { Scad_frontal_Delaunay, Scad_surfaces_and_volumes, 1, 36, 1, 1e+22, 1e-6, \ - 1, Scad_one_solid_per_physical_surface }, \ + { { 1, 36, 1e+22, 1e-6, 1, 1, 1, \ + Scad_frontal_Delaunay, \ + Scad_surfaces_and_volumes, \ + Scad_one_solid_per_physical_surface }, \ { Scad_verbosity_errors, 1 }, \ { 1 }, \ { 0, 0, Scad_log_none, 0 } \ @@ -165,6 +178,10 @@ SCAD_API res_T scad_set_options (const struct scad_options* options); /* May be NULL: set default */ +SCAD_API res_T +scad_get_options + (struct scad_options* options); + /******************************************************************************* * Geometry API - A geometry is a primitive, a group of primitives, or the * result of an operation on geometries. @@ -230,13 +247,27 @@ scad_geometry_get_mass double* mass); /* Get the center of mass of the various components of the geometry. - * Note that `center' must be allocated be the caller with enough room for (at - * least) 3 times the count of geom (scad_geometry_ge_count) doubles */ + * Note that `center' must be allocated by the caller with enough room for (at + * least) 3 times the count of geom (scad_geometry_get_count) doubles */ SCAD_API res_T scad_geometry_get_centerofmass (struct scad_geometry* geom, double* center); +SCAD_API res_T +scad_geometry_get_closest_point + (struct scad_geometry* geom, + const double* from, + double* closest, + double* closest_distance); + +/* Get the Boundig Box of geometry `geom' */ +SCAD_API res_T +scad_geometry_get_bounding_box + (struct scad_geometry* geom, + double min[3], + double max[3]); + /* Add a rectangle to the scene, defined by a point `xyz' and * `dxdy' the extents along the x-, y-axes. */ SCAD_API res_T @@ -382,7 +413,7 @@ scad_geometries_partition const int flags, struct scad_geometry** out_geometries); -/* Get the boundary of the geometry `geom'. */ +/* Get the boundary of the geometries in `geometries'. */ SCAD_API res_T scad_geometry_boundary (const char* name, /* Can be NULL */ @@ -397,6 +428,13 @@ scad_geometry_copy const char* name, /* Can be NULL */ struct scad_geometry** out_copy); +/* copy the geometry `geom'. */ +SCAD_API res_T +scad_geometry_set_visibility + (const struct scad_geometry* geom, + int visible, + int recursive); + /* Change the name of geometry `geom'. */ SCAD_API res_T scad_geometry_rename @@ -533,6 +571,53 @@ SCAD_API res_T scad_scene_mesh (void); +/* Flag `target' geometries as being the result of applying the `affine' + * tranform to `source' geometries. + * The result is that the mesh generated for `target' is the image on the mesh + * generated for `source' through the `affine' transform. + * Only apply to surfaces (dimension 2). If called on a volume, it applies to + * its 2D constituents. + * The two sets of surfaces must match exactly (same number of points, etc.). */ +SCAD_API res_T +scad_geometries_set_periodic + (struct scad_geometry** source, + const size_t source_count, + struct scad_geometry** target, + const size_t target_count, + double affine[16]); + +/* Set a size modifier for geometries in `geometries'. + * When meshing these geometries, triangles' size will be either size*modifier, + * or modifier where size would be the size of the triangle in the absence of a + * size modifier. + * The size modifier is applied recursively down to dimension 0 (points). + * If multiple size modifiers are applied, the order matters as the last applied + * size modifier remains. + * To reset a size modifier, just apply a new Scad_size_factor modifier of 1. */ +SCAD_API res_T +scad_geometries_set_mesh_size_modifier + (struct scad_geometry** geometries, + const size_t geometries_count, + enum scad_size_modifier_type type, + double modifier); + +/* Set a specific mesh algorithm for geometries in `geometries'. + * Only apply to surfaces (dimension 2). If called on a volume, it applies to + * its 2D constituents. */ +SCAD_API res_T +scad_geometries_set_mesh_algorithm + (struct scad_geometry** geometries, + const size_t geometries_count, + enum scad_mesh_algorithm algorithm); + +/* Clear the mesh of the geometries in `geometries'. + * Note that the mesh of a geometry can only be cleared if it is not on the + * boundary of another geometry with a non-empty mesh. */ +SCAD_API res_T +scad_geometries_clear_mesh + (struct scad_geometry** geometries, + const size_t geometries_count); + /* Get the normal of the geometry `geom' at position `p'. * The normal is set in `N' and the underlying 2D entity to which `p' belongs is * returned as a new geometry in `out_geometry'. */ diff --git a/src/scad_device.c b/src/scad_device.c @@ -120,6 +120,10 @@ device_release(struct scad_device* dev) htable_geometries_release(&dev->allgeom); device_release_tags_of_dim(dev, 2); device_release_tags_of_dim(dev, 3); + htable_size_modifiers_release(&dev->size_modifiers_by_dim[0]); + htable_size_modifiers_release(&dev->size_modifiers_by_dim[1]); + htable_size_modifiers_release(&dev->size_modifiers_by_dim[2]); + htable_size_modifiers_release(&dev->size_modifiers_by_dim[3]); if(log) { logger_print(dev->logger, log_type, "End finalizing scad.\n"); } @@ -597,6 +601,10 @@ scad_initialize htable_geometries_init(allocator, &g_device->allgeom); htable_tags2desc_init(allocator, &g_device->tags2desc[0]); htable_tags2desc_init(allocator, &g_device->tags2desc[1]); + htable_size_modifiers_init(allocator, &g_device->size_modifiers_by_dim[0]); + htable_size_modifiers_init(allocator, &g_device->size_modifiers_by_dim[1]); + htable_size_modifiers_init(allocator, &g_device->size_modifiers_by_dim[2]); + htable_size_modifiers_init(allocator, &g_device->size_modifiers_by_dim[3]); /* Init to default */ scad_set_options(NULL); @@ -679,15 +687,16 @@ scad_set_options dev = get_device(); keep = dev->options; - SET_GMSH_OPTION_INT(Mesh.Algorithm); - SET_GMSH_OPTION_INT(Mesh.MeshSizeExtendFromBoundary); SET_GMSH_OPTION_DOUBLE(Mesh.MeshSizeFactor); SET_GMSH_OPTION_DOUBLE(Mesh.MeshSizeFromCurvature); - SET_GMSH_OPTION_INT(Mesh.MeshSizeFromPoints); SET_GMSH_OPTION_DOUBLE(Mesh.MeshSizeMax); SET_GMSH_OPTION_DOUBLE(Mesh.MeshSizeMin); SET_GMSH_OPTION_DOUBLE(Mesh.Smoothing); SET_GMSH_OPTION_INT(Mesh.StlOneSolidPerSurface); + SET_GMSH_OPTION_INT(Mesh.MeshOnlyVisible); + SET_GMSH_OPTION_INT(Mesh.Algorithm); + SET_GMSH_OPTION_INT(Mesh.MeshSizeExtendFromBoundary); + SET_GMSH_OPTION_INT(Mesh.MeshSizeFromPoints); SET_GMSH_OPTION_INT(General.Verbosity); SET_GMSH_OPTION_INT(General.ExpertMode); @@ -715,4 +724,29 @@ error: goto exit; } +#undef SET_GMSH_OPTION_INT #undef SET_GMSH_OPTION_DOUBLE + +res_T +scad_get_options + (struct scad_options* options) +{ + res_T res = RES_OK; + struct scad_device* dev = NULL; + + if(! options) { + res = RES_BAD_ARG; + goto error; + } + + ERR(check_device(FUNC_NAME)); + + dev = get_device(); + + *options = dev->options; + +exit: + return res; +error: + goto exit; +} diff --git a/src/scad_device.h b/src/scad_device.h @@ -115,6 +115,11 @@ tag_desc_copy_and_release #define HTABLE_DATA_FUNCTOR_COPY_AND_RELEASE tag_desc_copy_and_release #include <rsys/hash_table.h> +#define HTABLE_NAME size_modifiers +#define HTABLE_KEY int +#define HTABLE_DATA double +#include <rsys/hash_table.h> + struct scad_device { struct logger* logger; struct mem_allocator* allocator; @@ -122,6 +127,7 @@ struct scad_device { struct htable_names geometry_names; struct htable_geometries allgeom; struct htable_tags2desc tags2desc[2]; /* Only geoms for 2D and 3D tags for now */ + struct htable_size_modifiers size_modifiers_by_dim[4]; int verbose; int need_synchro; diff --git a/src/scad_geometry.c b/src/scad_geometry.c @@ -251,6 +251,123 @@ error: goto exit; } +static res_T +store_tags + (int* dimTags, + size_t count, + struct htable_tags t[4]) +{ + res_T res = RES_OK; + char one = 1; + size_t i; + ASSERT((dimTags || count == 0) && t); + for(i = 0; i < count; i += 2) { + int dim = dimTags[i]; + int tag = dimTags[i+1]; + ASSERT(dim >= 0 && dim <= 3); + ERR(htable_tags_set(t+dim, &tag, &one)); + } + +exit: + return res; +error: + goto exit; +} + +static res_T +gather_tags_recursive + (struct scad_geometry** geometries, + const size_t geometries_count, + const int down_to_dim, + int* out_dimTags[4], + size_t out_dimTags_n[4]) +{ + res_T res = RES_OK; + int* dimTags[4] = { NULL, NULL, NULL, NULL }, *sub = NULL; + size_t i, sz[4]; + struct scad_device* dev = get_device(); + struct mem_allocator* allocator = dev->allocator; + struct htable_tags t[4]; + struct htable_tags_iterator it, end; + int dim; + + ASSERT((geometries || geometries_count == 0) && out_dimTags && out_dimTags_n); + ASSERT(0 <= down_to_dim && down_to_dim <= 3); + + for(i = (size_t)down_to_dim; i < 4; i++) { + htable_tags_init(allocator, t+i); + } + + /* list tags by dimension and remove duplicates */ + for(i = 0; i < geometries_count; i++) { + if(!geometries[i]) { + res = RES_BAD_ARG; + goto error; + } + ERR(store_tags(geometries[i]->gmsh_dimTags, geometries[i]->gmsh_dimTags_n, t)); + } + + /* Recursively build result by dimension and list constituents, + * begining with dim==3 */ + for(dim = 3; dim >= down_to_dim; dim--) { + size_t c = 0; + sz[dim] = 2 * htable_tags_size_get(t+dim); + if(sz[dim] == 0) continue; + dimTags[dim] = MEM_ALLOC(allocator, sz[dim] * sizeof(*dimTags)); + if(!dimTags[dim]) { + res = RES_MEM_ERR; + goto error; + } + htable_tags_begin(t+dim, &it); + htable_tags_end(t+dim, &end); + while(!htable_tags_iterator_eq(&it, &end)) { + dimTags[dim][c++] = dim; + dimTags[dim][c++] = *htable_tags_iterator_key_get(&it); + htable_tags_iterator_next(&it); + } + if(dim > down_to_dim) { + int ierr; + size_t subn; + gmshModelGetBoundary(dimTags[dim], sz[dim], &sub, &subn, 0, 0, 0, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + ERR(store_tags(sub, subn, t)); + gmshFree(sub); sub = NULL; + } + ASSERT(sz[dim] == c); + } + + for(i = (size_t)down_to_dim; i < 4; i++) { + out_dimTags_n[i] = sz[i]; + out_dimTags[i] = dimTags[i]; + } + +exit: + if(sub) gmshFree(sub); + for(i = (size_t)down_to_dim; i < 4; i++) { + htable_tags_release(t+i); + } + return res; +error: + for(i = (size_t)down_to_dim; i < 4; i++) { + MEM_RM(allocator, dimTags[i]); + } + goto exit; +} + +static double size_callback + (int dim, int tag, double x, double y, double z, double lc, void* data_) +{ + struct scad_device* dev = get_device(); + double *modifier = + htable_size_modifiers_find(dev->size_modifiers_by_dim + dim, &tag); + (void)x;(void)y;(void)z;(void)data_; + if(!modifier) + return lc; /* No modifier defined */ + if(*modifier < 0) + return -(*modifier); /* Absolute size modifier */ + return (*modifier) * lc; /* Size factor modifier */ +} + /* gmsh documentation states that memory allocated by gmsh should be freed using * gmshFree. * According to valgrind map results as allocated by gmsh are not fully freed if @@ -490,6 +607,71 @@ error: } res_T +scad_geometry_set_visibility + (const struct scad_geometry* geom, + int visible, + int recursive) +{ + res_T res = RES_OK; + int ierr; + int* data = NULL; + size_t sz; + + if(!geom) { + res = RES_BAD_ARG; + goto error; + } + + ERR(check_device(FUNC_NAME)); + + data = geom->gmsh_dimTags; + sz = geom->gmsh_dimTags_n; + + gmshModelSetVisibility(data, sz, visible, recursive, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + + get_device()->need_synchro = 1; + +exit: + return res; +error: + goto exit; +} + +res_T +scad_geometries_clear_mesh + (struct scad_geometry** geometries, + const size_t geometries_count) +{ + res_T res = RES_OK; + int ierr; + size_t sz; + int* data = NULL; + struct scad_device* dev = get_device(); + struct mem_allocator* allocator = NULL; + + if(!geometries) { + res = RES_BAD_ARG; + goto error; + } + + ERR(check_device(FUNC_NAME)); + + allocator = dev->allocator; + ERR(gather_tags(geometries, geometries_count, &data, &sz)); + gmshModelMeshClear(data, sz, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + + dev->need_synchro = 1; + +exit: + if(allocator) MEM_RM(allocator, data); + return res; +error: + goto exit; +} + +res_T scad_geometry_get_name (const struct scad_geometry* geom, const char** name) @@ -548,6 +730,59 @@ error: } res_T +scad_geometry_get_closest_point + (struct scad_geometry* geom, + const double* from, + double* closest, + double* closest_distance) +{ + res_T res = RES_OK; + size_t i = 0; + double* coord = NULL; + double* pcoord = NULL; + double tmp[3], min[3], min_d = DBL_MAX; + + if(!geom || !from || !closest) { + res = RES_BAD_ARG; + goto error; + } + + ERR(check_device(FUNC_NAME)); + + for(i = 0; i < geom->gmsh_dimTags_n; i += 2) { + double d; + int ierr = 0; + int dim = geom->gmsh_dimTags[i]; + int tag = geom->gmsh_dimTags[i + 1]; + size_t pcoord_n; + size_t coord_n; + + gmshModelGetClosestPoint(dim, tag, from, 3, &coord, &coord_n, &pcoord, + &pcoord_n, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + ASSERT(pcoord_n == (size_t)dim); + ASSERT(coord_n == 3); + d = d3_len(d3_sub(tmp, from, coord)); + if(d < min_d) { + min_d = d; + d3_set(min, tmp); + } + gmshFree(coord); + gmshFree(pcoord); + coord = pcoord = NULL; + } + d3_set(closest, min); + *closest_distance = min_d; + +exit: + gmshFree(coord); + gmshFree(pcoord); + return res; +error: + goto exit; +} + +res_T scad_geometry_get_centerofmass (struct scad_geometry* geom, double* center) @@ -581,6 +816,44 @@ error: } res_T +scad_geometry_get_bounding_box + (struct scad_geometry* geom, + double min[3], + double max[3]) +{ + res_T res = RES_OK; + size_t n = 0; + double mi[3], ma[3]; + + if(!geom || !min || !max) { + res = RES_BAD_ARG; + goto error; + } + + ERR(check_device(FUNC_NAME)); + + d3_splat(mi, DBL_MAX); + d3_splat(ma, -DBL_MAX); + for(n = 0; n < geom->gmsh_dimTags_n; n += 2) { + double i[3], a[3]; + int ierr = 0; + int dim = geom->gmsh_dimTags[n]; + int tag = geom->gmsh_dimTags[n + 1]; + gmshModelOccGetBoundingBox(dim, tag, i, i+1, i+2, a, a+1, a+2, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + d3_min(mi, mi, i); + d3_max(ma, ma, a); + } + d3_set(min, mi); + d3_set(max, ma); + +exit: + return res; +error: + goto exit; +} + +res_T scad_add_rectangle (const char* name, const double xyz[3], @@ -1509,7 +1782,7 @@ scad_geometry_extrude ERR(gmsh_err_to_res_T(ierr)); get_device()->need_synchro = 1; - /* Output includes both the 3D result and its 2D constituants. + /* Output includes both the 3D result and its 2D constituents. * Keep only 3D entities. */ for(i = 0; i < tagoutn; i += 2) { int dim = tagout[i]; @@ -2455,6 +2728,17 @@ scad_geometry_normal coord = pcoord = NULL; } if(!out) { /* Could not find a matching surface */ + if(str_is_empty(&geom->name)) { + log_warning(get_device(), + "Could not get normal at vertex %g %g %g " + "as unamed geometry %p is not close enough.\n", + SPLIT3(p), (void*)geom); + } else { + log_warning(get_device(), + "Could not get normal at vertex %g %g %g " + "as geometry %s is not close enough.\n", + SPLIT3(p), str_cget(&geom->name)); + } res = RES_BAD_ARG; goto error; } @@ -2503,3 +2787,159 @@ error: out = NULL; goto exit; } + +res_T +scad_geometries_set_mesh_size_modifier + (struct scad_geometry** geometries, + const size_t geometries_count, + enum scad_size_modifier_type type, + double modifier) +{ + res_T res = RES_OK; + struct scad_device* dev = get_device(); + int ierr, dim, some = 0; + int* tagout[4] = { NULL, NULL, NULL, NULL }; + size_t tagoutn[4] = { 0, 0, 0, 0}, i; + + if(!geometries || geometries_count == 0 || modifier <= 0) { + res = RES_BAD_ARG; + goto error; + } + + ERR(check_device(FUNC_NAME)); + + if(type == Scad_absolute_size) modifier = -modifier; + ERR(gather_tags_recursive(geometries, geometries_count, 0, tagout, tagoutn)); + for(dim = 0; dim < 4; dim++) { + for(i = 0; i < tagoutn[dim]; i += 2) { + int d = tagout[dim][i]; + int tag = tagout[dim][i+1]; + struct htable_size_modifiers* modifiers = dev->size_modifiers_by_dim + dim; + double* f_ptr = htable_size_modifiers_find(modifiers, &tag); + ASSERT(d == dim); + if(f_ptr && modifier == 1) { /* Size factor of 1 => no modifier */ + size_t c = htable_size_modifiers_erase(modifiers, &tag); + ASSERT(c == 1); + } else if(f_ptr) { + *f_ptr = modifier; + } else { + ERR(htable_size_modifiers_set(modifiers, &tag, &modifier)); + } + } + } + for(dim = 0; dim < 4; dim++) { + if(htable_size_modifiers_size_get(dev->size_modifiers_by_dim + dim) > 0) { + some = 1; + break; + } + } + + gmshModelMeshSetSizeCallback( (some ? size_callback : NULL), NULL, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + +exit: + for(dim = 0; dim < 4; dim++) { + MEM_RM(dev->allocator, tagout[dim]); + } + return res; +error: + goto exit; +} + +res_T +scad_geometries_set_mesh_algorithm +(struct scad_geometry** geometries, + const size_t geometries_count, + enum scad_mesh_algorithm algorithm) +{ + res_T res = RES_OK; + struct scad_device* dev = get_device(); + int ierr; + int* tagout[4] = { NULL, NULL, NULL, NULL }; + size_t tagoutn[4], i; + + if(!geometries || geometries_count == 0) { + res = RES_BAD_ARG; + goto error; + } + + ERR(check_device(FUNC_NAME)); + + ERR(gather_tags_recursive(geometries, geometries_count, 2, tagout, tagoutn)); + for(i = 0; i < tagoutn[2]; i += 2) { + int dim = tagout[2][i]; + int tag = tagout[2][i+1]; + gmshModelMeshSetAlgorithm(dim, tag, (int)algorithm, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + } + +exit: + for(i = 2; i < 4; i++) { + MEM_RM(dev->allocator, tagout[i]); + } + return res; +error: + goto exit; +} + +res_T +scad_geometries_set_periodic + (struct scad_geometry** source, + const size_t source_count, + struct scad_geometry** target, + const size_t target_count, + double affine[16]) +{ + res_T res = RES_OK; + struct scad_device* dev = get_device(); + struct mem_allocator* allocator = dev->allocator; + int ierr; + int* src_dimTagout[4] = { NULL, NULL, NULL, NULL }; + int* tgt_dimTagout[4] = { NULL, NULL, NULL, NULL }; + int* src_tags = NULL, *tgt_tags = NULL; + size_t src_dimTagoutn[4], tgt_dimTagoutn[4], src_tagsn = 0, tgt_tagsn = 0, i; + + if(!source || source_count == 0 || !target || target_count == 0 || !affine) { + res = RES_BAD_ARG; + goto error; + } + + ERR(check_device(FUNC_NAME)); + + ERR(gather_tags_recursive(source, source_count, 2, src_dimTagout, src_dimTagoutn)); + ERR(gather_tags_recursive(target, target_count, 2, tgt_dimTagout, tgt_dimTagoutn)); + ASSERT(src_dimTagoutn[2] % 2 == 0 && tgt_dimTagoutn[2] % 2 == 0); + src_tagsn = src_dimTagoutn[2] / 2; + tgt_tagsn = tgt_dimTagoutn[2] / 2; + if(src_tagsn == 0 || tgt_tagsn == 0) { + res = RES_BAD_ARG; + goto error; + } + + src_tags = MEM_ALLOC(allocator, src_tagsn); + tgt_tags = MEM_ALLOC(allocator, tgt_tagsn); + if(!src_tags || !tgt_tags) { + res = RES_MEM_ERR; + goto error; + } + for(i = 0; i < src_tagsn; i += 2) { + src_tags[i] = src_dimTagout[2][i+1]; + } + for(i = 0; i < tgt_tagsn; i += 2) { + tgt_tags[i] = tgt_dimTagout[2][i+1]; + } + gmshModelMeshSetPeriodic(2, tgt_tags, tgt_tagsn, src_tags, src_tagsn, + affine, 16, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + +exit: + for(i = 2; i < 4; i++) { + MEM_RM(dev->allocator, src_dimTagout[i]); + MEM_RM(dev->allocator, tgt_dimTagout[i]); + } + MEM_RM(dev->allocator, src_tags); + MEM_RM(dev->allocator, tgt_tags); + return res; +error: + goto exit; +} diff --git a/src/test_export2.c b/src/test_export2.c @@ -0,0 +1,102 @@ +/* Copyright (C) 2022-2024 |Méso|Star> (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 "scad.h" +#include "scad_geometry.h" +#include "test_common.h" + +#include <rsys/rsys.h> +#include <rsys/math.h> +#include <rsys/dynamic_array_double.h> +#include <rsys/mem_allocator.h> + +#include <stdlib.h> + +/* + * +-------------------+ + * | | + * | +-------------+ | + * | | | | + * | | +-------+ | | + * | | | | | | + * | | | | | | + * | | | | | | + * | | +-------+ | | + * | | | | + * | +-------------+ | + * | | + * +-------------------+ + */ + +int +main(int argc, char* argv[]) +{ + res_T res = RES_OK; + double p1[3] = {0, 0, 0}; + double p2[3] = {2, 2, 2}; + double p3[3] = {4, 4, 4}; + double d1[3] = {10, 10, 10}; + double d2[3] = {6, 6, 6}; + double d3[3] = {2, 2, 2}; + struct darray_double array; + struct scad_geometry* cube1 = NULL; + struct scad_geometry* cube2 = NULL; + struct scad_geometry* cube3 = NULL; + struct mem_allocator allocator; + + (void)argc; (void)argv; + + OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); + darray_double_init(&allocator, &array); + OK(scad_initialize(NULL, &allocator, 3)); + + OK(scad_add_box("cube1", p1, d1, &cube1)); + OK(scad_add_box("cube2", p2, d2, &cube2)); + OK(scad_add_box("cube3", p3, d3, &cube3)); + + OK(scad_scene_mesh()); + + /* Check that all three cubes can be exported whith forced normals */ + OK(scad_stl_export(cube1, NULL, Scad_force_normals_outward, 0)); + OK(scad_stl_export(cube2, NULL, Scad_force_normals_outward, 0)); + OK(scad_stl_export(cube3, "bin_cube3", Scad_force_normals_outward, 1)); + + /* Check that 2 cubes as a single model can be exported whith forced normals */ + OK(scad_stl_get_data(cube1, &array)); + OK(scad_stl_get_data(cube2, &array)); + OK(scad_stl_data_write(&array, "2cubes.stl", Scad_force_normals_outward, 0)); + + /* Check that with 3 cubes as a single model, the model cannot be exported + * whith forced normals... */ + OK(scad_stl_get_data(cube3, &array)); + BAD(scad_stl_data_write(&array, "3cubes.stl", Scad_force_normals_outward, 0)); + /* ...but can be exported anyway without forcing normals... */ + OK(scad_stl_data_write(&array, "3cubes.stl", Scad_keep_normals_unchanged, 0)); + /* ...and can still be exported if some triangles are duplicated */ + OK(scad_stl_get_data(cube1, &array)); + OK(scad_stl_data_write(&array, "3cubesd.stl", Scad_keep_normals_unchanged, 0)); + + OK(scad_geometry_ref_put(cube1)); + OK(scad_geometry_ref_put(cube2)); + OK(scad_geometry_ref_put(cube3)); + OK(scad_finalize()); + + darray_double_release(&array); + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + + return (res == RES_OK) ? EXIT_SUCCESS : EXIT_FAILURE; +}