commit 12dcc05afc4230809589aa199c1480bc792f418f
parent ff863c4029a9dc0b90e2af6de6826a5da29b0866
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Mon, 3 Mar 2025 10:50:08 +0100
Merge branch 'release_0.5.1' into develop
Diffstat:
5 files changed, 565 insertions(+), 12 deletions(-)
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
@@ -60,8 +60,10 @@ enum scad_verbosity_levels {
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 {
@@ -85,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 {
@@ -114,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 } \
@@ -166,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.
@@ -238,6 +254,20 @@ 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
@@ -383,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 */
@@ -398,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
@@ -534,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];
@@ -2514,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;
+}