star-cad

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

commit 06ea5f14c0a9a42d3eeb49e767eefd355ff5c7a0
parent 9a5a03aed72702075a55429b61454e464f53f8eb
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Thu, 21 Aug 2025 17:56:58 +0200

Code quality.

Fix warnings, improve comments, arg. testing and log messages.

Diffstat:
Msrc/scad.c | 24+++++++++++++++++++-----
Msrc/scad.h | 40+++++++++++++++++++---------------------
Msrc/scad_device.c | 3+++
Msrc/scad_device.h | 2++
Msrc/scad_geometry.c | 71+++++++++++++++++++++++++++++++++++++++++++++++------------------------
5 files changed, 90 insertions(+), 50 deletions(-)

diff --git a/src/scad.c b/src/scad.c @@ -368,7 +368,7 @@ scad_stl_get_data_partial int tags_initialized = 0; res_T res = RES_OK; - if(!geometry || !triangles) { + if(!geometry || !triangles || (!dont && dont_count > 0)) { res = RES_BAD_ARG; goto error; } @@ -428,8 +428,13 @@ scad_stl_get_data_partial } ASSERT(tcount == darray_double_size_get(triangles)); if(count0 == tcount) { - log_message(dev, "No triangle collected for geometry '%s'.\n", - str_cget(&geometry->name)); + if(str_is_empty(&geometry->name)) { + log_message(dev, "No triangle collected for unnamed geometry '%p'.\n", + (void*)geometry); + } else { + log_message(dev, "No triangle collected for geometry '%s'.\n", + str_cget(&geometry->name)); + } } exit: @@ -794,7 +799,11 @@ scad_stl_export struct str filename; int initialized = 0; - if(!geometry || (!file_name && str_is_empty(&geometry->name))) { + if(!geometry || (!file_name && str_is_empty(&geometry->name)) || + (orientation != SCAD_KEEP_NORMALS_UNCHANGED && + orientation != SCAD_FORCE_NORMALS_OUTWARD && + orientation != SCAD_FORCE_NORMALS_INWARD)) + { res = RES_BAD_ARG; goto error; } @@ -842,7 +851,12 @@ scad_stl_export_partial struct str filename; int initialized = 0; - if(!geometry) { + if(!geometry || (!file_name && str_is_empty(&geometry->name)) || + (!dont && dont_count > 0) || + (orientation != SCAD_KEEP_NORMALS_UNCHANGED && + orientation != SCAD_FORCE_NORMALS_OUTWARD && + orientation != SCAD_FORCE_NORMALS_INWARD)) + { res = RES_BAD_ARG; goto error; } diff --git a/src/scad.h b/src/scad.h @@ -59,7 +59,7 @@ enum scad_verbosity_levels { /* Mesh algorithms */ enum scad_mesh_algorithm { SCAD_MESHADAPT = 1, - SCAD_AUTOMATIC = 2, /* Delaunay on planes, meshAdapt elsewhere */ + SCAD_AUTOMATIC = 2, /* Delaunay on planes, mesh adapt elsewhere */ SCAD_INITIAL_MESH_ONLY = 3, /* Avoid new point creation */ SCAD_DELAUNAY = 5, SCAD_FRONTAL_DELAUNAY = 6, @@ -155,7 +155,7 @@ enum scad_normals_orientation { /* A type to specify how partitioning is done. */ enum scad_partition_flags { SCAD_ALLOW_OVERLAPPING = BIT(0), - SCAD_DUMP_ON_OVERLAPPING_ERROR = BIT(1) + SCAD_DUMP_ON_OVERLAPPING_ERROR = BIT(1) /* Dump individual geometries to STL */ }; BEGIN_DECLS @@ -207,9 +207,7 @@ scad_scene_clear /* Write the whole scene in a format that depends on filename extension. * Available formats include "brep", "msh" (gmsh-specific format), "step", - * "stl", "vtk", etc. - * Note that due to gmsh behaviour, writing a scene that never contained any - * geometry ends in error. */ + * "stl", "vtk", etc. */ SCAD_API res_T scad_scene_write (const char* filename); @@ -236,11 +234,11 @@ scad_add_rectangle const double dxdy[2], struct scad_geometry** rectangle); -/* Add a disk in (xy) plane to the scene, defined by a the center `xyz' and +/* Add a disk in the (xy) plane to the scene, defined by its `center' and * `radius'. */ SCAD_API res_T scad_add_disk - (const double xyz[3], + (const double center[3], const double radius, struct scad_geometry** disk); @@ -263,21 +261,21 @@ scad_add_box const double dxdydz[3], struct scad_geometry** box); -/* Add a `cylinder' to the scene, defined by the center `xyz' of its first - * circular face, the vector `axis' defining its axis and its radius `rad'. The - * `angle' argument defines the angular opening (from 0 to 2*PI). */ +/* Add a `cylinder' to the scene, defined by the center `center' of its first + * circular face, the vector `axis' defining its axis and its radius `radius'. + * The `angle' argument defines the angular opening (from 0 to 2*PI). */ SCAD_API res_T scad_add_cylinder - (const double xyz[3], + (const double center[3], const double axis[3], const double radius, const double angle, struct scad_geometry** cylinder); -/* Add a `sphere' of center `xyz' and radius `rad' to the scene. */ +/* Add a `sphere' of center `center' and radius `radius' to the scene. */ SCAD_API res_T scad_add_sphere - (const double xyz[3], + (const double center[3], const double radius, struct scad_geometry** sphere); @@ -510,7 +508,7 @@ scad_geometries_intersect /* Compute the boolean fragments (general fuse) resulting from the * intersection of the geometries in `geometries', making all interfaces * conformal. - * `flags' should be made by Oring values from enum scad_partition_flags to + * `flags' should be made by ORing values from enum scad_partition_flags to * enable non default behaviours (default is to disallow overlapping). * The output geometries are created unnamed. * When applied to geometries of different dimensions, the lower dimensional @@ -651,17 +649,17 @@ scad_step_import SCAD_API res_T scad_stl_export (struct scad_geometry* geometry, - const char* filename, + const char* filename, /* Can be NULL if geometry name is defined */ const enum scad_normals_orientation orientation, - const int binary); /* File format */ + const int binary); /* Same as previous, but geometries that are part of `exclude' are not exported. */ SCAD_API res_T scad_stl_export_partial (struct scad_geometry* geometry, - struct scad_geometry** exclude, + struct scad_geometry** exclude, /* Can be NULL */ const size_t exclude_count, - const char* filename, + const char* filename, /* Can be NULL if geometry name is defined */ const enum scad_normals_orientation orientation, const int binary); @@ -671,9 +669,9 @@ scad_stl_export_partial SCAD_API res_T scad_stl_export_split (struct scad_geometry* geometry, - const char* filename, + const char* filename, /* Can be NULL if geometry name is defined */ const enum scad_normals_orientation orientation, - const int binary); /* File format */ + const int binary); /* Accumulate the mesh of the geometry `geometry' into `triangles', each triangle * being described by 9 doubles in a STL way. @@ -688,7 +686,7 @@ scad_stl_get_data SCAD_API res_T scad_stl_get_data_partial (struct scad_geometry* geometry, - struct scad_geometry** exclude, + struct scad_geometry** exclude, /* Can be NULL */ const size_t exclude_count, struct darray_double* triangles); diff --git a/src/scad_device.c b/src/scad_device.c @@ -314,6 +314,7 @@ scad_dump_geometries res_T res = RES_OK; struct scad_device* dev = get_device(); struct htable_geometries_iterator it, end; + size_t cpt = 0; ERR(check_device(FUNC_NAME)); @@ -326,8 +327,10 @@ scad_dump_geometries while(!htable_geometries_iterator_eq(&it, &end)) { struct scad_geometry* geom = *htable_geometries_iterator_key_get(&it); ERR(scad_dump_geometry(geom)); + cpt++; htable_geometries_iterator_next(&it); } + printf("Counted %ld geometries.\n", cpt); exit: return res; diff --git a/src/scad_device.h b/src/scad_device.h @@ -56,7 +56,9 @@ hash_str(const struct str* a) #include <rsys/hash_table.h> enum delete_policy { + /* Delete the tag and all its lower-dimension components */ Scad_delete_recursive, + /* Delete only the tag, not its lower-dimension components */ Scad_delete_non_recursive, Scad_do_not_delete }; diff --git a/src/scad_geometry.c b/src/scad_geometry.c @@ -811,7 +811,7 @@ scad_geometries_clear_mesh struct scad_device* dev = get_device(); struct mem_allocator* allocator = NULL; - if(!geometries) { + if(!geometries || geometries_count == 0) { res = RES_BAD_ARG; goto error; } @@ -1569,6 +1569,7 @@ scad_geometries_fuse * no longer used by any star-cad geometry */ gmshModelOccFuse(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn, &mapnn, -1, 0, 0, &ierr); + ASSERT(tagoutn % 2 == 0); ERR(gmsh_err_to_res_T(ierr)); ERR(geometry_create(&geom)); @@ -1673,6 +1674,7 @@ scad_geometries_cut * no longer used by any star-cad geometry */ gmshModelOccCut(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn, &mapnn, -1, 0, 0, &ierr); + ASSERT(tagoutn % 2 == 0); ERR(gmsh_err_to_res_T(ierr)); ERR(geometry_create(&geom)); @@ -1745,6 +1747,7 @@ scad_geometries_intersect * no longer used by any star-cad geometry */ gmshModelOccIntersect(data1, sz1, data2, sz2, &tagout, &tagoutn, &map, &mapn, &mapnn, -1, 0, 0, &ierr); + ASSERT(tagoutn % 2 == 0); ERR(gmsh_err_to_res_T(ierr)); ERR(geometry_create(&geom)); @@ -1985,9 +1988,6 @@ scad_geometry_extrude int *tagout = NULL; size_t tagoutn; size_t i; -#ifndef NDEBUG - size_t j; -#endif int *ed, *extrude_data = NULL; size_t extrude_sz = 0; int ierr = 0; @@ -2007,6 +2007,24 @@ scad_geometry_extrude ERR(check_device(FUNC_NAME)); allocator = dev->allocator; + /* OCC cannot extrude 3D geometries */ + for(i = 0; i < geom->gmsh_dimTags_n; i += 2) { + const int dim = geom->gmsh_dimTags[i]; + if(dim != 2) { + if(str_is_empty(&geom->name)) { + log_error(get_device(), + "Cannot extrude unnamed 3D geometry '%p'.\n", + (void*)geom); + } else { + log_error(get_device(), + "Cannot extrude 3D geometry '%s'.\n", + str_cget(&geom->name)); + } + res = RES_BAD_ARG; + goto error; + } + } + 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)); @@ -2027,12 +2045,6 @@ scad_geometry_extrude for(i = 0; i < tagoutn; i += 2) { int dim = tagout[i]; int tag = tagout[i+1]; -#ifndef NDEBUG - /* Expecting geom's tags not part of tagout */ - for(j = 0; j < geom->gmsh_dimTags_n; j += 2) { - ASSERT(dim != geom->gmsh_dimTags[j] || tag != geom->gmsh_dimTags[j+1]); - } -#endif if(dim == 3) { *ed++ = dim; *ed++ = tag; @@ -2070,15 +2082,12 @@ scad_geometry_extrude const int tag = geom->gmsh_dimTags[i+1]; struct tag_desc* desc = device_get_description(dim, tag); ASSERT(dim == 2); - if(!desc) { - res = RES_BAD_OP; - goto error; - } + 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, extrude_data, extrude_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 + * deleted, they shouldn't be deleted when the geometry they belongs to is * released. */ desc->delete_policy = Scad_do_not_delete; } @@ -2203,6 +2212,8 @@ scad_geometry_copy 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)); ERR(geometry_create(&copy)); @@ -2438,7 +2449,7 @@ scad_geometries_partition "Unnamed geometry '%p' overlapping (dumped in '%s).\n", (void*)g, str_cget(&tmp)); } else { - log_error(get_device(), "Could not dump geoemtry.\n"); + log_error(get_device(), "Could not dump geometry.\n"); } } else { str_printf(&tmp, "%s_partition_error_%lu_%lu", @@ -2450,7 +2461,7 @@ scad_geometries_partition "Geometry '%s' overlapping (dumped in '%s).\n", str_cget(&g->name), str_cget(&tmp)); } else { - log_error(get_device(), "Could not dump geoemtry.\n"); + log_error(get_device(), "Could not dump geometry.\n"); } } } @@ -2573,6 +2584,9 @@ error: } if(tagout) { gmshModelOccRemove(tagout, tagoutn, 1, &ierr); + /* Do not check ierr, first because we are in error handler already, but + * also because an error is expected if these OCC entities have allready + * been freed through the call to ref_put above */ } goto exit; } @@ -2679,8 +2693,7 @@ scad_geometries_swap for(n = 0; n < c2; n += 2) { int dim = dt2[n]; int tag = dt2[n+1]; - if(n) { ERR(str_append_printf(&msg, ",")); } - ERR(str_append_printf(&msg, " %d.%d", dim, tag)); + ERR(str_append_printf(&msg, (n ? ", %d.%d" : "%d.%d"), dim, tag)); } logger_print(dev->logger, dev->log_type, "%s.\n", str_cget(&msg)); if(str_is_empty(&g2->name)) { @@ -2734,7 +2747,7 @@ scad_geometries_boundary struct str msg, name; int msg_initialized = 0, name_initialized = 0; - if(!geometries || !out_boundaries || !out_count) { + if(!geometries || geometries_count == 0 || !out_boundaries || !out_count) { res = RES_BAD_ARG; goto error; } @@ -2936,7 +2949,7 @@ scad_geometry_get_normal struct darray_int tags; int initialized = 0; double d, min_d = DBL_MAX, pcoord_min[2]; - int min_tag; + int min_tag = -1; size_t normals_n; if(!geom || !p || !N) { @@ -3078,7 +3091,9 @@ scad_geometries_set_mesh_size_modifier int* tagout[4] = { NULL, NULL, NULL, NULL }; size_t tagoutn[4] = { 0, 0, 0, 0}, i; - if(!geometries || geometries_count == 0 || modifier <= 0) { + if(!geometries || geometries_count == 0 || modifier <= 0 + || (type != SCAD_ABSOLUTE_SIZE && type != SCAD_SIZE_FACTOR)) + { res = RES_BAD_ARG; goto error; } @@ -3137,7 +3152,14 @@ scad_geometries_set_mesh_algorithm int* tagout[4] = { NULL, NULL, NULL, NULL }; size_t tagoutn[4], i; - if(!geometries || geometries_count == 0) { + if(!geometries || geometries_count == 0 || + (algorithm != SCAD_MESHADAPT && + algorithm != SCAD_AUTOMATIC && + algorithm != SCAD_INITIAL_MESH_ONLY && + algorithm != SCAD_DELAUNAY && + algorithm != SCAD_FRONTAL_DELAUNAY && + algorithm != SCAD_QUASI_STRUCTURED)) + { res = RES_BAD_ARG; goto error; } @@ -3178,7 +3200,8 @@ scad_geometries_set_periodic 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; + size_t src_dimTagoutn[4] = { 0,0,0,0 }, tgt_dimTagoutn[4] = { 0,0,0,0 }; + size_t src_tagsn = 0, tgt_tagsn = 0, i; if(!source || source_count == 0 || !target || target_count == 0 || !affine) { res = RES_BAD_ARG;