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:
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;
}