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 c7d9e8a3d80d7b944aaded0c7508bd0d5064267d
parent 521029ced8411fdfdae832bae693ad82ff01b110
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Tue, 12 Jan 2021 15:31:29 +0100

Continue improving accuracy tests

Diffstat:
Msrc/test_s2d_closest_point.c | 140++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msrc/test_s2d_raytrace.c | 145+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
2 files changed, 157 insertions(+), 128 deletions(-)

diff --git a/src/test_s2d_closest_point.c b/src/test_s2d_closest_point.c @@ -332,7 +332,7 @@ test_single_segment(struct s2d_device* dev) float low[2], upp[2], mid[2]; float pos[2]; float closest_pos[2]; - size_t i, j; + size_t a, i, j; unsigned indices[2] = {0, 1}; f2(vertices+0, -0.5f, -0.3f); @@ -386,7 +386,8 @@ test_single_segment(struct s2d_device* dev) pos[0] = mid[0] + (rand_canonic() * 2 - 1) * (upp[0] - low[0]) * 5.f; pos[1] = mid[1] + (rand_canonic() * 2 - 1) * (upp[1] - low[1]) * 5.f; - CHK(s2d_scene_view_closest_point(view, pos, (float)INF, NULL, &hit) == RES_OK); + CHK(s2d_scene_view_closest_point(view, pos, (float)INF, NULL, &hit) + == RES_OK); CHK(!S2D_HIT_NONE(&hit)); /* Check that the radius is an exclusive upper bound */ @@ -402,72 +403,81 @@ test_single_segment(struct s2d_device* dev) CHK(s2d_scene_view_ref_put(view) == RES_OK); CHK(s2d_shape_ref_put(shape) == RES_OK); CHK(s2d_scene_ref_put(scn) == RES_OK); - - FOR_EACH(i, 0, 100) { - float A[2], B[2], tmp[2]; - const float amplitude = 10000; + + /* Check accuracy on a configuration whose analytic distance is known */ + FOR_EACH(a, 0, 16) { + const float amplitude = exp2f((float)a); const float eps = 1e-6f * amplitude; - /* Randomly generate a segment AB on the X axis */ - A[0] = (rand_canonic() - 0.5f) * amplitude; - do B[0] = (rand_canonic() - 0.5f) * amplitude; - while(fabsf(A[0] - B[0]) < eps); - A[1] = B[1] = 0; - - f2_set(vertices + 0, A); - f2_set(vertices + 2, B); - - CHK(s2d_scene_create(dev, &scn) == RES_OK); - CHK(s2d_shape_create_line_segments(dev, &shape) == RES_OK); - CHK(s2d_scene_attach_shape(scn, shape) == RES_OK); - - vdata.usage = S2D_POSITION; - vdata.type = S2D_FLOAT2; - vdata.get = line_segments_get_position; - desc.vertices = vertices; - desc.indices = indices; - CHK(s2d_line_segments_setup_indexed_vertices - (shape, 1, line_segments_get_ids, 2, &vdata, 1, &desc) == RES_OK); - - CHK(s2d_scene_view_create(scn, S2D_TRACE, &view) == RES_OK); - - FOR_EACH(j, 0, 100) { - /* Randomly generate a pos not on the segment */ - pos[0] = (rand_canonic() - 0.5f) * amplitude; - do pos[1] = (rand_canonic() - 0.5f) * amplitude; - while (fabsf(pos[1]) < 1e-5); - - /* Compute closest point */ - CHK(s2d_scene_view_closest_point(view, pos, (float)INF, NULL, &hit) == RES_OK); - CHK(!S2D_HIT_NONE(&hit)); - CHK(s2d_primitive_get_attrib(&hit.prim, S2D_POSITION, hit.u, &attr) == RES_OK); - - /* Cross check the closest point query result */ - closest_point_segment(pos, A, B, closest_pos); - CHK(f2_eq_eps(closest_pos, attr.value, eps)); - if(A[0] > pos[0] && B[0] > pos[0]) { - const float* X = (A[0] > B[0]) ? B : A; - const float d = f2_len(f2_sub(tmp, pos, X)); - CHK(f2_eq_eps(closest_pos, X, eps)); - CHK(eq_epsf(hit.distance, d, eps)); - } - else if(A[0] < pos[0] && B[0] < pos[0]) { - const float* X = (A[0] < B[0]) ? B : A; - const float d = f2_len(f2_sub(tmp, pos, X)); - CHK(f2_eq_eps(closest_pos, X, eps)); - CHK(eq_epsf(hit.distance, d, eps)); - } else { - float expected[2]; - float d; - f2(expected, pos[0], 0); - d = f2_len(f2_sub(tmp, pos, expected)); - CHK(f2_eq_eps(closest_pos, expected, eps)); - CHK(eq_epsf(hit.distance, d, eps)); + FOR_EACH(i, 0, 1000) { + float A[2], B[2], AB[2], N[2]; + int n; + + /* Randomly generate a segment AB */ + FOR_EACH(n, 0, 2) + A[n] = (rand_canonic() - 0.5f) * amplitude; + do { + FOR_EACH(n, 0, 2) B[n] = (rand_canonic() - 0.5f) * amplitude; + } while (f2_eq_eps(A, B, eps)); + + f2_sub(AB, B, A); + f2(N, -AB[1], AB[0]); + f2_normalize(N, N); + + f2_set(vertices + 0, A); + f2_set(vertices + 2, B); + + CHK(s2d_scene_create(dev, &scn) == RES_OK); + CHK(s2d_shape_create_line_segments(dev, &shape) == RES_OK); + CHK(s2d_scene_attach_shape(scn, shape) == RES_OK); + + vdata.usage = S2D_POSITION; + vdata.type = S2D_FLOAT2; + vdata.get = line_segments_get_position; + desc.vertices = vertices; + desc.indices = indices; + CHK(s2d_line_segments_setup_indexed_vertices + (shape, 1, line_segments_get_ids, 2, &vdata, 1, &desc) == RES_OK); + + CHK(s2d_scene_view_create(scn, S2D_TRACE, &view) == RES_OK); + + FOR_EACH(j, 0, 1000) { + float proj[2]; /* Projection of pos on the line */ + float u, h, tmp[2]; + + /* Randomly generate a pos not on the segment + * with know position wrt the problem: pos = A + u.AB + k.N */ + u = 3 * rand_canonic() - 1; + h = (2 * rand_canonic() - 1) * amplitude; + f2_add(proj, A, f2_mulf(proj, AB, u)); + f2_add(pos, proj, f2_mulf(pos, N, h)); + + /* Compute closest point */ + CHK(s2d_scene_view_closest_point(view, pos, (float)INF, NULL, &hit) + == RES_OK); + CHK(!S2D_HIT_NONE(&hit)); + CHK(s2d_primitive_get_attrib(&hit.prim, S2D_POSITION, hit.u, &attr) + == RES_OK); + + /* Check result */ + if(u <= 0) { + const float d = f2_len(f2_sub(tmp, pos, A)); + CHK(f2_eq_eps(attr.value, A, eps)); + CHK(eq_epsf(hit.distance, d, eps)); + } + else if(u >= 1) { + const float d = f2_len(f2_sub(tmp, pos, B)); + CHK(f2_eq_eps(attr.value, B, eps)); + CHK(eq_epsf(hit.distance, d, eps)); + } else { + CHK(f2_eq_eps(attr.value, proj, eps)); + CHK(eq_epsf(hit.distance, fabsf(h), eps)); + } } - } - CHK(s2d_scene_view_ref_put(view) == RES_OK); - CHK(s2d_shape_ref_put(shape) == RES_OK); - CHK(s2d_scene_ref_put(scn) == RES_OK); + CHK(s2d_scene_view_ref_put(view) == RES_OK); + CHK(s2d_shape_ref_put(shape) == RES_OK); + CHK(s2d_scene_ref_put(scn) == RES_OK); + } } } diff --git a/src/test_s2d_raytrace.c b/src/test_s2d_raytrace.c @@ -49,7 +49,7 @@ test_single_segment(struct s2d_device* dev) float vertices[4]; float v0[2], v1[2]; float pos[2]; - size_t i, j; + size_t a, i, j; unsigned indices[2] = {0, 1}; f2(vertices+0, -0.5f, -0.3f); @@ -75,71 +75,90 @@ test_single_segment(struct s2d_device* dev) CHK(s2d_scene_view_ref_put(view) == RES_OK); CHK(s2d_shape_ref_put(shape) == RES_OK); CHK(s2d_scene_ref_put(scn) == RES_OK); - - FOR_EACH(i, 0, 100) { - float A[2], B[2], tmp[2]; - const float amplitude = 10000; + + /* Check accuracy on a configuration whose analytic distance is known */ + FOR_EACH(a, 0, 16) { + const float amplitude = exp2f((float)a); const float eps = 1e-6f * amplitude; - /* Randomly generate a segment AB on the X axis */ - A[0] = (rand_canonic() - 0.5f) * amplitude; - do B[0] = (rand_canonic() - 0.5f) * amplitude; - while(fabsf(A[0] - B[0]) < eps); - A[1] = B[1] = 0; - - f2_set(vertices + 0, A); - f2_set(vertices + 2, B); - - CHK(s2d_scene_create(dev, &scn) == RES_OK); - CHK(s2d_shape_create_line_segments(dev, &shape) == RES_OK); - CHK(s2d_scene_attach_shape(scn, shape) == RES_OK); - - vdata.usage = S2D_POSITION; - vdata.type = S2D_FLOAT2; - vdata.get = line_segments_get_position; - desc.vertices = vertices; - desc.indices = indices; - CHK(s2d_line_segments_setup_indexed_vertices - (shape, 1, line_segments_get_ids, 2, &vdata, 1, &desc) == RES_OK); - - CHK(s2d_scene_view_create(scn, S2D_TRACE, &view) == RES_OK); - - FOR_EACH(j, 0, 100) { - float l; - float dir[3], range[2] = { 0, FLT_MAX }; - - /* Randomly generate a pos not on the segment */ - pos[0] = (rand_canonic() - 0.5f) * amplitude; - do pos[1] = (rand_canonic() - 0.5f) * amplitude; - while (fabsf(pos[1]) < 1e-5); - - /* Raytrace towards the X axis (can hit or miss the segment) */ - f3(dir, 0, (pos[1] > 0 ? -1.f : 1.f), rand_canonic() - 0.5f); - CHK(s2d_scene_view_trace_ray(view, pos, dir, range, NULL, &hit) == RES_OK); - l = f3_normalize(dir, dir); - CHK(s2d_scene_view_trace_ray_3d(view, pos, dir, range, NULL, &hit3) == RES_OK); - - /* Check the result */ - if((A[0] > pos[0] && B[0] > pos[0]) || (A[0] < pos[0] && B[0] < pos[0])) { - CHK(S2D_HIT_NONE(&hit)); - CHK(S2D_HIT_NONE(&hit3)); - } else { - float expected[2]; - float d = f2_len(f2_sub(tmp, pos, expected)); - f2(expected, pos[0], 0); - d = f2_len(f2_sub(tmp, pos, expected)); - CHK(!S2D_HIT_NONE(&hit)); - CHK(eq_epsf(hit.distance, d, eps)); - CHK(eq_epsf(hit3.distance, d * l, eps)); - CHK(s2d_primitive_get_attrib(&hit.prim, S2D_POSITION, hit.u, &attr) == RES_OK); - CHK(f2_eq_eps(attr.value, expected, eps)); - CHK(s2d_primitive_get_attrib(&hit3.prim, S2D_POSITION, hit3.u, &attr) == RES_OK); - CHK(f2_eq_eps(attr.value, expected, eps)); + FOR_EACH(i, 0, 1000) { + float A[2], B[2], AB[2], N[2]; + int n; + + /* Randomly generate a segment AB */ + FOR_EACH(n, 0, 2) + A[n] = (rand_canonic() - 0.5f) * amplitude; + do { + FOR_EACH(n, 0, 2) B[n] = (rand_canonic() - 0.5f) * amplitude; + } while (f2_eq_eps(A, B, eps)); + + f2_sub(AB, B, A); + f2(N, -AB[1], AB[0]); + f2_normalize(N, N); + + f2_set(vertices + 0, A); + f2_set(vertices + 2, B); + + CHK(s2d_scene_create(dev, &scn) == RES_OK); + CHK(s2d_shape_create_line_segments(dev, &shape) == RES_OK); + CHK(s2d_scene_attach_shape(scn, shape) == RES_OK); + + vdata.usage = S2D_POSITION; + vdata.type = S2D_FLOAT2; + vdata.get = line_segments_get_position; + desc.vertices = vertices; + desc.indices = indices; + CHK(s2d_line_segments_setup_indexed_vertices + (shape, 1, line_segments_get_ids, 2, &vdata, 1, &desc) == RES_OK); + + CHK(s2d_scene_view_create(scn, S2D_TRACE, &view) == RES_OK); + + FOR_EACH(j, 0, 1000) { + float proj[2]; /* Projection of pos on the line */ + float u, h, l; + float dir[3], range[2] = { 0, FLT_MAX }; + + /* Randomly generate a pos not on the segment + * with know position wrt the problem: pos = A + u.AB + k.N */ + u = 1.2f * rand_canonic() - 0.1f; + h = (2 * rand_canonic() - 1) * amplitude; + f2_add(proj, A, f2_mulf(proj, AB, u)); + f2_add(pos, proj, f2_mulf(pos, N, h)); + + /* Raytrace from pos towards proj */ + f2_mulf(dir, N, (h > 0 ? -1.f : 1.f)); + CHK(s2d_scene_view_trace_ray(view, pos, dir, range, NULL, &hit) + == RES_OK); + dir[2] = rand_canonic() - 0.5f; + l = f3_normalize(dir, dir); + CHK(s2d_scene_view_trace_ray_3d(view, pos, dir, range, NULL, &hit3) + == RES_OK); + + /* Check result */ + if(u < 0 || u > 1) { + if(!S2D_HIT_NONE(&hit) || !S2D_HIT_NONE(&hit3)) + CHK(u >= -FLT_EPSILON && u <= 1 + FLT_EPSILON); + } else { + if(S2D_HIT_NONE(&hit) || S2D_HIT_NONE(&hit3)) + CHK(u <= FLT_EPSILON || u >= 1 - FLT_EPSILON); + } + if(!S2D_HIT_NONE(&hit)) { + CHK(eq_epsf(hit.distance, fabsf(h), eps)); + CHK(s2d_primitive_get_attrib(&hit.prim, S2D_POSITION, hit.u, &attr) + == RES_OK); + CHK(f2_eq_eps(attr.value, proj, eps)); + } + if(!S2D_HIT_NONE(&hit3)) { + CHK(eq_epsf(hit3.distance, l * fabsf(h), eps)); + CHK(s2d_primitive_get_attrib(&hit3.prim, S2D_POSITION, hit3.u, &attr) + == RES_OK); + CHK(f2_eq_eps(attr.value, proj, eps)); + } } - } - CHK(s2d_scene_view_ref_put(view) == RES_OK); - CHK(s2d_shape_ref_put(shape) == RES_OK); - CHK(s2d_scene_ref_put(scn) == RES_OK); + CHK(s2d_scene_view_ref_put(view) == RES_OK); + CHK(s2d_shape_ref_put(shape) == RES_OK); + CHK(s2d_scene_ref_put(scn) == RES_OK); + } } }