star-cad

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

commit 9fb788a055d42ced6149c0a89280396eba8c7df3
parent 824ff8bfea364942354cbac1a4c71d96fc60136a
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Tue, 29 Jul 2025 15:19:09 +0200

Fix get_normal

Ensure the normal is computed at the closest point from the provided
position.
Fix a memleak.
Also fix an arg constness and allow a return arg to be NULL.

Diffstat:
Msrc/scad.h | 13+++++++++----
Msrc/scad_geometry.c | 108+++++++++++++++++++++++++++++++++++++++++--------------------------------------
2 files changed, 65 insertions(+), 56 deletions(-)

diff --git a/src/scad.h b/src/scad.h @@ -417,14 +417,19 @@ scad_geometry_get_closest_point struct scad_geometry** closest_geom); /* Can be NULL */ /* Get the normal of the geometry `geometry' 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'. */ + * The normal is set in `N' and the underlying 2D geometry on which `p' is + * located is returned as a new geometry in `underlying_geometry' if it is not + * NULL. + * If `geometry' is 3D, this underlying geometry is (a part of) its boundary. + * Note that the position `p' is supposed to be close enough of `geometry', or + * this operation is meaningless (as the normal is taken on a computed point on + * the geometry that is the closest point from position `p'). */ SCAD_API res_T scad_geometry_get_normal (struct scad_geometry* geometry, - double p[3], + const double p[3], double N[3], - struct scad_geometry** out_geometry); + struct scad_geometry** underlying_geometry); /* Can be NULL */ /* Get the Boundig Box of geometry `geometry' in the form of `min' and `max' * vectors. */ diff --git a/src/scad_geometry.c b/src/scad_geometry.c @@ -22,6 +22,7 @@ #include <rsys/mem_allocator.h> #include <rsys/str.h> #include <rsys/math.h> +#include <rsys/double2.h> #include <rsys/double3.h> #include <rsys/logger.h> #include <rsys/hash_table.h> @@ -2769,7 +2770,7 @@ error: res_T scad_geometry_get_normal (struct scad_geometry* geom, - double p[3], + const double p[3], double N[3], struct scad_geometry** out_geometry) { @@ -2780,16 +2781,19 @@ scad_geometry_get_normal size_t sz = 0; struct scad_geometry* out = NULL; struct scad_device* dev = get_device(); - int log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL); - enum log_type log_type = dev->log_type; + int log; + enum log_type log_type; struct mem_allocator* allocator = NULL; double* coord = NULL; double* pcoord = NULL; double* normals = NULL; struct darray_int tags; int initialized = 0; + double d, min_d = DBL_MAX, pcoord_min[2]; + int min_tag; + size_t normals_n; - if(!geom || !p || !N || !out_geometry) { + if(!geom || !p || !N) { res = RES_BAD_ARG; goto error; } @@ -2804,71 +2808,67 @@ scad_geometry_get_normal data = darray_int_cdata_get(&tags); sz = darray_int_size_get(&tags); + log = (dev->options.Misc.LogRefCounting & SCAD_LOG_DIMTAGS_ALL); + log_type = dev->log_type; + + /* Find the closest point on tags of geom */ for(i = 0; i < sz; ++i) { size_t pcoord_n; size_t coord_n; - size_t normals_n; const int dim = 2; int tag = data[i]; + double tmp[3]; gmshModelGetClosestPoint(dim, tag, p, 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); - - if(d3_eq_eps(p, coord, 1e-6)) { - gmshModelGetNormal(tag, pcoord, pcoord_n, &normals, &normals_n, &ierr); - ERR(gmsh_err_to_res_T(ierr)); - ASSERT(normals_n == 3); - - ERR(geometry_create(&out)); - out->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*out->gmsh_dimTags)); - if(!out->gmsh_dimTags) { - res = RES_MEM_ERR; - goto error; - } - out->gmsh_dimTags_n = 2; - out->gmsh_dimTags[0] = dim; - out->gmsh_dimTags[1] = tag; - d3_set(N, normals); - - ERR(device_register_tags(out)); - - /* Need to protect geometries' tags or deleting out geometry will possibly - * delete them */ - if(log) { - logger_print(dev->logger, log_type, - "Tag %d.%d getting a reference to other tags.\n", dim, tag); - } - ERR(device_register_ref_to_tags(dim, tag, geom->gmsh_dimTags, - geom->gmsh_dimTags_n)); - if(log) { - logger_print(dev->logger, log_type, - "Tag %d.%d getting a reference to other tags done.\n", dim, tag); - } - - break; + d = d3_len(d3_sub(tmp, p, coord)); + if(d < min_d) { + min_d = d; + min_tag = tag; + d2_set(pcoord_min, pcoord); } - gmshFree(coord); gmshFree(pcoord); 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)); + if(min_d == DBL_MAX) { /* At least if sz==0 */ + goto exit; + } + + /* Get the normal at the selected point */ + gmshModelGetNormal(min_tag, pcoord_min, 2, &normals, &normals_n, &ierr); + ERR(gmsh_err_to_res_T(ierr)); + ASSERT(normals_n == 3); + d3_set(N, normals); + + /* Create a geometry if required */ + if(out_geometry) { + ERR(geometry_create(&out)); + out->gmsh_dimTags = MEM_ALLOC(allocator, 2 * sizeof(*out->gmsh_dimTags)); + if(!out->gmsh_dimTags) { + res = RES_MEM_ERR; + goto error; + } + out->gmsh_dimTags_n = 2; + out->gmsh_dimTags[0] = 2; + out->gmsh_dimTags[1] = min_tag; + ERR(device_register_tags(out)); + + /* Need to protect geometries' tags or deleting out geometry will possibly + * delete them */ + if(log) { + logger_print(dev->logger, log_type, + "Tag %d.%d getting a reference to other tags.\n", 2, min_tag); + } + ERR(device_register_ref_to_tags(2, min_tag, geom->gmsh_dimTags, + geom->gmsh_dimTags_n)); + if(log) { + logger_print(dev->logger, log_type, + "Tag %d.%d getting a reference to other tags done.\n", 2, min_tag); } - res = RES_BAD_ARG; - goto error; } exit: @@ -2879,6 +2879,10 @@ exit: if(out_geometry) *out_geometry = out; return res; error: + if(out) { + SCAD(geometry_ref_put(out)); + out = NULL; + } goto exit; }