star-2d

Contour structuring for efficient 2D geometric queries
git clone git://git.meso-star.fr/star-2d.git
Log | Files | Refs | README | LICENSE

commit ac68ce2c111dee51d62210aae42cf4c203be61e8
parent d1cf0e76c3c0a0af8402fbe59fca14a24b2d2ff0
Author: Vincent Forest <vincent.forest@meso-star.com>
Date:   Wed, 22 Jul 2020 14:21:09 +0200

Add the s2d_scene_view_closest_point function

Diffstat:
Mcmake/CMakeLists.txt | 3++-
Msrc/s2d.h | 27+++++++++++++++++++++++----
Asrc/s2d_scene_view_closest_point.c | 212+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 237 insertions(+), 5 deletions(-)

diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -36,7 +36,7 @@ option(NO_TEST "Disable the test" OFF) ################################################################################ # Check dependencies ################################################################################ -find_package(Embree 3 REQUIRED) +find_package(Embree 3.6 REQUIRED) find_package(RCMake 0.2.2 REQUIRED) find_package(RSys 0.6 REQUIRED) @@ -67,6 +67,7 @@ set(S2D_FILES_SRC s2d_primitive.c s2d_scene.c s2d_scene_view.c + s2d_scene_view_closest_point.c s2d_shape.c) set(S2D_FILES_INC_API s2d.h) set(S2D_FILES_INC diff --git a/src/s2d.h b/src/s2d.h @@ -148,9 +148,10 @@ enum s2d_scene_view_flag { }; /* Filter function data type. One can define such function to discard - * intersections along a ray with respect to user defined criteria, e.g.: - * masked/transparent primitive, etc. Return 0 or the intersection is not - * discarded and a value not equal to zero otherwise. */ + * intersections along a ray or the result of a closest point query with + * respect to user defined criteria, e.g.: masked/transparent primitive, etc. + * Return 0 if the intersection is not discarded and a value not equal to zero + * otherwise. */ typedef int (*s2d_hit_filter_function_T) (const struct s2d_hit* hit, @@ -289,8 +290,26 @@ s2d_scene_view_trace_ray_3d void* ray_data, struct s2d_hit* hit); +/* Return the point onto the scene segments that is the closest of the + * submitted `pos'. Note that even though only one point is returned, several + * position can have the same minimal distance to the queried position. The + * `radius' parameter defines the maximum search distance around `pos'. Each + * candidate position are internally filtered by the hit_filter_function + * attached to the corresponding shape; the user can thus reject a candidate + * position according to its own criteria. This function can be called only if + * the scnview was created with the S2D_TRACE flag which is actually the flag + * used to tell Star-2D to internally build an acceleration structure on which + * this function relies. */ +S2D_API res_T +s2d_scene_view_closest_point + (struct s2d_scene_view* scnview, + const float pos[2], /* Position to query */ + const float radius, /* Search distance in ]0, radius[ */ + void* query_data, /* User data sent to the hit filter func. May be NULL */ + struct s2d_hit* hit); + /* Uniformly sample the scene and returned the sampled primitive and its sample - * position. CCan be called only if the scnview was created with the + * position. Can be called only if the scnview was created with the * S2D_SAMPLE flag */ S2D_API res_T s2d_scene_view_sample diff --git a/src/s2d_scene_view_closest_point.c b/src/s2d_scene_view_closest_point.c @@ -0,0 +1,212 @@ +/* Copyright (C) 2016-2019 |Meso|Star> (contact@meso-star.com) + * + * This software is governed by the CeCILL license under French law and + * abiding by the rules of distribution of free software. You can use, + * modify and/or redistribute the software under the terms of the CeCILL + * license as circulated by CEA, CNRS and INRIA at the following URL + * "http://www.cecill.info". + * + * As a counterpart to the access to the source code and rights to copy, + * modify and redistribute granted by the license, users are provided only + * with a limited warranty and the software's author, the holder of the + * economic rights, and the successive licensors have only limited + * liability. + * + * In this respect, the user's attention is drawn to the risks associated + * with loading, using, modifying and/or developing or reproducing the + * software by the user in light of its specific status of free software, + * that may mean that it is complicated to manipulate, and that also + * therefore means that it is reserved for developers and experienced + * professionals having in-depth computer knowledge. Users are therefore + * encouraged to load and test the software's suitability as regards their + * requirements in conditions enabling the security of their systems and/or + * data to be ensured and, more generally, to use and operate it in the + * same conditions as regards security. + * + * The fact that you are presently reading this means that you have had + * knowledge of the CeCILL license and that you accept its terms. */ + +#include "s2d.h" +#include "s2d_device_c.h" +#include "s2d_geometry.h" +#include "s2d_line_segments.h" +#include "s2d_scene_view_c.h" + +#include <rsys/float2.h> + +struct point_query_context { + struct RTCPointQueryContext rtc; + struct s2d_scene_view* scnview; + void* data; /* Per point query defined data */ +}; + +/******************************************************************************* + * Helper functions + ******************************************************************************/ +static INLINE float* +closest_point_segment + (const float p[2], /* Position */ + const float v0[2], /* 1st segment vertex */ + const float v1[2], /* 2nd segment vertex */ + const float E[2], /* v0 -> v1 vector */ + float closest_pt[2], /* Closest position */ + float* s) /* Parametric coordinate of the closest point onto [v0, v1] */ +{ + float v[2]; /* Vector from v0 to p */ + float segment_len; /* Length of the [v0, v1] */ + float dst; /* Dst from v0 to the orthogonal projection of p onto [v0, v1] */ + ASSERT(p && v0 && v1); + ASSERT(f2_eq(f2_sub(v, v1, v0), E)); + + /* Orthogonally project the point onto the segment */ + f2_sub(v, p, v0); + segment_len = f2_len(E); + dst = f2_dot(v, E); + + /* Check if the closest point is the segment vertex 'v0' */ + if(dst <= 0) { + *s = 0; + return f2_set(closest_pt, v0); + } + /* Check if the closest point is the segment vertex 'v1' */ + if(dst >= segment_len) { + *s = 1; + return f2_set(closest_pt, v1); + } + + /* The closest point is on the segment */ + *s = CLAMP(dst / segment_len, 0, 1); + return f2_add(closest_pt, f2_mulf(closest_pt, E, dst/segment_len), v0); +} + +static bool +closest_point_line_segments + (struct RTCPointQueryFunctionArguments* args, + struct geometry* geom, + void* query_data) +{ + struct s2d_hit hit = S2D_HIT_NULL; + struct s2d_hit* out_hit = NULL; + struct hit_filter* filter = NULL; + const uint32_t* ids = NULL; + float v0[2], v1[2]; /* Segment vertices */ + float E[2]; /* Segment vector */ + float N[2]; /* Segment normal */ + float query_pos[2]; /* Submitted position */ + float closest_point[2]; /* Computed closest point */ + float vec[2]; /* Vector from query pos to the closest point */ + float dst; /* Distance to the closest point */ + float s; /* Parametric coordinate of the closest point */ + ASSERT(args && geom); + ASSERT(args->primID < line_segments_get_nsegments(geom->lines)); + + /* Fetch the line segments indices */ + ids = line_segments_get_ids(geom->lines) + args->primID*2/*#indices per segment*/; + + /* Fetch segments vertices */ + ASSERT(geom->lines->attribs_type[S2D_POSITION] == S2D_FLOAT2); + f2_set(v0, line_segments_get_pos(geom->lines)+ids[0]*2/*#coords per vertex */); + f2_set(v1, line_segments_get_pos(geom->lines)+ids[1]*2/*#coords per vertex */); + f2_sub(E, v1, v0); + + query_pos[0] = args->query->x; + query_pos[1] = args->query->y; + + /* Compute the closest point ont the segment from the sumbitted query_pos */ + closest_point_segment(query_pos, v0, v1, E, closest_point, &s); + + f2_sub(vec, closest_point, query_pos); + dst = f2_len(vec); + if(dst >= args->query->radius) return 0; + + /* Compute the segment normal */ + N[0] = E[1]; + N[1] = E[0]; + + /* Flip the geometry normal wrt the flip line_segments flag */ + if(geom->flip_contour) f2_minus(N, N); + + /* Setup the hit */ + hit.prim.mesh__ = geom; + hit.prim.prim_id = args->primID; + hit.prim.geom_id = geom->name; + hit.prim.scene_prim_id = hit.prim.prim_id + geom->scene_prim_id_offset; + hit.normal[0] = N[0]; + hit.normal[1] = N[1]; + hit.u = s; + hit.distance = dst; + + /* `vec' is the direction along which the closest point was found. Submit it + * to the filter function as the direction of the computed hit. */ + filter = &geom->lines->filter; + if(filter->func + && filter->func(&hit, query_pos, vec, query_data, filter->data)) { + return 0; /* This point is filtered. Discard it! */ + } + + /* Update output data */ + out_hit = args->userPtr; + *out_hit = hit; + args->query->radius = dst; /* Shrink the query radius */ + + return 1; /* Notify that the query radius was updated */ +} + +static bool +closest_point(struct RTCPointQueryFunctionArguments* args) +{ + struct point_query_context* ctx = NULL; + struct geometry* geom = NULL; + ASSERT(args); + + ctx = CONTAINER_OF(args->context, struct point_query_context, rtc); + + /* Instance are not supported by Star-2D */ + ASSERT(args->context->instStackSize == 0); + + geom = scene_view_geometry_from_embree_id(ctx->scnview, args->geomID); + return closest_point_line_segments(args, geom, ctx->data); +} + +/******************************************************************************* + * Exported functions + ******************************************************************************/ +res_T +s2d_scene_view_closest_point + (struct s2d_scene_view* scnview, + const float pos[2], + const float radius, + void* query_data, + struct s2d_hit* hit) +{ + struct RTCPointQuery query; + struct point_query_context query_ctx; + + if(!scnview || !pos || radius <= 0 || !hit) + return RES_BAD_ARG; + + if((scnview->mask & S2D_TRACE) == 0) { + log_error(scnview->scn->dev, + "%s: the S2D_TRACE flag is not active onto the submitted scene view.\n", + FUNC_NAME); + return RES_BAD_OP; + } + + *hit = S2D_HIT_NULL; + + /* Initialise the point query */ + query.x = pos[0]; + query.y = pos[1]; + query.z = 0; + query.radius = radius; + query.time = FLT_MAX; /* Invalid fields */ + + /* Initialise the point query context */ + rtcInitPointQueryContext(&query_ctx.rtc); + query_ctx.scnview = scnview; + query_ctx.data = query_data; + + /* Here we go! */ + rtcPointQuery(scnview->rtc_scn, &query, &query_ctx.rtc, closest_point, hit); + return RES_OK; +}