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 946a6f56457162ac5a549396460ba152b8ee319e
parent 2bfbf3e2a97d217b66724a7b0cea260a4c2d789b
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Tue,  5 Sep 2023 10:36:31 +0200

Fix a crash linked to numerical accuracy

Diffstat:
Msrc/senc3d_scene_analyze.c | 47+++++++++++++++++++++++++----------------------
1 file changed, 25 insertions(+), 22 deletions(-)

diff --git a/src/senc3d_scene_analyze.c b/src/senc3d_scene_analyze.c @@ -163,17 +163,18 @@ self_hit_filter { struct filter_ctx* fctx_ = ray_data; struct filter_ctx1* fctx; - const struct triangle_comp* components; + const struct triangle_comp* trg_comp; const component_id_t* hit_comp; float s = 0; enum senc3d_side hit_side; int i; - double mz = -INF; + double org_z, mz = -INF; const struct triangle_in* triangles; const struct triangle_in* trg = NULL; const union double3* vertices; + struct cc_descriptor* const* comp_descriptors; - (void)ray_org; (void)ray_dir; (void)ray_range; (void)filter_data; + (void)ray_dir; (void)ray_range; (void)filter_data; ASSERT(hit && fctx_); if(fctx_->type == FCTX2) { @@ -182,8 +183,8 @@ self_hit_filter struct filter_ctx2* ctx2 = &fctx_->c.ctx2; ASSERT(hit->prim.prim_id < darray_triangle_comp_size_get(ctx2->triangles_comp)); - components = darray_triangle_comp_cdata_get(ctx2->triangles_comp); - hit_comp = components[hit->prim.prim_id].component; + trg_comp = darray_triangle_comp_cdata_get(ctx2->triangles_comp); + hit_comp = trg_comp[hit->prim.prim_id].component; if(hit_comp[SENC3D_FRONT] == ctx2->component || hit_comp[SENC3D_BACK] == ctx2->component) { @@ -193,11 +194,11 @@ self_hit_filter } /* The filter is called from a point query on successive hits found from - * ray_org, that belongs to origin_component. It can keep or reject the hit. + * ray_org, that belongs to origin_component. It can keep or reject the hit. * Hits are only submitted inside a certain radius from ray_org, that is * decreased to the hit distance for every hit that is kept. * At the end, the last kept hit (= the closest), determines a component to - * which origin_component is linked. At a later stage the algorithm proceeds + * which origin_component is linked. At a later stage the algorithm process * linked components to determine their relative inclusions. * * For each hit, the filter computes if the hit is on a component above @@ -211,8 +212,9 @@ self_hit_filter * and remains undetected by star enclosures). */ ASSERT(fctx_->type == FCTX1); fctx = &fctx_->c.ctx1; - components = darray_triangle_comp_cdata_get(fctx->triangles_comp); - hit_comp = components[hit->prim.prim_id].component; + comp_descriptors = darray_ptr_component_descriptor_cdata_get(fctx->components); + trg_comp = darray_triangle_comp_cdata_get(fctx->triangles_comp); + hit_comp = trg_comp[hit->prim.prim_id].component; triangles = darray_triangle_in_cdata_get(&fctx->scn->triangles_in); vertices = darray_position_cdata_get(&fctx->scn->vertices); ASSERT(hit->prim.prim_id @@ -228,8 +230,6 @@ self_hit_filter if(hit->distance == 0) { /* origin_component is in contact with some other components * We will need further exploration to know if they should be considered */ - struct cc_descriptor* const* descriptors - = darray_ptr_component_descriptor_cdata_get(fctx->components); int n; /* If same component, process only once */ @@ -242,14 +242,14 @@ self_hit_filter struct s3d_hit hit2 = S3D_HIT_NULL; struct filter_ctx fctx2; ASSERT(c < darray_ptr_component_descriptor_size_get(fctx->components)); - if(descriptors[c]->is_outer_border) { - if(fabs(descriptors[c]->_6volume) - <= fabs(descriptors[fctx->origin_component]->_6volume)) + if(comp_descriptors[c]->is_outer_border) { + if(fabs(comp_descriptors[c]->_6volume) + <= fabs(comp_descriptors[fctx->origin_component]->_6volume)) /* Component is not large enough to include origin_component */ continue; } else { - vrtx_id_t c_z_id = descriptors[c]->max_z_vrtx_id; - vrtx_id_t o_z_id = descriptors[fctx->origin_component]->max_z_vrtx_id; + vrtx_id_t c_z_id = comp_descriptors[c]->max_z_vrtx_id; + vrtx_id_t o_z_id = comp_descriptors[fctx->origin_component]->max_z_vrtx_id; ASSERT(c_z_id < darray_position_size_get(&fctx->scn->vertices)); ASSERT(o_z_id < darray_position_size_get(&fctx->scn->vertices)); if(vertices[c_z_id].pos.z <= vertices[o_z_id].pos.z) @@ -260,14 +260,14 @@ self_hit_filter * overlap, testing a single point is OK, as long as the point is not on * c boundary (can be on origin_component boundary, though). * As this case is supposed to be rare, we go for a basic algorithm */ - for(side = descriptors[fctx->origin_component]->side_range.first; - side <= descriptors[fctx->origin_component]->side_range.last; + for(side = comp_descriptors[fctx->origin_component]->side_range.first; + side <= comp_descriptors[fctx->origin_component]->side_range.last; side++) { /* Find a triangle on origin_component boundary that is not on c * boundary (the 2 components cannot share all their triangles) */ trg_id_t t = TRGSIDE_2_TRG(side); - const component_id_t* candidate_comp = components[t].component; + const component_id_t* candidate_comp = trg_comp[t].component; if(candidate_comp[SENC3D_FRONT] != fctx->origin_component && candidate_comp[SENC3D_BACK] != fctx->origin_component) continue; @@ -296,8 +296,8 @@ self_hit_filter ASSERT(S3D_HIT_NONE(&hit2)); /* The ray is supposed to go to infinity */ /* origin_component is linked_to an outer component if cpt is odd, * linked_to an inner component if cpt is even */ - if(descriptors[c]->is_outer_border == (fctx2.c.ctx2.cpt % 2)) { - double v = fabs(descriptors[c]->_6volume); + if(comp_descriptors[c]->is_outer_border == (fctx2.c.ctx2.cpt % 2)) { + double v = fabs(comp_descriptors[c]->_6volume); /* If origin_component is inside several components, the one we are * looking for is the smallest one */ if(v >= fctx->current_6volume) continue; @@ -324,7 +324,10 @@ self_hit_filter ASSERT(v < darray_position_size_get(&fctx->scn->vertices)); if(i == 0 || mz < p->pos.z) mz = p->pos.z; } - if(mz <= ray_org[2]) + /* Don't use org[2] as, being float, it would lead to a float VS double + * comparison that causes accuracy problems. */ + org_z = vertices[comp_descriptors[fctx->origin_component]->max_z_vrtx_id].pos.z; + if(mz <= org_z) return 1; /* Hit triangle is below ray_org: reject */ if(hit_comp[SENC3D_FRONT] == hit_comp[SENC3D_BACK]) {