| 300 | } |
| 301 | |
| 302 | Expected<FaceBitSet> findDisorientedFaces( const Mesh& mesh, const FindDisorientationParams& params ) |
| 303 | { |
| 304 | MR_TIMER; |
| 305 | auto disorientedFaces = mesh.topology.getValidFaces(); |
| 306 | |
| 307 | Mesh cpyMesh; |
| 308 | const Mesh* targetMesh{ &mesh }; |
| 309 | EdgeBitSet outHoles; |
| 310 | if ( params.virtualFillHoles && mesh.topology.findNumHoles( &outHoles ) > 0 ) |
| 311 | { |
| 312 | cpyMesh = mesh; |
| 313 | targetMesh = &cpyMesh; |
| 314 | auto sb = subprogress( params.cb, 0.0f, 0.5f ); |
| 315 | int i = 0; |
| 316 | int num = int( outHoles.count() ); |
| 317 | auto metric = getMinAreaMetric( mesh ); |
| 318 | for ( auto e : outHoles ) |
| 319 | { |
| 320 | ++i; |
| 321 | fillHole( cpyMesh, e, { .metric = metric } ); // use simplest filling |
| 322 | if ( !reportProgress( sb, float( i ) / float( num ) ) ) |
| 323 | return unexpectedOperationCanceled(); |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | auto sb = subprogress( params.cb, targetMesh == &mesh ? 0.0f : 0.5f, 1.0f ); |
| 328 | |
| 329 | auto keepGoing = BitSetParallelFor( mesh.topology.getValidFaces(), [&] ( FaceId f ) |
| 330 | { |
| 331 | auto normal = Vector3d( mesh.normal( f ) ); |
| 332 | auto triCenter = Vector3d( mesh.triCenter( f ) ); |
| 333 | int counter = 0; |
| 334 | auto interPred = [f, &counter] ( const MeshIntersectionResult& res )->bool |
| 335 | { |
| 336 | if ( res.proj.face != f ) // TODO: we should also try grouping intersections, to ignore too close ones (by some epsilon), to filter several layered areas |
| 337 | ++counter; |
| 338 | return true; |
| 339 | }; |
| 340 | rayMeshIntersectAll( *targetMesh, Line3d( triCenter, normal ), interPred ); |
| 341 | bool pValid = counter % 2 == 0; |
| 342 | auto pCounter = counter; |
| 343 | bool nValid = true; |
| 344 | int nCounter = INT_MAX; |
| 345 | bool resValid = pValid; |
| 346 | if ( params.mode != FindDisorientationParams::RayMode::Positive ) |
| 347 | { |
| 348 | counter = 0; |
| 349 | rayMeshIntersectAll( *targetMesh, Line3d( triCenter, -normal ), interPred ); |
| 350 | nValid = counter % 2 == 1; |
| 351 | nCounter = counter - 1; // ideal face has 0-pCounter and 1-nCounter: so we decrement nCounter for fair compare |
| 352 | |
| 353 | resValid = pValid && nValid; |
| 354 | if ( params.mode == FindDisorientationParams::RayMode::Shallowest && pValid != nValid ) |
| 355 | { |
| 356 | if ( pCounter == nCounter ) |
| 357 | resValid = true; |
| 358 | else if ( nCounter < pCounter ) |
| 359 | resValid = nValid; |
nothing calls this directly
no test coverage detected