star-cad

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

commit 9182baeae54af2ef2b68ef305d30dfc755d103d8
parent 11f03a751b240b37bd6a5a57afe237a92473a536
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Mon,  8 Sep 2025 17:42:05 +0200

Rework on the auto-sync feature

Try to limit calls to synchronize as it is flagged as expensive while
ensuring correctness.

Diffstat:
MMakefile | 4+++-
Msrc/scad.c | 13+++++++++----
Msrc/scad_device.c | 39++++++++++++++++++++++++++-------------
Msrc/scad_device.h | 3+++
Msrc/scad_geometry.c | 19+++++++------------
Asrc/test_sync.c | 179+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 227 insertions(+), 30 deletions(-)

diff --git a/Makefile b/Makefile @@ -131,7 +131,8 @@ TEST_SRC =\ src/test_export2.c\ src/test_lifetime.c\ src/test_partition.c\ - src/test_periodic.c + src/test_periodic.c\ + src/test_sync.c TEST_OBJ = $(TEST_SRC:.c=.o) TEST_DEP = $(TEST_SRC:.c=.d) @@ -185,5 +186,6 @@ test_export2 \ test_lifetime \ test_partition \ test_periodic \ +test_sync \ : config.mk scad-local.pc $(LIBNAME) $(CC) $(CFLAGS_EXE) -o $@ src/$@.o $(LDFLAGS_EXE) $(SCAD_LIBS) $(RSYS_LIBS) -lm diff --git a/src/scad.c b/src/scad.c @@ -189,7 +189,9 @@ error: goto exit; } -/* Accumulate tags of geometry in tags */ +/* Accumulate the 2D tags of geometry (if it is 2D) or the tags of its boundary + * (if it is 3D). + * Trigger a call to sync_device() */ res_T get_2d_tags (struct scad_geometry* geometry, @@ -208,6 +210,7 @@ get_2d_tags data = geometry->gmsh_dimTags; ASSERT(sz % 2 == 0); + ERR(sync_device()); for(i = 0; i < sz; i += 2) { int dim = data[i]; int tag = data[i+1]; @@ -265,7 +268,7 @@ scad_synchronize } gmshModelOccSynchronize(&ierr); - get_device()->need_synchro = dev->options.Misc.DebugAutoSync; + dev->need_synchro = dev->options.Misc.DebugAutoSync; ERR(gmsh_err_to_res_T(ierr)); exit: @@ -291,8 +294,8 @@ scad_run_ui(void) goto error; } - if(dev->options.Misc.SynchronizeOnRunUI && get_device()->need_synchro) { - ERR(scad_synchronize()); + if(dev->options.Misc.SynchronizeOnRunUI) { + ERR(sync_device()); } gmshFltkRun(&ierr); @@ -334,6 +337,7 @@ scad_scene_write } ERR(check_device(FUNC_NAME)); + ERR(sync_device()); gmshWrite(name, &ierr); ERR(gmsh_err_to_res_T(ierr)); @@ -938,6 +942,7 @@ scad_scene_mesh ERR(check_device(FUNC_NAME)); + ERR(sync_device()); gmshModelMeshGenerate(2, &ierr); ERR(gmsh_err_to_res_T(ierr)); diff --git a/src/scad_device.c b/src/scad_device.c @@ -80,13 +80,10 @@ device_release(struct scad_device* dev) MEM_RM(dev->allocator, dev); htable_geometries_release(&tmp); -#ifndef NDEBUG - { - int ierr; - gmshModelOccSynchronize(&ierr); + if(dev->options.Misc.DebugEmptyContext) { + /* After releasing all star-cad stuff, gmsh and OCC contexts must be empty */ return check_empty_gmsh_occ(dev); } -#endif return res; } @@ -149,10 +146,6 @@ check_device goto error; } - if(g_device->need_synchro) { - ERR(scad_synchronize()); - } - if(g_device->options.Misc.RunUIAtEachStep) { ERR(scad_run_ui()); } @@ -169,6 +162,21 @@ error: goto exit; } +res_T +sync_device(void) +{ + res_T res = RES_OK; + + if(g_device->need_synchro) { + ERR(scad_synchronize()); /* Reset need_synchro according to options */ + } + +exit: + return res; +error: + goto exit; +} + struct scad_device* get_device (void) @@ -339,6 +347,7 @@ do_device_tags_ref_get struct tag_desc d; tag_desc_init(dev->allocator, &d); ERR(htable_tags2desc_set(t2d, &tag, &d)); + dev->need_synchro = 1; if(log) { logger_print(dev->logger, log_type, "New tag %d.%d (count set to 1).\n", dim, tag); @@ -416,6 +425,7 @@ do_device_tags_ref_put dim, tag); } gmshModelOccRemove(dimTags+i, 2, 0, &ierr); + dev->need_synchro = 1; ERR(gmsh_err_to_res_T(ierr)); break; case Scad_delete_recursive: @@ -424,6 +434,7 @@ do_device_tags_ref_put dim, tag); } gmshModelOccRemove(dimTags+i, 2, 1, &ierr); + dev->need_synchro = 1; ERR(gmsh_err_to_res_T(ierr)); break; default: FATAL("Invalid enum value"); @@ -537,13 +548,13 @@ check_empty_gmsh_occ(struct scad_device* dev) for(d = 3; d >= 0; d--) { gmshFree(dimTags); dimTags = NULL; - gmshModelGetEntities(&dimTags, &dimTags_n, d, &ierr); + gmshModelOccGetEntities(&dimTags, &dimTags_n, d, &ierr); ASSERT(dimTags_n % 2 == 0); if(dimTags_n == 0) continue; found = 1; log_error(dev, - "There are %ld unreferenced gmsh entities of dim %d from an empty star-cad context%c\n", + "There are %ld unreferenced Open-Cascade entities of dim %d from an empty star-cad context%c\n", dimTags_n / 2, d, (d > 1 ? ':' : '.')); if(d < 2) continue; if(!msg_initialized) str_init(dev->allocator, &msg); @@ -559,17 +570,19 @@ check_empty_gmsh_occ(struct scad_device* dev) goto error; } + ERR(sync_device()); + found = 0; for(d = 3; d >= 0; d--) { gmshFree(dimTags); dimTags = NULL; - gmshModelOccGetEntities(&dimTags, &dimTags_n, d, &ierr); + gmshModelGetEntities(&dimTags, &dimTags_n, d, &ierr); ASSERT(dimTags_n % 2 == 0); if(dimTags_n == 0) continue; found = 1; log_error(dev, - "There are %ld unreferenced Open-Cascade entities of dim %d from an empty star-cad context%c\n", + "There are %ld unreferenced gmsh entities of dim %d from an empty star-cad context%c\n", dimTags_n / 2, d, (d > 1 ? ':' : '.')); if(d < 2) continue; if(!msg_initialized) str_init(dev->allocator, &msg); diff --git a/src/scad_device.h b/src/scad_device.h @@ -198,6 +198,9 @@ LOCAL_SYM res_T check_device (const char* function_name); +LOCAL_SYM res_T +sync_device(void); + LOCAL_SYM struct scad_device* get_device (void); diff --git a/src/scad_geometry.c b/src/scad_geometry.c @@ -150,7 +150,6 @@ geometry_create ref_init(&geom->ref); str_init(allocator, &geom->name); ERR(htable_geometries_set(&dev->allgeom, &geom, &one)); - dev->need_synchro = 1; if(dev->options.Misc.LogRefCounting & SCAD_LOG_GEOMETRY) { if(str_is_empty(&geom->name)) { @@ -389,6 +388,8 @@ error: goto exit; } +/* Recursivelly get the tags of geometries, possibly down to dim 0. + * Trigger a call to sync_device() */ static res_T gather_tags_recursive (struct scad_geometry** geometries, @@ -425,6 +426,7 @@ gather_tags_recursive /* Recursively build result by dimension and list constituents, * begining with dim==3 */ + ERR(sync_device()); for(dim = 3; dim >= down_to_dim; dim--) { size_t c = 0; sz[dim] = 2 * htable_tags_size_get(t+dim); @@ -517,7 +519,6 @@ geometry_release(ref_T* ref) size_t n; ASSERT(ref); - dev->need_synchro = 1; geom = CONTAINER_OF(ref, struct scad_geometry, ref); CHK(RES_OK == device_unregister_tags(dev->log, dev->log_type, geom)); MEM_RM(allocator, geom->gmsh_dimTags); @@ -790,11 +791,10 @@ scad_geometry_set_visibility data = geom->gmsh_dimTags; sz = geom->gmsh_dimTags_n; + ERR(sync_device()); gmshModelSetVisibility(data, sz, visible, recursive, &ierr); ERR(gmsh_err_to_res_T(ierr)); - get_device()->need_synchro = 1; - exit: return res; error: @@ -822,7 +822,6 @@ scad_geometries_clear_mesh allocator = dev->allocator; ERR(gather_tags_recursive(geometries, geometries_count, 0, data, sz)); - ERR(sync_device()); for(dim = 3; dim >= 0; dim--) { /* Cannot clear the mesh of lower dim entities if linked to higher dim * entities with uncleared mesh: start from the higher dim down to 0 */ @@ -2003,6 +2002,7 @@ scad_geometries_common_boundaries * (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); @@ -2141,7 +2141,6 @@ scad_geometry_rotate gmshModelOccRotate(out->gmsh_dimTags, out->gmsh_dimTags_n, SPLIT3(pt), SPLIT3(axis), angle, &ierr); - get_device()->need_synchro = 1; ERR(gmsh_err_to_res_T(ierr)); exit: @@ -2203,7 +2202,6 @@ scad_geometry_extrude gmshModelOccExtrude(geom->gmsh_dimTags, geom->gmsh_dimTags_n, SPLIT3(dxdydz), &tagout, &tagoutn, NULL, 0, NULL, 0, 0, &ierr); ERR(gmsh_err_to_res_T(ierr)); - get_device()->need_synchro = 1; /* Output includes both the 3D result and its 2D constituents. * Keep only 3D entities. */ @@ -2386,7 +2384,6 @@ scad_geometry_copy sz1 = geom->gmsh_dimTags_n; data1 = geom->gmsh_dimTags; gmshModelOccCopy(data1, sz1, &tagout, &tagoutn, &ierr); - get_device()->need_synchro = 1; ASSERT(tagoutn % 2 == 0); ERR(gmsh_err_to_res_T(ierr)); @@ -2474,7 +2471,6 @@ scad_geometry_translate gmshModelOccTranslate(out->gmsh_dimTags, out->gmsh_dimTags_n, SPLIT3(dxdydz), &ierr); - get_device()->need_synchro = 1; ERR(gmsh_err_to_res_T(ierr)); exit: @@ -2577,8 +2573,6 @@ scad_geometries_partition ERR(gmsh_err_to_res_T(ierr)); ASSERT(sz == 2*mapnn); /* Because input tags where deduplicated */ - get_device()->need_synchro = 1; - /* Check first if there was an overlapping problem */ if(!(flags & SCAD_ALLOW_OVERLAPPING)) { /* No overlapping means that each tag in geometries is translated into a @@ -2931,6 +2925,7 @@ scad_geometries_boundary 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)); @@ -3301,7 +3296,7 @@ scad_geometries_set_mesh_size_modifier } } - gmshModelMeshSetSizeCallback( (some ? size_callback : NULL), NULL, &ierr); + gmshModelMeshSetSizeCallback((some ? size_callback : NULL), NULL, &ierr); ERR(gmsh_err_to_res_T(ierr)); exit: diff --git a/src/test_sync.c b/src/test_sync.c @@ -0,0 +1,179 @@ +/* Copyright (C) 2022-2025 |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/str.h> +#include <rsys/math.h> +#include <rsys/double3.h> +#include <rsys/mem_allocator.h> +#include <rsys/dynamic_array_double.h> + +#include <stdlib.h> +#include <stdio.h> + +static void +get_position + (const size_t ivert, double pos[2], void* data) +{ + double* coord = data; + ASSERT(pos && coord); + pos[0] = coord[2*ivert]; + pos[1] = coord[1+2*ivert]; +} + +int +main(int argc, char* argv[]) +{ + res_T res = RES_OK; + const double p1[3] = {0, 0, 0}; + const double p2[3] = {0.25, 0.25, 0.8}; + const double p3[3] = {0.85, 0, 0}; + const double p4[3] = {1, 0, 0}; + const double d0[3] = {0.1, 0.1, 0.1}; + const double d1[3] = {1, 1, 1}; + const double d2[3] = {1, 1, 0}; + const double s[3] = {1, 1, 1}; + double coord[] = {0, 1.6, 0.5, 0.9, 0.8, 0.6}; + struct scad_geometry* geom1 = NULL; + struct scad_geometry* geom2 = NULL; + struct scad_geometry* geom = NULL; + struct scad_geometry* poly = NULL; + struct scad_geometry** geom_array = NULL; + struct scad_geometry* geoms[2]; + struct scad_geometry* out_geoms[2]; + struct mem_allocator allocator; + struct darray_double trg; + struct scad_options options = SCAD_DEFAULT_OPTIONS; + double m, tmp[3], tmp2[3]; + size_t i, c; + int e; + + (void)argc; (void)argv; + + OK(mem_init_proxy_allocator(&allocator, &mem_default_allocator)); + + OK(scad_initialize(NULL, &allocator, 3)); + options.Mesh.MeshSizeExtendFromBoundary = 0; + options.Mesh.MeshSizeFromPoints = 0; + options.Mesh.Algorithm = SCAD_INITIAL_MESH_ONLY; + OK(scad_set_options(&options)); + + OK(scad_add_box(p1, d1, &geom)); + OK(scad_scene_mesh()); + darray_double_init(&allocator, &trg); + OK(scad_stl_get_data(geom, &trg)); + CHK(12 * 9 == darray_double_size_get(&trg)); + darray_double_release(&trg); + OK(scad_geometry_ref_put(geom)); + + OK(scad_add_box(p1, d1, &geom1)); + OK(scad_add_box(p2, d1, &geom2)); + OK(scad_geometries_cut(&geom1, 1, &geom2, 1, &geom)); + OK(scad_geometry_ref_put(geom1)); + OK(scad_geometry_ref_put(geom2)); + OK(scad_scene_mesh()); + darray_double_init(&allocator, &trg); + OK(scad_stl_get_data(geom, &trg)); + CHK(24 * 9 == darray_double_size_get(&trg)); + darray_double_release(&trg); + OK(scad_geometry_ref_put(geom)); + + OK(scad_add_polygon(get_position, coord, 0, 3, &poly)); + OK(scad_geometry_extrude(poly, d1, &geom)); + OK(scad_geometry_ref_put(poly)); + OK(scad_scene_mesh()); + darray_double_init(&allocator, &trg); + OK(scad_stl_get_data(geom, &trg)); + CHK(8 * 9 == darray_double_size_get(&trg)); + darray_double_release(&trg); + OK(scad_geometry_ref_put(geom)); + + OK(scad_add_box(p1, d1, &geom1)); + OK(scad_geometry_translate(geom1, d1, &geom)); + OK(scad_geometry_ref_put(geom1)); + OK(scad_scene_mesh()); + darray_double_init(&allocator, &trg); + OK(scad_stl_get_data(geom, &trg)); + CHK(12 * 9 == darray_double_size_get(&trg)); + darray_double_release(&trg); + OK(scad_geometry_ref_put(geom)); + + OK(scad_add_box(p1, d1, &geom)); + OK(scad_geometry_set_visibility(geom, 0, 1)); + OK(scad_scene_mesh()); + darray_double_init(&allocator, &trg); + OK(scad_stl_get_data(geom, &trg)); + CHK(0 == darray_double_size_get(&trg)); + darray_double_release(&trg); + OK(scad_geometry_ref_put(geom)); + + OK(scad_add_box(p1, d1, &geom)); + OK(scad_scene_mesh()); + OK(scad_geometries_clear_mesh(&geom, 1)); + darray_double_init(&allocator, &trg); + OK(scad_stl_get_data(geom, &trg)); + CHK(0 == darray_double_size_get(&trg)); + darray_double_release(&trg); + OK(scad_geometry_ref_put(geom)); + + options.Mesh.Algorithm = SCAD_MESHADAPT; + options.Mesh.MeshSizeFromPoints = 1; + options.Mesh.MeshSizeExtendFromBoundary = 1; + OK(scad_set_options(&options)); + + OK(scad_add_box(p1, d1, &geom)); + OK(scad_scene_mesh()); + darray_double_init(&allocator, &trg); + OK(scad_stl_get_data(geom, &trg)); + c = darray_double_size_get(&trg); + darray_double_clear(&trg); + printf("%ld trg\n", c/9); + + OK(scad_geometries_clear_mesh(&geom, 1)); + OK(scad_stl_get_data(geom, &trg)); + CHK(0 == darray_double_size_get(&trg)); + + OK(scad_geometries_set_mesh_size_modifier(&geom, 1, SCAD_SIZE_FACTOR, 0.25)); + OK(scad_scene_mesh()); + OK(scad_stl_get_data(geom, &trg)); + printf("%ld trg\n", darray_double_size_get(&trg)/9); + CHK(c < darray_double_size_get(&trg)); + darray_double_release(&trg); + OK(scad_geometry_ref_put(geom)); + + options.Mesh.MeshSizeExtendFromBoundary = 0; + options.Mesh.MeshSizeFromPoints = 0; + options.Mesh.Algorithm = SCAD_INITIAL_MESH_ONLY; + OK(scad_set_options(&options)); + +#if 0 +set mesh size cb : besoin de sync ? +#endif + + options.Misc.LogRefCounting = SCAD_LOG_DIMTAGS_ONLY_UNDELETED; + OK(scad_set_options(&options)); + + OK(scad_finalize()); + + check_memory_allocator(&allocator); + mem_shutdown_proxy_allocator(&allocator); + CHK(mem_allocated_size() == 0); + + return (res == RES_OK) ? EXIT_SUCCESS : EXIT_FAILURE; +}