| 537 | } |
| 538 | |
| 539 | auto MeshDecimator::canCollapse_( EdgeId edgeToCollapse, const Vector3f & collapsePos ) -> CanCollapseRes |
| 540 | { |
| 541 | const auto & topology = mesh_.topology; |
| 542 | auto vl = topology.left( edgeToCollapse ).valid() ? topology.dest( topology.next( edgeToCollapse ) ) : VertId{}; |
| 543 | auto vr = topology.right( edgeToCollapse ).valid() ? topology.dest( topology.prev( edgeToCollapse ) ) : VertId{}; |
| 544 | |
| 545 | // cannot collapse internal edge if its left and right faces share another edge |
| 546 | if ( vl && vr ) |
| 547 | { |
| 548 | bool oDegree2 = false; |
| 549 | if ( auto pe = topology.prev( edgeToCollapse ); pe != edgeToCollapse && pe == topology.next( edgeToCollapse ) ) |
| 550 | oDegree2 = true; // (pe) is shared between left and right faces of edgeToCollapse |
| 551 | |
| 552 | bool dDegree2 = false; |
| 553 | if ( auto pe = topology.prev( edgeToCollapse.sym() ); pe != edgeToCollapse.sym() && pe == topology.next( edgeToCollapse.sym() ) ) |
| 554 | dDegree2 = true; // (pe) is shared between left and right faces of edgeToCollapse |
| 555 | |
| 556 | // if both oDegree2 and dDegree2 are true, then vl == vr |
| 557 | assert( !oDegree2 || !dDegree2 || vl == vr ); |
| 558 | |
| 559 | // but can collapse if left and right faces of edgeToCollapse share all 3 edges |
| 560 | if ( oDegree2 != dDegree2 ) |
| 561 | return { .status = CollapseStatus::SharedEdge }; |
| 562 | } |
| 563 | const bool collapsingFlippable = !settings_.notFlippable || !settings_.notFlippable->test( edgeToCollapse ); |
| 564 | |
| 565 | auto vo = topology.org( edgeToCollapse ); |
| 566 | auto vd = topology.dest( edgeToCollapse ); |
| 567 | auto po = mesh_.points[vo]; |
| 568 | auto pd = mesh_.points[vd]; |
| 569 | if ( collapsePos == pd ) |
| 570 | { |
| 571 | // reverse the edge to have its origin in remaining fixed vertex |
| 572 | edgeToCollapse = edgeToCollapse.sym(); |
| 573 | std::swap( vo, vd ); |
| 574 | std::swap( vl, vr ); |
| 575 | std::swap( po, pd ); |
| 576 | } |
| 577 | |
| 578 | auto smallShift = [maxBdShiftSq = sqr( settings_.maxBdShift )]( const LineSegm3f & segm, const Vector3f & p ) |
| 579 | { |
| 580 | return ( closestPointOnLineSegm( p, segm ) - p ).lengthSq() <= maxBdShiftSq; |
| 581 | }; |
| 582 | if ( ( !vl || !vr ) && settings_.maxBdShift < FLT_MAX ) |
| 583 | { |
| 584 | if ( !smallShift( mesh_.edgeSegment( edgeToCollapse ), collapsePos ) ) |
| 585 | return { .status = CollapseStatus::PosFarBd }; // new vertex is too far from collapsing boundary edge |
| 586 | if ( !vr ) |
| 587 | { |
| 588 | if ( !smallShift( LineSegm3f{ mesh_.orgPnt( mesh_.topology.prevLeftBd( edgeToCollapse ) ), collapsePos }, po ) ) |
| 589 | return { .status = CollapseStatus::PosFarBd }; // origin of collapsing boundary edge is too far from new boundary segment |
| 590 | if ( !smallShift( LineSegm3f{ mesh_.destPnt( mesh_.topology.nextLeftBd( edgeToCollapse ) ), collapsePos }, pd ) ) |
| 591 | return { .status = CollapseStatus::PosFarBd }; // destination of collapsing boundary edge is too far from new boundary segment |
| 592 | } |
| 593 | if ( !vl ) |
| 594 | { |
| 595 | if ( !smallShift( LineSegm3f{ mesh_.orgPnt( mesh_.topology.prevLeftBd( edgeToCollapse.sym() ) ), collapsePos }, pd ) ) |
| 596 | return { .status = CollapseStatus::PosFarBd }; // destination of collapsing boundary edge is too far from new boundary segment |
nothing calls this directly
no test coverage detected