commit bb36c865c041d5608a5882169589c951ed82e883
parent c127175d8610c030bd6aa997832c1152abb46cdb
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date: Wed, 15 Oct 2025 11:50:09 +0200
Reintroduce the collect version of scad_geometries_[common_]boundaries
Namely scad_geometries_boundary and scad_geometries_common_boundary.
While upgrading codes to star-cad 0.6, it appeared that the list
versions could not fit all needs.
Diffstat:
4 files changed, 311 insertions(+), 6 deletions(-)
diff --git a/README.md b/README.md
@@ -31,11 +31,9 @@ Edit config.mk as needed, then run:
- API changes for geometry creation: geometry name is no longer provided to the
various add_<something> functions, that now create unnamed geometries; instead
names have to be set after geometry creation through `scad_geometry_set_name'.
-- API change for `scad_geometries_boundaries' and
- `scad_geometries_common_boundaries': now return a list of geometries
- (that could be grouped through `scad_collect_geometries'); used to return a
- single geometry containing the (possibly empty) boundary elements (that could
- be ungrouped using `scad_geometry_explode').
+- Add `scad_geometries_boundaries' and `scad_geometries_common_boundaries' that
+ return a list of geometries instead of a (possibly empty) single geometry
+ containing all the boundary elements.
- API uniformization (involving args order, naming, etc.).
- Fix geometry naming. Now get_name returns NULL if name was set to NULL or
let unset (used to return ""). Allow to set same name again (to the same
@@ -45,7 +43,7 @@ Edit config.mk as needed, then run:
point, fixing a memleak in the process.
- Fix `scad_scene_clear' (geometries with multiple references where not
released).
-- Fix `scad_geometries_common_boundaries'. Used to trigger OccIntersect that, as
+- Fix `scad_geometries_common_boundary'. Used to trigger OccIntersect that, as
a side effet, partitionned geometries.
- Fix `scad_step_import' (arguments where inverted).
- Change `scad_geometry_get_centerofmass'. Now returns the unique center of mass
diff --git a/src/scad.h b/src/scad.h
@@ -581,6 +581,17 @@ scad_geometries_common_boundaries
struct scad_geometry*** out_boundaries,
size_t *out_count);
+/* Same as above, with the boundary entities collected in a single geometry.
+ * Note that there is always an output geometry returned is `out_boundary', that
+ * can be empty (scad_geometry_get_count = 0). */
+SCAD_API res_T
+scad_geometries_common_boundary
+ (struct scad_geometry** geometries,
+ const size_t geometries_count,
+ struct scad_geometry** tools,
+ const size_t tools_count,
+ struct scad_geometry** out_boundary);
+
/* Get the boundaries of the geometries in `geometries', considered as a whole.
* The output geometries are created unnamed.
* The result `out_boundaries' being allocated using the allocator provided when
@@ -592,6 +603,15 @@ scad_geometries_boundaries
struct scad_geometry*** out_boundaries,
size_t *out_count);
+/* Same as above, with the boundary entities collected in a single geometry.
+ * Note that there is always an output geometry returned is `out_boundary', that
+ * can be empty (scad_geometry_get_count = 0). */
+SCAD_API res_T
+scad_geometries_boundary
+ (struct scad_geometry** geometries,
+ const size_t geometries_count,
+ struct scad_geometry** out_boundary);
+
/* Copy the geometry `geometry', except for its name.
* The new geometry remains unnamed. */
SCAD_API res_T
diff --git a/src/scad_geometry.c b/src/scad_geometry.c
@@ -2104,6 +2104,134 @@ error:
}
res_T
+scad_geometries_common_boundary
+ (struct scad_geometry** geometries,
+ const size_t geometries_count,
+ struct scad_geometry** tools,
+ const size_t tools_count,
+ struct scad_geometry** out_boundary)
+{
+ res_T res = RES_OK;
+ int* tagout = NULL;
+ size_t tagoutn, sz1, sz2, u_sz;
+ int* data1 = NULL;
+ int* data2 = NULL;
+ int* unique = NULL;
+ int ierr = 0;
+ int* bound1 = NULL;
+ int* bound2 = NULL;
+ size_t n1, n2;
+ struct scad_geometry* out_geom = NULL;
+ struct mem_allocator* allocator = NULL;
+ struct scad_device* dev = get_device();
+ int log;
+ enum log_type log_type;
+ size_t i, n;
+ struct str msg;
+ int msg_initialized = 0;
+
+ if(!geometries || !geometries_count || !tools || !tools_count || !out_boundary) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+
+ ERR(gather_tags(geometries, geometries_count, &data1, &sz1));
+ ERR(gather_tags(tools, tools_count, &data2, &sz2));
+
+ /* data1 and data2 can have tags in common: deduplicate them!
+ * (even if the refcounting stuff can manage duplicates) */
+ ERR(process_tag_list(data1, sz1, data2, sz2, UNIQUE_TAGS, &unique, &u_sz));
+
+ ERR(sync_device());
+ gmshModelGetBoundary(data1, sz1, &bound1, &n1, 1, 0, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ gmshModelGetBoundary(data2, sz2, &bound2, &n2, 1, 0, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+
+ ERR(process_tag_list(bound1, n1, bound2, n2, COMMON_TAGS, &tagout, &tagoutn));
+ ASSERT(tagoutn % 2 == 0);
+
+ if(tagoutn == 0) {
+ log_message(dev, "Common boundary list is empty.\n");
+ } else {
+ log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL);
+ log_type = dev->log_type;
+ if(log) {
+ str_init(allocator, &msg);
+ msg_initialized = 1;
+ logger_print(dev->logger, log_type,
+ "Common boundary specific tag management:\n");
+ ERR(str_printf(&msg, " tags ["));
+ for(i = 0; i < tagoutn; i += 2) {
+ const int dim = tagout[i];
+ const int tag = tagout[i+1];
+ ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
+ }
+ ERR(str_append_printf(&msg, "] getting a ref to tags ["));
+ for(i = 0; i < u_sz; i += 2) {
+ const int dim = unique[i];
+ const int tag = unique[i+1];
+ ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
+ }
+ logger_print(dev->logger, log_type, "%s].\n", str_cget(&msg));
+ }
+ }
+
+ ERR(geometry_create(&out_geom));
+ out_geom->gmsh_dimTags_n = tagoutn;
+ out_geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout));
+ if(!out_geom->gmsh_dimTags) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ memcpy(out_geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout));
+ ERR(device_register_tags(out_geom));
+
+ for(i = 0, n = 0; i < tagoutn; i += 2, n++) {
+ int dim = tagout[i];
+ int tag = tagout[i+1];
+ struct tag_desc* desc = device_get_description(dim, tag);
+ ASSERT(desc);
+ ASSERT(dim == 2);
+ /* Need to protect out_geometry's tags by getting a ref on input tags or
+ * deleting input geometry will possibly delete them */
+ ERR(device_register_ref_to_tags(dim, tag, unique, u_sz));
+ /* As the 2D tags will be deleted when the 3D tag they are part of are
+ * deleted, they shouldn't be deleted when the geometry they belongs to are
+ * released. */
+ desc->delete_policy = Scad_do_not_delete;
+ }
+
+exit:
+ if(msg_initialized) str_release(&msg);
+ if(out_boundary) *out_boundary = out_geom;
+ if(allocator) {
+ MEM_RM(allocator, data1);
+ MEM_RM(allocator, data2);
+ MEM_RM(allocator, unique);
+ MEM_RM(allocator, tagout);
+ }
+ gmshFree(bound1);
+ gmshFree(bound2);
+ return res;
+error:
+ if(ierr) {
+ int dim = INT_MAX;
+ if(!mixed_dim_err_msg("common boundary", geometries, geometries_count, &dim))
+ mixed_dim_err_msg("common boundary", tools, tools_count, &dim);
+ }
+ if(out_geom) {
+ SCAD(geometry_ref_put(out_geom));
+ out_geom = NULL;
+ }
+ if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
+ goto exit;
+}
+
+res_T
scad_geometry_rotate
(const struct scad_geometry* geom,
const double pt[3],
@@ -3023,6 +3151,101 @@ error:
goto exit;
}
+
+res_T
+scad_geometries_boundary
+ (struct scad_geometry** geometries,
+ const size_t geometries_count,
+ struct scad_geometry** out_boundary)
+{
+ res_T res = RES_OK;
+ int* tagout = NULL;
+ size_t tagoutn, sz;
+ int* data = NULL;
+ int ierr = 0;
+ struct scad_geometry* out_geom = NULL;
+ struct mem_allocator* allocator = NULL;
+ struct scad_device* dev = get_device();
+ int log;
+ enum log_type log_type;
+ size_t i;
+ struct str msg;
+ int msg_initialized = 0;
+
+ if(!geometries || geometries_count == 0 || !out_boundary) {
+ res = RES_BAD_ARG;
+ goto error;
+ }
+
+ ERR(check_device(FUNC_NAME));
+ allocator = dev->allocator;
+
+ ERR(gather_tags(geometries, geometries_count, &data, &sz));
+ ERR(sync_device());
+ gmshModelGetBoundary(data, sz, &tagout, &tagoutn, 1, 0, 0, &ierr);
+ ERR(gmsh_err_to_res_T(ierr));
+ ASSERT(tagoutn % 2 == 0);
+
+ log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL);
+ log_type = dev->log_type;
+ if(log) {
+ str_init(allocator, &msg);
+ msg_initialized = 1;
+ logger_print(dev->logger, log_type, "Boundary specific tag management:\n");
+ ERR(str_printf(&msg, " tags ["));
+ for(i = 0; i < tagoutn; i += 2) {
+ const int dim = tagout[i];
+ const int tag = tagout[i+1];
+ ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
+ }
+ ERR(str_append_printf(&msg, "] getting a ref to tags ["));
+ for(i = 0; i < sz; i += 2) {
+ const int dim = data[i];
+ const int tag = data[i+1];
+ ERR(str_append_printf(&msg, (i ? ", %d.%d" : "%d.%d"), dim, tag));
+ }
+ logger_print(dev->logger, log_type, "%s].\n", str_cget(&msg));
+ }
+
+ ERR(geometry_create(&out_geom));
+ out_geom->gmsh_dimTags_n = tagoutn;
+ out_geom->gmsh_dimTags = MEM_ALLOC(allocator, tagoutn * sizeof(*tagout));
+ if(!out_geom->gmsh_dimTags) {
+ res = RES_MEM_ERR;
+ goto error;
+ }
+ memcpy(out_geom->gmsh_dimTags, tagout, tagoutn * sizeof(*tagout));
+ ERR(device_register_tags(out_geom));
+ for(i = 0; i < out_geom->gmsh_dimTags_n; i += 2) {
+ const int dim = out_geom->gmsh_dimTags[i];
+ const int tag = out_geom->gmsh_dimTags[i+1];
+ struct tag_desc* desc = device_get_description(dim, tag);
+ ASSERT(dim == 2);
+ ASSERT(desc != NULL);
+ /* Need to protect input geometry's tags by getting a ref on output tags or
+ * deleting out_geometry will possibly delete them */
+ ERR(device_register_ref_to_tags(dim, tag, data, sz));
+ /* As the 2D tags will be deleted when the 3D tag they are part of are
+ * deleted, they shouldn't be deleted when the geometry they belongs to is
+ * released. */
+ desc->delete_policy = Scad_do_not_delete;
+ }
+
+exit:
+ if(msg_initialized) str_release(&msg);
+ if(allocator) MEM_RM(allocator, data);
+ if(out_boundary) *out_boundary = out_geom;
+ gmshFree(tagout);
+ return res;
+error:
+ if(out_geom) {
+ SCAD(geometry_ref_put(out_geom));
+ out_geom = NULL;
+ }
+ if(tagout) gmshModelOccRemove(tagout, tagoutn, 1, &ierr);
+ goto exit;
+}
+
res_T
scad_step_import
(const char* filename,
diff --git a/src/test_api.c b/src/test_api.c
@@ -134,7 +134,9 @@ main(int argc, char* argv[])
BAD(scad_geometries_intersect(&geom1, 1, &geom2, 1, &geom));
BAD(scad_geometries_partition(&geom1, 1, 0, &geom));
BAD(scad_geometries_common_boundaries(&geom1, 1, &geom2, 1, &geom_array, &c));
+ BAD(scad_geometries_common_boundary(&geom1, 1, &geom2, 1, &geom));
BAD(scad_geometries_boundaries(&geom1, 1, &geom_array, &c));
+ BAD(scad_geometries_boundary(&geom1, 1, &geom));
BAD(scad_geometry_copy(geom1, &geom2));
BAD(scad_geometry_set_visibility(geom1, 0, 0));
BAD(scad_geometries_set_periodic(&geom1, 1, &geom2, 1, affine));
@@ -202,7 +204,9 @@ main(int argc, char* argv[])
BAD(scad_geometries_intersect(&geom1, 1, &geom2, 1, &geom));
BAD(scad_geometries_partition(&geom1, 1, 0, &geom));
BAD(scad_geometries_common_boundaries(&geom1, 1, &geom2, 1, &geom_array, &c));
+ BAD(scad_geometries_common_boundary(&geom1, 1, &geom2, 1, &geom));
BAD(scad_geometries_boundaries(&geom1, 1, &geom_array, &c));
+ BAD(scad_geometries_boundary(&geom1, 1, &geom));
BAD(scad_geometry_copy(geom1, &geom2));
BAD(scad_geometry_set_visibility(geom1, 0, 0));
BAD(scad_geometries_set_periodic(&geom1, 1, &geom2, 1, affine));
@@ -1375,6 +1379,51 @@ main(int argc, char* argv[])
OK(scad_scene_count(&c));
CHK(c == 0);
+ OK(scad_add_sphere(p1, 0.1, &geoms[0]));
+ OK(scad_add_sphere(p3, 0.1, &geoms[1]));
+ OK(scad_geometries_partition(geoms, 2, SCAD_ALLOW_OVERLAPPING, out_geoms));
+ OK(scad_geometries_swap(geoms, out_geoms, 2, SCAD_SWAP_GEOMETRY));
+ OK(scad_geometry_ref_put(out_geoms[0]));
+ OK(scad_geometry_ref_put(out_geoms[1]));
+ BAD(scad_geometries_common_boundary(NULL, 0, NULL, 0, NULL));
+ BAD(scad_geometries_common_boundary(NULL, 0, NULL, 0, &geom));
+ BAD(scad_geometries_common_boundary(NULL, 0, NULL, 1, NULL));
+ BAD(scad_geometries_common_boundary(NULL, 0, NULL, 1, &geom));
+ BAD(scad_geometries_common_boundary(NULL, 0, geoms+1, 0, NULL));
+ BAD(scad_geometries_common_boundary(NULL, 0, geoms+1, 0, &geom));
+ BAD(scad_geometries_common_boundary(NULL, 0, geoms+1, 1, NULL));
+ BAD(scad_geometries_common_boundary(NULL, 0, geoms+1, 1, &geom));
+ BAD(scad_geometries_common_boundary(NULL, 1, NULL, 0, NULL));
+ BAD(scad_geometries_common_boundary(NULL, 1, NULL, 0, &geom));
+ BAD(scad_geometries_common_boundary(NULL, 1, NULL, 1, NULL));
+ BAD(scad_geometries_common_boundary(NULL, 1, NULL, 1, &geom));
+ BAD(scad_geometries_common_boundary(NULL, 1, geoms+1, 0, NULL));
+ BAD(scad_geometries_common_boundary(NULL, 1, geoms+1, 0, &geom));
+ BAD(scad_geometries_common_boundary(NULL, 1, geoms+1, 1, NULL));
+ BAD(scad_geometries_common_boundary(NULL, 1, geoms+1, 1, &geom));
+ BAD(scad_geometries_common_boundary(geoms, 0, NULL, 0, NULL));
+ BAD(scad_geometries_common_boundary(geoms, 0, NULL, 0, &geom));
+ BAD(scad_geometries_common_boundary(geoms, 0, NULL, 1, NULL));
+ BAD(scad_geometries_common_boundary(geoms, 0, NULL, 1, &geom));
+ BAD(scad_geometries_common_boundary(geoms, 0, geoms+1, 0, NULL));
+ BAD(scad_geometries_common_boundary(geoms, 0, geoms+1, 0, &geom));
+ BAD(scad_geometries_common_boundary(geoms, 0, geoms+1, 1, NULL));
+ BAD(scad_geometries_common_boundary(geoms, 0, geoms+1, 1, &geom));
+ BAD(scad_geometries_common_boundary(geoms, 1, NULL, 0, NULL));
+ BAD(scad_geometries_common_boundary(geoms, 1, NULL, 0, &geom));
+ BAD(scad_geometries_common_boundary(geoms, 1, NULL, 1, NULL));
+ BAD(scad_geometries_common_boundary(geoms, 1, NULL, 1, &geom));
+ BAD(scad_geometries_common_boundary(geoms, 1, geoms+1, 0, NULL));
+ BAD(scad_geometries_common_boundary(geoms, 1, geoms+1, 0, &geom));
+ BAD(scad_geometries_common_boundary(geoms, 1, geoms+1, 1, NULL));
+ OK(scad_geometries_common_boundary(geoms, 1, geoms+1, 1, &geom));
+ OK(scad_geometry_ref_put(geoms[0]));
+ OK(scad_geometry_ref_put(geoms[1]));
+ OK(scad_geometry_ref_put(geom));
+
+ OK(scad_scene_count(&c));
+ CHK(c == 0);
+
OK(scad_add_sphere(p1, 0.1, &geom1));
BAD(scad_geometries_boundaries(NULL, 0, NULL, NULL));
BAD(scad_geometries_boundaries(NULL, 0, NULL, &c));
@@ -1403,6 +1452,21 @@ main(int argc, char* argv[])
CHK(c == 0);
OK(scad_add_sphere(p1, 0.1, &geom1));
+ BAD(scad_geometries_boundary(NULL, 0, NULL));
+ BAD(scad_geometries_boundary(NULL, 0, &geom));
+ BAD(scad_geometries_boundary(NULL, 1, NULL));
+ BAD(scad_geometries_boundary(&geom1, 0, NULL));
+ BAD(scad_geometries_boundary(NULL, 1, &geom));
+ BAD(scad_geometries_boundary(&geom1, 0, &geom));
+ BAD(scad_geometries_boundary(&geom1, 1, NULL));
+ OK(scad_geometries_boundary(&geom1, 1, &geom));
+ OK(scad_geometry_ref_put(geom1));
+ OK(scad_geometry_ref_put(geom));
+
+ OK(scad_scene_count(&c));
+ CHK(c == 0);
+
+ OK(scad_add_sphere(p1, 0.1, &geom1));
BAD(scad_geometry_copy(NULL, &geom));
BAD(scad_geometry_copy(geom1, NULL));
OK(scad_geometry_copy(geom1, &geom));