Skip to content
Snippets Groups Projects
triSurfaceTools.C 73.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*---------------------------------------------------------------------------*\
      =========                 |
      \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
       \\    /   O peration     |
    
        \\  /    A nd           | Copyright (C) 2011 OpenFOAM Foundation
    
         \\/     M anipulation  |
    -------------------------------------------------------------------------------
    License
        This file is part of OpenFOAM.
    
    
        OpenFOAM is free software: you can redistribute it and/or modify it
        under the terms of the GNU General Public License as published by
        the Free Software Foundation, either version 3 of the License, or
        (at your option) any later version.
    
    
        OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
        ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
        FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
        for more details.
    
        You should have received a copy of the GNU General Public License
    
        along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
    
    
    \*---------------------------------------------------------------------------*/
    
    #include "triSurfaceTools.H"
    
    #include "triSurface.H"
    #include "OFstream.H"
    #include "mergePoints.H"
    #include "polyMesh.H"
    #include "plane.H"
    #include "geompack.H"
    
    
    // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
    
    const Foam::label Foam::triSurfaceTools::ANYEDGE = -1;
    const Foam::label Foam::triSurfaceTools::NOEDGE = -2;
    const Foam::label Foam::triSurfaceTools::COLLAPSED = -3;
    
    // * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //
    
    /*
        Refine by splitting all three edges of triangle ('red' refinement).
        Neighbouring triangles (which are not marked for refinement get split
        in half ('green') refinement. (R. Verfuerth, "A review of a posteriori
        error estimation and adaptive mesh refinement techniques",
        Wiley-Teubner, 1996)
    */
    
    // FaceI gets refined ('red'). Propagate edge refinement.
    void Foam::triSurfaceTools::calcRefineStatus
    (
        const triSurface& surf,
        const label faceI,
        List<refineType>& refine
    )
    {
        if (refine[faceI] == RED)
        {
            // Already marked for refinement. Do nothing.
        }
        else
        {
            // Not marked or marked for 'green' refinement. Refine.
            refine[faceI] = RED;
    
            const labelList& myNeighbours = surf.faceFaces()[faceI];
    
            forAll(myNeighbours, myNeighbourI)
            {
                label neighbourFaceI = myNeighbours[myNeighbourI];
    
                if (refine[neighbourFaceI] == GREEN)
                {
                    // Change to red refinement and propagate
                    calcRefineStatus(surf, neighbourFaceI, refine);
                }
                else if (refine[neighbourFaceI] == NONE)
                {
                    refine[neighbourFaceI] = GREEN;
                }
            }
        }
    }
    
    
    // Split faceI along edgeI at position newPointI
    void Foam::triSurfaceTools::greenRefine
    (
        const triSurface& surf,
        const label faceI,
        const label edgeI,
        const label newPointI,
        DynamicList<labelledTri>& newFaces
    )
    {
        const labelledTri& f = surf.localFaces()[faceI];
        const edge& e = surf.edges()[edgeI];
    
        // Find index of edge in face.
    
        label fp0 = findIndex(f, e[0]);
        label fp1 = f.fcIndex(fp0);
        label fp2 = f.fcIndex(fp1);
    
        if (f[fp1] == e[1])
        {
            // Edge oriented like face
            newFaces.append
            (
                labelledTri
                (
                    f[fp0],
                    newPointI,
                    f[fp2],
                    f.region()
                )
            );
            newFaces.append
            (
                labelledTri
                (
                    newPointI,
                    f[fp1],
                    f[fp2],
                    f.region()
                )
            );
        }
        else
        {
            newFaces.append
            (
                labelledTri
                (
                    f[fp2],
                    newPointI,
                    f[fp1],
                    f.region()
                )
            );
            newFaces.append
            (
                labelledTri
                (
                    newPointI,
                    f[fp0],
                    f[fp1],
                    f.region()
                )
            );
        }
    }
    
    
    
    // Refine all triangles marked for refinement.
    
    Foam::triSurface Foam::triSurfaceTools::doRefine
    (
        const triSurface& surf,
        const List<refineType>& refineStatus
    )
    {
        // Storage for new points. (start after old points)
        DynamicList<point> newPoints(surf.nPoints());
        forAll(surf.localPoints(), pointI)
        {
            newPoints.append(surf.localPoints()[pointI]);
        }
        label newVertI = surf.nPoints();
    
        // Storage for new faces
        DynamicList<labelledTri> newFaces(surf.size());
    
    
        // Point index for midpoint on edge
        labelList edgeMid(surf.nEdges(), -1);
    
        forAll(refineStatus, faceI)
        {
            if (refineStatus[faceI] == RED)
            {
                // Create new vertices on all edges to be refined.
                const labelList& fEdges = surf.faceEdges()[faceI];
    
                forAll(fEdges, i)
                {
                    label edgeI = fEdges[i];
    
                    if (edgeMid[edgeI] == -1)
                    {
                        const edge& e = surf.edges()[edgeI];
    
                        // Create new point on mid of edge
                        newPoints.append
                        (
                            0.5
                          * (
                                surf.localPoints()[e.start()]
                              + surf.localPoints()[e.end()]
                            )
                        );
                        edgeMid[edgeI] = newVertI++;
                    }
                }
    
                // Now we have new mid edge vertices for all edges on face
                // so create triangles for RED rerfinement.
    
                const edgeList& edges = surf.edges();
    
                // Corner triangles
                newFaces.append
                (
                    labelledTri
                    (
                        edgeMid[fEdges[0]],
                        edges[fEdges[0]].commonVertex(edges[fEdges[1]]),
                        edgeMid[fEdges[1]],
                        surf[faceI].region()
                    )
                );
    
                newFaces.append
                (
                    labelledTri
                    (
                        edgeMid[fEdges[1]],
                        edges[fEdges[1]].commonVertex(edges[fEdges[2]]),
                        edgeMid[fEdges[2]],
                        surf[faceI].region()
                    )
                );
    
                newFaces.append
                (
                    labelledTri
                    (
                        edgeMid[fEdges[2]],
                        edges[fEdges[2]].commonVertex(edges[fEdges[0]]),
                        edgeMid[fEdges[0]],
                        surf[faceI].region()
                    )
                );
    
                // Inner triangle
                newFaces.append
                (
                    labelledTri
                    (
                        edgeMid[fEdges[0]],
                        edgeMid[fEdges[1]],
                        edgeMid[fEdges[2]],
                        surf[faceI].region()
                    )
                );
    
    
                // Create triangles for GREEN refinement.
                forAll(fEdges, i)
                {
                    const label edgeI = fEdges[i];
    
                    label otherFaceI = otherFace(surf, faceI, edgeI);
    
                    if ((otherFaceI != -1) && (refineStatus[otherFaceI] == GREEN))
                    {
                        greenRefine
                        (
                            surf,
                            otherFaceI,
                            edgeI,
                            edgeMid[edgeI],
                            newFaces
                        );
                    }
                }
            }
        }
    
        // Copy unmarked triangles since keep original vertex numbering.
        forAll(refineStatus, faceI)
        {
            if (refineStatus[faceI] == NONE)
            {
                newFaces.append(surf.localFaces()[faceI]);
            }
        }
    
        newFaces.shrink();
        newPoints.shrink();
    
    
        // Transfer DynamicLists to straight ones.
        pointField allPoints;
        allPoints.transfer(newPoints);
        newPoints.clear();
    
    
    mattijs's avatar
    mattijs committed
        return triSurface(newFaces, surf.patches(), allPoints, true);
    
    }
    
    
    // Angle between two neighbouring triangles,
    // angle between shared-edge end points and left and right face end points
    Foam::scalar Foam::triSurfaceTools::faceCosAngle
    (
        const point& pStart,
        const point& pEnd,
        const point& pLeft,
        const point& pRight
    )
    {
        const vector common(pEnd - pStart);
        const vector base0(pLeft - pStart);
        const vector base1(pRight - pStart);
    
        vector n0(common ^ base0);
        n0 /= Foam::mag(n0);
    
        vector n1(base1 ^ common);
        n1 /= Foam::mag(n1);
    
        return n0 & n1;
    }
    
    
    // Protect edges around vertex from collapsing.
    // Moving vertI to a different position
    // - affects obviously the cost of the faces using it
    // - but also their neighbours since the edge between the faces changes
    void Foam::triSurfaceTools::protectNeighbours
    (
        const triSurface& surf,
        const label vertI,
        labelList& faceStatus
    )
    {
    //    const labelList& myFaces = surf.pointFaces()[vertI];
    //    forAll(myFaces, i)
    //    {
    //        label faceI = myFaces[i];
    //
    //        if ((faceStatus[faceI] == ANYEDGE) || (faceStatus[faceI] >= 0))
    //        {
    //            faceStatus[faceI] = NOEDGE;
    //        }
    //    }
    
        const labelList& myEdges = surf.pointEdges()[vertI];
        forAll(myEdges, i)
        {
            const labelList& myFaces = surf.edgeFaces()[myEdges[i]];
    
            forAll(myFaces, myFaceI)
            {
                label faceI = myFaces[myFaceI];
    
                if ((faceStatus[faceI] == ANYEDGE) || (faceStatus[faceI] >= 0))
                {
                    faceStatus[faceI] = NOEDGE;
                }
           }
        }
    }
    
    
    //
    // Edge collapse helper functions
    //
    
    // Get all faces that will get collapsed if edgeI collapses.
    Foam::labelHashSet Foam::triSurfaceTools::getCollapsedFaces
    (
        const triSurface& surf,
        label edgeI
    )
    {
        const edge& e = surf.edges()[edgeI];
        label v1 = e.start();
        label v2 = e.end();
    
        // Faces using edge will certainly get collapsed.
        const labelList& myFaces = surf.edgeFaces()[edgeI];
    
        labelHashSet facesToBeCollapsed(2*myFaces.size());
    
        forAll(myFaces, myFaceI)
        {
            facesToBeCollapsed.insert(myFaces[myFaceI]);
        }
    
        // From faces using v1 check if they share an edge with faces
        // using v2.
    
        //  - share edge: are part of 'splay' tree and will collapse if edge
    
    396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
        //    collapses
        const labelList& v1Faces = surf.pointFaces()[v1];
    
        forAll(v1Faces, v1FaceI)
        {
            label face1I = v1Faces[v1FaceI];
    
            label otherEdgeI = oppositeEdge(surf, face1I, v1);
    
            // Step across edge to other face
            label face2I = otherFace(surf, face1I, otherEdgeI);
    
            if (face2I != -1)
            {
                // found face on other side of edge. Now check if uses v2.
                if (oppositeVertex(surf, face2I, otherEdgeI) == v2)
                {
                    // triangles face1I and face2I form splay tree and will
                    // collapse.
                    facesToBeCollapsed.insert(face1I);
                    facesToBeCollapsed.insert(face2I);
                }
            }
        }
    
        return facesToBeCollapsed;
    }
    
    
    // Return value of faceUsed for faces using vertI
    Foam::label Foam::triSurfaceTools::vertexUsesFace
    (
        const triSurface& surf,
        const labelHashSet& faceUsed,
        const label vertI
    )
    {
        const labelList& myFaces = surf.pointFaces()[vertI];
    
        forAll(myFaces, myFaceI)
        {
            label face1I = myFaces[myFaceI];
    
            if (faceUsed.found(face1I))
            {
                return face1I;
            }
        }
        return -1;
    }
    
    
    // Calculate new edge-face addressing resulting from edge collapse
    void Foam::triSurfaceTools::getMergedEdges
    (
        const triSurface& surf,
        const label edgeI,
        const labelHashSet& collapsedFaces,
        HashTable<label, label, Hash<label> >& edgeToEdge,
        HashTable<label, label, Hash<label> >& edgeToFace
    )
    {
        const edge& e = surf.edges()[edgeI];
        label v1 = e.start();
        label v2 = e.end();
    
        const labelList& v1Faces = surf.pointFaces()[v1];
        const labelList& v2Faces = surf.pointFaces()[v2];
    
        // Mark all (non collapsed) faces using v2
        labelHashSet v2FacesHash(v2Faces.size());
    
        forAll(v2Faces, v2FaceI)
        {
            if (!collapsedFaces.found(v2Faces[v2FaceI]))
            {
                v2FacesHash.insert(v2Faces[v2FaceI]);
            }
        }
    
    
        forAll(v1Faces, v1FaceI)
        {
            label face1I = v1Faces[v1FaceI];
    
            if (collapsedFaces.found(face1I))
            {
                continue;
            }
    
            //
            // Check if face1I uses a vertex connected to a face using v2
            //
    
            label vert1I = -1;
            label vert2I = -1;
            otherVertices
            (
                surf,
                face1I,
                v1,
                vert1I,
                vert2I
            );
            //Pout<< "Face:" << surf.localFaces()[face1I] << " other vertices:"
            //    << vert1I << ' ' << vert2I << endl;
    
            // Check vert1, vert2 for usage by v2Face.
    
            label commonVert = vert1I;
            label face2I = vertexUsesFace(surf, v2FacesHash, commonVert);
            if (face2I == -1)
            {
                commonVert = vert2I;
                face2I = vertexUsesFace(surf, v2FacesHash, commonVert);
            }
    
            if (face2I != -1)
            {
                // Found one: commonVert is used by both face1I and face2I
                label edge1I = getEdge(surf, v1, commonVert);
                label edge2I = getEdge(surf, v2, commonVert);
    
                edgeToEdge.insert(edge1I, edge2I);
                edgeToEdge.insert(edge2I, edge1I);
    
                edgeToFace.insert(edge1I, face2I);
                edgeToFace.insert(edge2I, face1I);
            }
        }
    }
    
    
    // Calculates (cos of) angle across edgeI of faceI,
    // taking into account updated addressing (resulting from edge collapse)
    Foam::scalar Foam::triSurfaceTools::edgeCosAngle
    (
        const triSurface& surf,
        const label v1,
        const point& pt,
        const labelHashSet& collapsedFaces,
        const HashTable<label, label, Hash<label> >& edgeToEdge,
        const HashTable<label, label, Hash<label> >& edgeToFace,
        const label faceI,
        const label edgeI
    )
    {
        const pointField& localPoints = surf.localPoints();
    
        label A = surf.edges()[edgeI].start();
        label B = surf.edges()[edgeI].end();
        label C = oppositeVertex(surf, faceI, edgeI);
    
        label D = -1;
    
        label face2I = -1;
    
        if (edgeToEdge.found(edgeI))
        {
            // Use merged addressing
            label edge2I = edgeToEdge[edgeI];
            face2I = edgeToFace[edgeI];
    
            D = oppositeVertex(surf, face2I, edge2I);
        }
        else
        {
            // Use normal edge-face addressing
            face2I = otherFace(surf, faceI, edgeI);
    
            if ((face2I != -1) && !collapsedFaces.found(face2I))
            {
                D = oppositeVertex(surf, face2I, edgeI);
            }
        }
    
        scalar cosAngle = 1;
    
        if (D != -1)
        {
            if (A == v1)
            {
                cosAngle = faceCosAngle
                (
                    pt,
                    localPoints[B],
                    localPoints[C],
                    localPoints[D]
                );
            }
            else if (B == v1)
            {
                cosAngle = faceCosAngle
                (
                    localPoints[A],
                    pt,
                    localPoints[C],
                    localPoints[D]
                );
            }
            else if (C == v1)
            {
                cosAngle = faceCosAngle
                (
                    localPoints[A],
                    localPoints[B],
                    pt,
                    localPoints[D]
                );
            }
            else if (D == v1)
            {
                cosAngle = faceCosAngle
                (
                    localPoints[A],
                    localPoints[B],
                    localPoints[C],
                    pt
                );
            }
            else
            {
                FatalErrorIn("edgeCosAngle")
                    << "face " << faceI << " does not use vertex "
                    << v1 << " of collapsed edge" << abort(FatalError);
            }
        }
        return cosAngle;
    }
    
    
    //- Calculate minimum (cos of) edge angle using addressing from collapsing
    //  edge to v1 at pt.
    Foam::scalar Foam::triSurfaceTools::collapseMinCosAngle
    (
        const triSurface& surf,
        const label v1,
        const point& pt,
        const labelHashSet& collapsedFaces,
        const HashTable<label, label, Hash<label> >& edgeToEdge,
        const HashTable<label, label, Hash<label> >& edgeToFace
    )
    {
        const labelList& v1Faces = surf.pointFaces()[v1];
    
        scalar minCos = 1;
    
        forAll(v1Faces, v1FaceI)
        {
            label faceI = v1Faces[v1FaceI];
    
            if (collapsedFaces.found(faceI))
            {
                continue;
            }
    
            const labelList& myEdges = surf.faceEdges()[faceI];
    
            forAll(myEdges, myEdgeI)
            {
                label edgeI = myEdges[myEdgeI];
    
                minCos =
                    min
                    (
                        minCos,
                        edgeCosAngle
                        (
                            surf,
                            v1,
                            pt,
                            collapsedFaces,
                            edgeToEdge,
                            edgeToFace,
                            faceI,
                            edgeI
                        )
                    );
            }
        }
    
        return minCos;
    }
    
    
    // Calculate max dihedral angle after collapsing edge to v1 (at pt).
    // Note that all edges of all faces using v1 are affected.
    bool Foam::triSurfaceTools::collapseCreatesFold
    (
        const triSurface& surf,
        const label v1,
        const point& pt,
        const labelHashSet& collapsedFaces,
        const HashTable<label, label, Hash<label> >& edgeToEdge,
        const HashTable<label, label, Hash<label> >& edgeToFace,
        const scalar minCos
    )
    {
        const labelList& v1Faces = surf.pointFaces()[v1];
    
        forAll(v1Faces, v1FaceI)
        {
            label faceI = v1Faces[v1FaceI];
    
            if (collapsedFaces.found(faceI))
            {
                continue;
            }
    
            const labelList& myEdges = surf.faceEdges()[faceI];
    
            forAll(myEdges, myEdgeI)
            {
                label edgeI = myEdges[myEdgeI];
    
                if
                (
                    edgeCosAngle
                    (
                        surf,
                        v1,
                        pt,
                        collapsedFaces,
                        edgeToEdge,
                        edgeToFace,
                        faceI,
                        edgeI
                    )
                  < minCos
                )
                {
                    return true;
                }
            }
        }
    
        return false;
    }
    
    
    //// Return true if collapsing edgeI creates triangles on top of each other.
    //// Is when the triangles neighbouring the collapsed one already share
    // a vertex.
    //bool Foam::triSurfaceTools::collapseCreatesDuplicates
    //(
    //    const triSurface& surf,
    //    const label edgeI,
    //    const labelHashSet& collapsedFaces
    //)
    //{
    //Pout<< "duplicate : edgeI:" << edgeI << surf.edges()[edgeI]
    //    << " collapsedFaces:" << collapsedFaces.toc() << endl;
    //
    //    // Mark neighbours of faces to be collapsed, i.e. get the first layer
    //    // of triangles outside collapsedFaces.
    //    // neighbours actually contains the
    //    // edge with which triangle connects to collapsedFaces.
    //
    //    HashTable<label, label, Hash<label> > neighbours;
    //
    //    labelList collapsed = collapsedFaces.toc();
    //
    //    forAll(collapsed, collapseI)
    //    {
    //        const label faceI = collapsed[collapseI];
    //
    //        const labelList& myEdges = surf.faceEdges()[faceI];
    //
    //        Pout<< "collapsing faceI:" << faceI << " uses edges:" << myEdges
    //            << endl;
    //
    //        forAll(myEdges, myEdgeI)
    //        {
    //            const labelList& myFaces = surf.edgeFaces()[myEdges[myEdgeI]];
    //
    //            Pout<< "Edge " << myEdges[myEdgeI] << " is used by faces "
    //                << myFaces << endl;
    //
    //            if ((myEdges[myEdgeI] != edgeI) && (myFaces.size() == 2))
    //            {
    //                // Get the other face
    //                label neighbourFaceI = myFaces[0];
    //
    //                if (neighbourFaceI == faceI)
    //                {
    //                    neighbourFaceI = myFaces[1];
    //                }
    //
    //                // Is 'outside' face if not in collapsedFaces itself
    //                if (!collapsedFaces.found(neighbourFaceI))
    //                {
    //                    neighbours.insert(neighbourFaceI, myEdges[myEdgeI]);
    //                }
    //            }
    //        }
    //    }
    //
    //    // Now neighbours contains first layer of triangles outside of
    //    // collapseFaces
    
    //    // -two if edgeI is a boundary edge
    //    // since the outside 'edge' of collapseFaces should
    //    // form a triangle and the face connected to edgeI is not inserted.
    //    // -four if edgeI is not a boundary edge since then the outside edge forms
    //    // a diamond.
    //
    //    // Check if any of the faces in neighbours share an edge. (n^2)
    //
    //    labelList neighbourList = neighbours.toc();
    //
    //Pout<< "edgeI:" << edgeI << "  neighbourList:" << neighbourList << endl;
    //
    //
    //    forAll(neighbourList, i)
    //    {
    //        const labelList& faceIEdges = surf.faceEdges()[neighbourList[i]];
    //
    //        for (label j = i+1; j < neighbourList.size(); i++)
    //        {
    //            const labelList& faceJEdges = surf.faceEdges()[neighbourList[j]];
    
    //            // Check if faceI and faceJ share an edge
    //            forAll(faceIEdges, fI)
    //            {
    //                forAll(faceJEdges, fJ)
    //                {
    //                    Pout<< " comparing " << faceIEdges[fI] << " to "
    //                        << faceJEdges[fJ] << endl;
    //
    //                    if (faceIEdges[fI] == faceJEdges[fJ])
    //                    {
    //                        return true;
    //                    }
    //                }
    //            }
    //        }
    //    }
    //    Pout<< "Found no match. Returning false" << endl;
    //    return false;
    //}
    
    
    // Finds the triangle edge cut by the plane between a point inside / on edge
    // of a triangle and a point outside. Returns:
    //  - cut through edge/point
    //  - miss()
    Foam::surfaceLocation Foam::triSurfaceTools::cutEdge
    (
        const triSurface& s,
        const label triI,
        const label excludeEdgeI,
        const label excludePointI,
    
        const point& triPoint,
        const plane& cutPlane,
        const point& toPoint
    )
    {
        const pointField& points = s.points();
        const labelledTri& f = s[triI];
        const labelList& fEdges = s.faceEdges()[triI];
    
        // Get normal distance to planeN
        FixedList<scalar, 3> d;
    
        scalar norm = 0;
        forAll(d, fp)
        {
            d[fp] = (points[f[fp]]-cutPlane.refPoint()) & cutPlane.normal();
            norm += mag(d[fp]);
        }
    
        // Normalise and truncate
        forAll(d, i)
        {
            d[i] /= norm;
    
            if (mag(d[i]) < 1E-6)
            {
                d[i] = 0.0;
            }
        }
    
        // Return information
        surfaceLocation cut;
    
        if (excludePointI != -1)
        {
            // Excluded point. Test only opposite edge.
    
            label fp0 = findIndex(s.localFaces()[triI], excludePointI);
    
            if (fp0 == -1)
            {
                FatalErrorIn("cutEdge(..)") << "excludePointI:" << excludePointI
                    << " localF:" << s.localFaces()[triI] << abort(FatalError);
            }
    
            label fp1 = f.fcIndex(fp0);
            label fp2 = f.fcIndex(fp1);
    
    
            if (d[fp1] == 0.0)
            {
                cut.setHit();
                cut.setPoint(points[f[fp1]]);
                cut.elementType() = triPointRef::POINT;
                cut.setIndex(s.localFaces()[triI][fp1]);
            }
            else if (d[fp2] == 0.0)
            {
                cut.setHit();
                cut.setPoint(points[f[fp2]]);
                cut.elementType() = triPointRef::POINT;
                cut.setIndex(s.localFaces()[triI][fp2]);
            }
            else if
            (
                (d[fp1] < 0 && d[fp2] < 0)
             || (d[fp1] > 0 && d[fp2] > 0)
            )
            {
                // Both same sign. Not crossing edge at all.
                // cut already set to miss().
            }
            else
            {
                cut.setHit();
                cut.setPoint
                (
                    (d[fp2]*points[f[fp1]] - d[fp1]*points[f[fp2]])
                  / (d[fp2] - d[fp1])
                );
                cut.elementType() = triPointRef::EDGE;
                cut.setIndex(fEdges[fp1]);
            }
        }
        else
        {
            // Find the two intersections
            FixedList<surfaceLocation, 2> inters;
            label interI = 0;
    
            forAll(f, fp0)
            {
                label fp1 = f.fcIndex(fp0);
    
                if (d[fp0] == 0)
                {
                    if (interI >= 2)
                    {
                        FatalErrorIn("cutEdge(..)")
                            << "problem : triangle has three intersections." << nl
                            << "triangle:" << f.tri(points)
                            << " d:" << d << abort(FatalError);
                    }
                    inters[interI].setHit();
                    inters[interI].setPoint(points[f[fp0]]);
                    inters[interI].elementType() = triPointRef::POINT;
                    inters[interI].setIndex(s.localFaces()[triI][fp0]);
                    interI++;
                }
                else if
                (
                    (d[fp0] < 0 && d[fp1] > 0)
                 || (d[fp0] > 0 && d[fp1] < 0)
                )
                {
                    if (interI >= 2)
                    {
                        FatalErrorIn("cutEdge(..)")
                            << "problem : triangle has three intersections." << nl
                            << "triangle:" << f.tri(points)
                            << " d:" << d << abort(FatalError);
                    }
                    inters[interI].setHit();
                    inters[interI].setPoint
                    (
                        (d[fp0]*points[f[fp1]] - d[fp1]*points[f[fp0]])
                      / (d[fp0] - d[fp1])
                    );
                    inters[interI].elementType() = triPointRef::EDGE;
                    inters[interI].setIndex(fEdges[fp0]);
                    interI++;
                }
            }
    
    
            if (interI == 0)
            {
                // Return miss
            }
            else if (interI == 1)
            {
                // Only one intersection. Should not happen!
                cut = inters[0];
            }
            else if (interI == 2)
            {
                // Handle excludeEdgeI
                if
                (
                    inters[0].elementType() == triPointRef::EDGE
                 && inters[0].index() == excludeEdgeI
                )