static triSurface pack
(
    const triSurface& surf,
    const pointField& localPoints,
    const labelList& pointMap
)
{
    List<labelledTri> newTriangles(surf.size());
    label newTriangleI = 0;

    forAll(surf, faceI)
    {
        const labelledTri& f = surf.localFaces()[faceI];

        label newA = pointMap[f[0]];
        label newB = pointMap[f[1]];
        label newC = pointMap[f[2]];

        if ((newA != newB) && (newA != newC) && (newB != newC))
        {
            newTriangles[newTriangleI++] =
                labelledTri(newA, newB, newC, f.region());
        }
    }
    newTriangles.setSize(newTriangleI);

    return triSurface(newTriangles, surf.patches(), localPoints);
}
//- Sets point neighbours of face to val
static void markPointNbrs
(
    const triSurface& surf,
    const label faceI,
    const bool val,
    boolList& okToCollapse
)
{
    const triSurface::FaceType& f = surf.localFaces()[faceI];

    forAll(f, fp)
    {
        const labelList& pFaces = surf.pointFaces()[f[fp]];

        forAll(pFaces, i)
        {
            okToCollapse[pFaces[i]] = false;
        }
    }
}
// Checks if there exists a special topological situation that causes
// edge and the face it hit not to be recognized.
//
// For now if the face shares a point with the edge
bool Foam::surfaceIntersection::excludeEdgeHit
(
    const triSurface& surf,
    const label edgeI,
    const label faceI,
    const scalar
)
{
    const labelledTri& f = surf.localFaces()[faceI];

    const edge& e = surf.edges()[edgeI];

    if
    (
        (f[0] == e.start())
     || (f[0] == e.end())
     || (f[1] == e.start())
     || (f[1] == e.end())
     || (f[2] == e.start())
     || (f[2] == e.end())
    )
    {
        return true;

//        // Get edge vector
//        vector eVec = e.vec(surf.localPoints());
//        eVec /= mag(eVec) + VSMALL;
//
//        const labelList& eLabels = surf.faceEdges()[faceI];
//
//        // Get edge vector of 0th edge of face
//        vector e0Vec = surf.edges()[eLabels[0]].vec(surf.localPoints());
//        e0Vec /= mag(e0Vec) + VSMALL;
//
//        vector n = e0Vec ^ eVec;
//
//        if (mag(n) < SMALL)
//        {
//            // e0 is aligned with e. Choose next edge of face.
//            vector e1Vec = surf.edges()[eLabels[1]].vec(surf.localPoints());
//            e1Vec /= mag(e1Vec) + VSMALL;
//
//            n = e1Vec ^ eVec;
//
//            if (mag(n) < SMALL)
//            {
//                // Problematic triangle. Two edges aligned with edgeI. Give
//                // up.
//                return true;
//            }
//        }
//
//        // Check if same as faceNormal
//        if (mag(n & surf.faceNormals()[faceI]) > 1-tol)
//        {
//
//            Pout<< "edge:" << e << "  face:" << faceI
//                << "  e0Vec:" << e0Vec << "  n:" << n
//                << "  normalComponent:" << (n & surf.faceNormals()[faceI])
//                << "  tol:" << tol << endl;
//
//            return true;
//        }
//        else
//        {
//            return false;
//        }
    }
    else
    {
        return false;
    }
}
// Collapses small edge to point, thus removing triangle.
label collapseEdge(triSurface& surf, const scalar minLen)
{
    label nTotalCollapsed = 0;

    while (true)
    {
        const pointField& localPoints = surf.localPoints();
        const List<labelledTri>& localFaces = surf.localFaces();


        // Mapping from old to new points
        labelList pointMap(surf.nPoints());
        forAll(pointMap, i)
        {
            pointMap[i] = i;
        }

        // Storage for new points.
        pointField newPoints(localPoints);

        // To protect neighbours of collapsed faces.
        boolList okToCollapse(surf.size(), true);
        label nCollapsed = 0;

        forAll(localFaces, faceI)
        {
            if (okToCollapse[faceI])
            {
                // Check edge lengths.
                const triSurface::FaceType& f = localFaces[faceI];

                forAll(f, fp)
                {
                    label v = f[fp];
                    label v1 = f[f.fcIndex(fp)];

                    if (mag(localPoints[v1] - localPoints[v]) < minLen)
                    {
                        // Collapse f[fp1] onto f[fp].
                        pointMap[v1] = v;
                        newPoints[v] = 0.5*(localPoints[v1] + localPoints[v]);

                        Pout<< "Collapsing triange " << faceI << " to edge mid "
                            << newPoints[v] << endl;

                        nCollapsed++;
                        okToCollapse[faceI] = false;

                        // Protect point neighbours from collapsing.
                        markPointNbrs(surf, faceI, false, okToCollapse);

                        break;
                    }
                }
            }
        }

        Pout<< "collapseEdge : collapsing " << nCollapsed << " triangles"
            << endl;

        nTotalCollapsed += nCollapsed;

        if (nCollapsed == 0)
        {
            break;
        }

        // Pack the triangles
        surf = pack(surf, newPoints, pointMap);
    }
Foam::labelList Foam::orientedSurface::edgeToFace
(
    const triSurface& s,
    const labelList& changedEdges,
    labelList& flip
)
{
    labelList changedFaces(2*changedEdges.size());
    label changedI = 0;

    // 1.6.x merge: using local faces.  Reconsider
    // Rewrite uses cached local faces for efficiency
    // HJ, 24/Aug/2010
    const List<labelledTri> lf =  s.localFaces();

    forAll(changedEdges, i)
    {
        label edgeI = changedEdges[i];

        const labelList& eFaces = s.edgeFaces()[edgeI];

        if (eFaces.size() < 2)
        {
            // Do nothing, faces was already visited.
        }
        else if (eFaces.size() == 2)
        {
            label face0 = eFaces[0];
            label face1 = eFaces[1];

            const labelledTri& f0 = lf[face0];
            const labelledTri& f1 = lf[face1];

            // Old.  HJ, 24/Aug/2010
//            const labelledTri& f0 = s[face0];
//            const labelledTri& f1 = s[face1];

            if (flip[face0] == UNVISITED)
            {
                if (flip[face1] == UNVISITED)
                {
                    FatalErrorIn("orientedSurface::edgeToFace") << "Problem"
                            << abort(FatalError);
                }
                else
                {
                    // Face1 has a flip state, face0 hasn't
                    if (consistentEdge(s.edges()[edgeI], f0, f1))
                    {
                        // Take over flip status
                        flip[face0] = (flip[face1] == FLIP ? FLIP : NOFLIP);
                    }
                    else
                    {
                        // Invert
                        flip[face0] = (flip[face1] == FLIP ? NOFLIP : FLIP);
                    }
                    changedFaces[changedI++] = face0;
                }
            }
            else
            {
                if (flip[face1] == UNVISITED)
                {
                    // Face0 has a flip state, face1 hasn't
                    if (consistentEdge(s.edges()[edgeI], f0, f1))
                    {
                        flip[face1] = (flip[face0] == FLIP ? FLIP : NOFLIP);
                    }
                    else
                    {
                        flip[face1] = (flip[face0] == FLIP ? NOFLIP : FLIP);
                    }
                    changedFaces[changedI++] = face1;
                }
            }
        }
        else
        {
            // Multiply connected. Do what?
        }
    }