star-enclosures-3d

Extract enclosures from 3D geometry
git clone git://git.meso-star.fr/star-enclosures-3d.git
Log | Files | Refs | README | LICENSE

commit 169986528c08c8bfef4f3f3515bd9529030b9502
parent 90edf161d38fa7bb9e1a0e2e8aba9996bcba5bf9
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Fri,  5 Apr 2024 15:18:48 +0200

Add flexible debug code for connex components grouping

Just in case this tool is needed to fix further bugs, allow to dump
components to STL files and to log algorithm flow

Diffstat:
Msrc/senc3d.h | 11++++++++++-
Msrc/senc3d_scene_analyze.c | 159+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 169 insertions(+), 1 deletion(-)

diff --git a/src/senc3d.h b/src/senc3d.h @@ -141,7 +141,16 @@ enum senc3d_convention { /* Geometrical normals point toward the enclosure */ SENC3D_CONVENTION_NORMAL_INSIDE = BIT(2), /* Geometrical normals point to the opposite of the enclosure */ - SENC3D_CONVENTION_NORMAL_OUTSIDE = BIT(3) + SENC3D_CONVENTION_NORMAL_OUTSIDE = BIT(3), + + /* + * Additional bits used for debugging purposes + */ + + /* Dump identified connex components before grouping in STL files */ + SENC3D_DUMP_COMPONENTS_STL = BIT(4), + /* Extensive logs on grouping algorithm */ + SENC3D_LOG_COMPONENTS_INFORMATION = BIT(5) }; BEGIN_DECLS diff --git a/src/senc3d_scene_analyze.c b/src/senc3d_scene_analyze.c @@ -341,12 +341,22 @@ self_hit_filter const union double3* vertices = darray_position_cdata_get(&ctx->scn->vertices); enum senc3d_side hit_side; float s = 0, hit_normal[3], rdir[3]; + const int log_components = + ctx->scn->convention & SENC3D_LOG_COMPONENTS_INFORMATION; ASSERT(hit->prim.prim_id < darray_triangle_comp_size_get(ctx->triangles_comp)); + if(log_components) { + printf("Component #%u: investigating hit (d=%g, n= %g %g %g).\n", + ctx->origin_component, hit->distance, SPLIT3(hit->normal)); + } + if(hit->distance > ctx->hit_dist) { /* No improvement */ + if(log_components) { + printf("Component #%u: further away => reject.\n", ctx->origin_component); + } return 1; } @@ -354,11 +364,17 @@ self_hit_filter || hit_comp[SENC3D_BACK] == ctx->origin_component) { /* Self hit */ + if(log_components) { + printf("Component #%u: self hit => reject.\n", ctx->origin_component); + } return 1; } if(hit->distance > 0 && ray_dir[2] <= 0) { /* Not upward */ + if(log_components) { + printf("Component #%u: not upward => reject.\n", ctx->origin_component); + } return 1; } @@ -374,15 +390,26 @@ self_hit_filter ASSERT(c < darray_ptr_component_descriptor_size_get(ctx->components)); if(c == ctx->hit_component) { /* Cannot change ctx->hit_component */ + if(log_components) { + printf("Component #%u: hit component #%u and already linked to it:" + " reject\n", ctx->origin_component, c); + } continue; } if(comp_descriptors[c]->is_outer_border) { double v; /* The inner component we are trying to link can only be linked to * an outer component if it is inside */ + if(log_components) { + printf("Component #%u: hit outer component #%u\n", + ctx->origin_component, c); + } if(!is_component_inside(comp_descriptors[c], comp_descriptors[ctx->origin_component], ctx)) { + if(log_components) { + printf("Component #%u: not inside: reject\n", ctx->origin_component); + } continue; } v = fabs(comp_descriptors[c]->_6volume); @@ -398,7 +425,20 @@ self_hit_filter ctx->current_6volume = v; ctx->hit_dist = 0; ctx->hit_prim = hit->prim; + if(log_components) { + if(v < ctx->current_6volume) { + printf("Component #%u: currently the smaller one: keep component" + " #%u\n", ctx->origin_component, ctx->hit_component); + } else { + printf("Component #%u: change from inner to outer: keep component" + " #%u\n", ctx->origin_component, ctx->hit_component); + } + } } else { + if(log_components) { + printf("Component #%u: not the smaller one: reject\n", + ctx->origin_component); + } continue; } } else { @@ -407,9 +447,17 @@ self_hit_filter double org_z, v; /* If we've already found a valid outer component, inner components * should not be considered anymore */ + if(log_components) { + printf("Component #%u: hit inner component #%u\n", + ctx->origin_component, c); + } if(ctx->hit_component != COMPONENT_NULL__ && comp_descriptors[ctx->hit_component]->is_outer_border ) { + if(log_components) { + printf("Component #%u: already in an outer component: reject\n", + ctx->origin_component); + } continue; } /* The inner component we are trying to link can only be linked to @@ -421,11 +469,18 @@ self_hit_filter ASSERT(c_z_id < darray_position_size_get(&ctx->scn->vertices)); ASSERT(vertices[c_z_id].pos.z >= org_z); if(vertices[c_z_id].pos.z == org_z) { + if(log_components) { + printf("Component #%u: not (even in part) above: reject\n", + ctx->origin_component); + } continue; /* Not above */ } if(is_component_inside(comp_descriptors[c], comp_descriptors[ctx->origin_component], ctx)) { + if(log_components) { + printf("Component #%u: not outside: reject\n", ctx->origin_component); + } continue; /* Inside */ } v = fabs(comp_descriptors[c]->_6volume); @@ -437,7 +492,15 @@ self_hit_filter ctx->current_6volume = v; ctx->hit_dist = 0; ctx->hit_prim = hit->prim; + if(log_components) { + printf("Component #%u: currently the bigger one: keep component" + " #%u\n", ctx->origin_component, ctx->hit_component); + } } else { + if(log_components) { + printf("Component #%u: not the bigger one: reject\n", + ctx->origin_component); + } continue; } } @@ -453,6 +516,10 @@ self_hit_filter f3_set(ctx->hit_dir, ray_dir); ctx->hit_dist = hit->distance; ctx->hit_prim = hit->prim; + if(log_components) { + printf("Component #%u: 2 sides with same component: keep component" + " #%u\n", ctx->origin_component, ctx->hit_component); + } return 1; } @@ -467,10 +534,17 @@ self_hit_filter f3_normalize(rdir, rdir); s = f3_dot(rdir, hit_normal); ASSERT(!isnan(s)); + if(log_components) { + printf("Component #%u: had to fix s (was NaN)\n", ctx->origin_component); + } } if(ctx->hit_dist == hit->distance && fabsf(ctx->s) >= fabsf(s)) { /* Same distance with no s improvement: keep the previous hit */ + if(log_components) { + printf("Component #%u: not improving s (%g VS %g): reject\n", + ctx->origin_component, s, ctx->s); + } return 1; } @@ -481,6 +555,10 @@ self_hit_filter f3_set(ctx->hit_dir, ray_dir); ctx->hit_dist = hit->distance; ctx->hit_prim = hit->prim; + if(log_components) { + printf("Component #%u: tiny s (%g): keep but don't know the component\n", + ctx->origin_component, s); + } return 1; } /* Determine which side was hit */ @@ -496,6 +574,10 @@ self_hit_filter f3_set(ctx->hit_dir, ray_dir); ctx->hit_dist = hit->distance; ctx->hit_prim = hit->prim; + if(log_components) { + printf("Component #%u: standard s (%g): keep component #%u\n", + ctx->origin_component, s, ctx->hit_component); + } return 1; } } @@ -1034,6 +1116,8 @@ group_connex_components int64_t ccc; struct filter_ctx ctx0, ctx1; float lower[3], upper[3]; + const int log_components = scn->convention & SENC3D_LOG_COMPONENTS_INFORMATION; + const int dump_components = scn->convention & SENC3D_DUMP_COMPONENTS_STL; ASSERT(scn && triangles_comp && connex_components && s3d_view && next_enclosure_id && res); @@ -1053,6 +1137,65 @@ group_connex_components cc_count = (component_id_t)tmp; positions = darray_position_cdata_get(&scn->vertices); + if(dump_components) { + /* Do it now before any other problem can occur (fingers crossed). + * Do it sequential and not optimized as it is debug code. + * Don't throw errors, just skip to the next component. */ + static unsigned scene_cpt = 0; + struct str name; + res_T tmp_res; + const struct triangle_comp* tc = + darray_triangle_comp_cdata_get(triangles_comp); + const struct triangle_in* tin = darray_triangle_in_cdata_get(&scn->triangles_in); + const int output_normal_in = + (scn->convention & SENC3D_CONVENTION_NORMAL_INSIDE) != 0; + str_init(scn->dev->allocator, &name); + printf("Dumping components for scene #%u.\n", scene_cpt); + for(ccc = 0; ccc < (int64_t)cc_count; ccc++) { + FILE* f; + trg_id_t t; + component_id_t c = (component_id_t)ccc; + tmp_res = str_printf(&name, "scn_%u_comp_%u.stl", scene_cpt, c); + if(tmp_res != RES_OK) continue; + f = fopen(str_cget(&name), "w"); + if(!f) continue; + fprintf(f, "solid %s\n", str_cget(&name)); + for(t = 0; t < scn->ntris; t++) { + const component_id_t cf_id = tc[t].component[SENC3D_FRONT]; + const component_id_t cb_id = tc[t].component[SENC3D_BACK]; + if(cf_id == c || cb_id == c) { + const vrtx_id_t* vertice_id = tin[t].vertice_id; + double n[3], e1[3], e2[3]; + const int input_normal_in = (cf_id == c); + const int revert_triangle = (input_normal_in != output_normal_in); + const vrtx_id_t i0 = vertice_id[0]; + const vrtx_id_t i1 = vertice_id[revert_triangle ? 2 : 1]; + const vrtx_id_t i2 = vertice_id[revert_triangle ? 1 : 2]; + /* This triangle is in component #c */ + if(cf_id == cb_id) { /* Both sides in component */ + /* Could add some log */ + } + d3_sub(e1, positions[i1].vec, positions[i0].vec); + d3_sub(e2, positions[i2].vec, positions[i0].vec); + d3_normalize(n, d3_cross(n, e1, e2)); + fprintf(f, " facet normal %16g %16g %16g\n", SPLIT3(n)); + fprintf(f, " outer loop\n"); + fprintf(f, " vertex %16g %16g %16g\n", SPLIT3(positions[i0].vec)); + fprintf(f, " vertex %16g %16g %16g\n", SPLIT3(positions[i1].vec)); + fprintf(f, " vertex %16g %16g %16g\n", SPLIT3(positions[i2].vec)); + fprintf(f, " endloop\n"); + fprintf(f, " endfacet\n"); + } + } + printf("Dumped component #%u in file %s.\n", c, str_cget(&name)); + fprintf(f, "endsolid %s\n", str_cget(&name)); + fclose(f); + } + str_release(&name); + scene_cpt++; + } + + ctx0.type = FCTX0; ctx0.c.ctx0.triangles_comp = triangles_comp; ctx1.type = FCTX1; @@ -1084,6 +1227,9 @@ group_connex_components id = ATOMIC_INCR(next_enclosure_id) - 1; ASSERT(id <= ENCLOSURE_MAX__); cc->enclosure_id = (enclosure_id_t)id; + if(log_components) { + printf("Component #%u: is outer, not processed\n", c); + } continue; } @@ -1108,6 +1254,9 @@ group_connex_components if(S3D_HIT_NONE(&hit)) { cc->cc_group_root = CC_GROUP_ROOT_INFINITE; cc->enclosure_id = 0; + if(log_components) { + printf("Component #%u: is part of enclosure #0\n", c); + } continue; } @@ -1119,6 +1268,10 @@ group_connex_components /* New search radius is hit.distance + some margin to cope with numerical * issues (and r==0 is an error) */ r = hit.distance * (1 + FLT_EPSILON) + FLT_EPSILON; + if(log_components) { + printf("Component #%u: starting search for components (R=%g) from %g %g %g\n", + c, r, SPLIT3(origin)); + } /* Cast a sphere to find links between connex components */ tmp_res = s3d_scene_view_closest_point(s3d_view, origin, r, &ctx1, &hit); if(tmp_res != RES_OK) { @@ -1131,6 +1284,9 @@ group_connex_components if(ctx1.c.ctx1.hit_dist == FLT_MAX) { cc->cc_group_root = CC_GROUP_ROOT_INFINITE; cc->enclosure_id = 0; + if(log_components) { + printf("Component #%u: is part of enclosure #0\n", c); + } continue; } else if(ctx1.c.ctx1.hit_component == COMPONENT_NULL__) { @@ -1146,6 +1302,9 @@ group_connex_components /* If hit, group this component */ cc->cc_group_root = ctx1.c.ctx1.hit_component; ASSERT(cc->cc_group_root < cc_count); + if(log_components) { + printf("Component #%u: linked to component #%u\n", c, cc->cc_group_root); + } } } /* Implicit barrier here */