Foam::vector Foam::featurePointConformer::sharedFaceNormal
(
    const extendedFeatureEdgeMesh& feMesh,
    const label edgeI,
    const label nextEdgeI
) const
{
    const labelList& edgeInormals = feMesh.edgeNormals()[edgeI];
    const labelList& nextEdgeInormals = feMesh.edgeNormals()[nextEdgeI];

    const vector& A1 = feMesh.normals()[edgeInormals[0]];
    const vector& A2 = feMesh.normals()[edgeInormals[1]];

    const vector& B1 = feMesh.normals()[nextEdgeInormals[0]];
    const vector& B2 = feMesh.normals()[nextEdgeInormals[1]];

//    Info<< "        A1 = " << A1 << endl;
//    Info<< "        A2 = " << A2 << endl;
//    Info<< "        B1 = " << B1 << endl;
//    Info<< "        B2 = " << B2 << endl;

    const scalar A1B1 = mag((A1 & B1) - 1.0);
    const scalar A1B2 = mag((A1 & B2) - 1.0);
    const scalar A2B1 = mag((A2 & B1) - 1.0);
    const scalar A2B2 = mag((A2 & B2) - 1.0);

//    Info<< "      A1B1 = " << A1B1 << endl;
//    Info<< "      A1B2 = " << A1B2 << endl;
//    Info<< "      A2B1 = " << A2B1 << endl;
//    Info<< "      A2B2 = " << A2B2 << endl;

    if (A1B1 < A1B2 && A1B1 < A2B1 && A1B1 < A2B2)
    {
        return 0.5*(A1 + B1);
    }
    else if (A1B2 < A1B1 && A1B2 < A2B1 && A1B2 < A2B2)
    {
        return 0.5*(A1 + B2);
    }
    else if (A2B1 < A1B1 && A2B1 < A1B2 && A2B1 < A2B2)
    {
        return 0.5*(A2 + B1);
    }
    else
    {
        return 0.5*(A2 + B2);
    }
}
bool Foam::featurePointConformer::createSpecialisedFeaturePoint
(
    const extendedFeatureEdgeMesh& feMesh,
    const labelList& pEds,
    const pointFeatureEdgesTypes& pFEdgesTypes,
    const List<extendedFeatureEdgeMesh::edgeStatus>& allEdStat,
    const label ptI,
    DynamicList<Vb>& pts
) const
{
    if
    (
        !pFEdgesTypes.found(extendedFeatureEdgeMesh::EXTERNAL)
     || !pFEdgesTypes.found(extendedFeatureEdgeMesh::INTERNAL)
    )
    {
        return false;
    }

    if
    (
        pFEdgesTypes[extendedFeatureEdgeMesh::EXTERNAL] == 2
     && pFEdgesTypes[extendedFeatureEdgeMesh::INTERNAL] == 1
     && pEds.size() == 3
    )
    {
        if (debug) Info<< "nExternal == 2 && nInternal == 1" << endl;

        const Foam::point& featPt = feMesh.points()[ptI];

        if
        (
            Pstream::parRun()
         && !foamyHexMesh_.decomposition().positionOnThisProcessor(featPt)
        )
        {
            return false;
        }

        label nVert = foamyHexMesh_.number_of_vertices();

        const label initialNumOfPoints = pts.size();

        const scalar ppDist = foamyHexMesh_.pointPairDistance(featPt);

        const vectorField& normals = feMesh.normals();

        const labelListList& edgeNormals = feMesh.edgeNormals();

        label concaveEdgeI = -1;
        labelList convexEdgesI(2, label(-1));
        label nConvex = 0;

        forAll(pEds, i)
        {
            const extendedFeatureEdgeMesh::edgeStatus& eS = allEdStat[i];

            if (eS == extendedFeatureEdgeMesh::INTERNAL)
            {
                concaveEdgeI = pEds[i];
            }
            else if (eS == extendedFeatureEdgeMesh::EXTERNAL)
            {
                convexEdgesI[nConvex++] = pEds[i];
            }
            else if (eS == extendedFeatureEdgeMesh::FLAT)
            {
                WarningIn("Foam::conformalVoronoiMesh::"
                    "createSpecialisedFeaturePoint")
                    << "Edge " << eS << " is flat"
                    << endl;
            }
            else
            {
                FatalErrorIn("Foam::conformalVoronoiMesh::"
                    "createSpecialisedFeaturePoint")
                    << "Edge " << eS << " not concave/convex"
                    << exit(FatalError);
            }
        }

        const vector& concaveEdgePlaneANormal =
            normals[edgeNormals[concaveEdgeI][0]];

        const vector& concaveEdgePlaneBNormal =
            normals[edgeNormals[concaveEdgeI][1]];

        // Intersect planes parallel to the concave edge planes offset
        // by ppDist and the plane defined by featPt and the edge vector.
        plane planeA
        (
            featPt + ppDist*concaveEdgePlaneANormal,
            concaveEdgePlaneANormal
        );

        plane planeB
        (
            featPt + ppDist*concaveEdgePlaneBNormal,
            concaveEdgePlaneBNormal
        );

        const vector& concaveEdgeDir = feMesh.edgeDirection
        (
            concaveEdgeI,
            ptI
        );

        // Todo,needed later but want to get rid of this.
        const Foam::point concaveEdgeLocalFeatPt =
            featPt + ppDist*concaveEdgeDir;

        // Finding the nearest point on the intersecting line to the edge
        // point. Floating point errors often occur using planePlaneIntersect

        plane planeF(concaveEdgeLocalFeatPt, concaveEdgeDir);

        const Foam::point concaveEdgeExternalPt = planeF.planePlaneIntersect
        (
            planeA,
            planeB
        );

        // Redefine planes to be on the feature surfaces to project through

        planeA = plane(featPt, concaveEdgePlaneANormal);

        planeB = plane(featPt, concaveEdgePlaneBNormal);

        const Foam::point internalPtA =
            concaveEdgeExternalPt
          - 2.0*planeA.distance(concaveEdgeExternalPt)
            *concaveEdgePlaneANormal;

        pts.append
        (
            Vb
            (
                internalPtA,
                foamyHexMesh_.vertexCount() + pts.size(),
                Vb::vtInternalFeaturePoint,
                Pstream::myProcNo()
            )
        );

        const label internalPtAIndex(pts.last().index());

        const Foam::point internalPtB =
            concaveEdgeExternalPt
          - 2.0*planeB.distance(concaveEdgeExternalPt)
            *concaveEdgePlaneBNormal;

        pts.append
        (
            Vb
            (
                internalPtB,
                foamyHexMesh_.vertexCount() + pts.size(),
                Vb::vtInternalFeaturePoint,
                Pstream::myProcNo()
            )
        );

        const label internalPtBIndex(pts.last().index());

        // Add the external points

        Foam::point externalPtD;
        Foam::point externalPtE;

        vector convexEdgePlaneCNormal(vector::zero);
        vector convexEdgePlaneDNormal(vector::zero);

        const labelList& concaveEdgeNormals = edgeNormals[concaveEdgeI];
        const labelList& convexEdgeANormals = edgeNormals[convexEdgesI[0]];
        const labelList& convexEdgeBNormals = edgeNormals[convexEdgesI[1]];

        forAll(concaveEdgeNormals, edgeNormalI)
        {
            bool convexEdgeA = false;
            bool convexEdgeB = false;

            forAll(convexEdgeANormals, edgeAnormalI)
            {
                const vector& concaveNormal
                    = normals[concaveEdgeNormals[edgeNormalI]];
                const vector& convexNormal
                    = normals[convexEdgeANormals[edgeAnormalI]];

                if (debug)
                {
                    Info<< "Angle between vectors = "
                        << degAngleBetween(concaveNormal, convexNormal) << endl;
                }

                // Need a looser tolerance, because sometimes adjacent triangles
                // on the same surface will be slightly out of alignment.
                if (areParallel(concaveNormal, convexNormal, tolParallel))
                {
                    convexEdgeA = true;
                }
            }

            forAll(convexEdgeBNormals, edgeBnormalI)
            {
                const vector& concaveNormal
                    = normals[concaveEdgeNormals[edgeNormalI]];
                const vector& convexNormal
                    = normals[convexEdgeBNormals[edgeBnormalI]];

                if (debug)
                {
                    Info<< "Angle between vectors = "
                        << degAngleBetween(concaveNormal, convexNormal) << endl;
                }

                // Need a looser tolerance, because sometimes adjacent triangles
                // on the same surface will be slightly out of alignment.
                if (areParallel(concaveNormal, convexNormal, tolParallel))
                {
                    convexEdgeB = true;
                }
            }

            if ((convexEdgeA && convexEdgeB) || (!convexEdgeA && !convexEdgeB))
            {
                WarningIn
                    (
                     "Foam::conformalVoronoiMesh"
                     "::createSpecialisedFeaturePoint"
                    )
                    << "Both or neither of the convex edges share the concave "
                    << "edge's normal."
                    << " convexEdgeA = " << convexEdgeA
                    << " convexEdgeB = " << convexEdgeB
                    << endl;

                // Remove points that have just been added before returning
                for (label i = 0; i < 2; ++i)
                {
                    pts.remove();
                    nVert--;
                }

                return false;
            }

            if (convexEdgeA)
            {
                forAll(convexEdgeANormals, edgeAnormalI)
                {
                    const vector& concaveNormal
                        = normals[concaveEdgeNormals[edgeNormalI]];
                    const vector& convexNormal
                        = normals[convexEdgeANormals[edgeAnormalI]];

                    if
                    (
                        !areParallel(concaveNormal, convexNormal, tolParallel)
                    )
                    {
                        convexEdgePlaneCNormal = convexNormal;

                        plane planeC(featPt, convexEdgePlaneCNormal);

                        externalPtD =
                            internalPtA
                          + 2.0*planeC.distance(internalPtA)
                           *convexEdgePlaneCNormal;

                        pts.append
                        (
                            Vb
                            (
                                externalPtD,
                                foamyHexMesh_.vertexCount() + pts.size(),
                                Vb::vtExternalFeaturePoint,
                                Pstream::myProcNo()
                            )
                        );

                        ftPtPairs_.addPointPair
                        (
                            internalPtAIndex,
                            pts.last().index()
                        );
                    }
                }
            }

            if (convexEdgeB)
            {
                forAll(convexEdgeBNormals, edgeBnormalI)
                {
                    const vector& concaveNormal
                        = normals[concaveEdgeNormals[edgeNormalI]];
                    const vector& convexNormal
                        = normals[convexEdgeBNormals[edgeBnormalI]];

                    if
                    (
                        !areParallel(concaveNormal, convexNormal, tolParallel)
                    )
                    {
                        convexEdgePlaneDNormal = convexNormal;

                        plane planeD(featPt, convexEdgePlaneDNormal);

                        externalPtE =
                            internalPtB
                          + 2.0*planeD.distance(internalPtB)
                           *convexEdgePlaneDNormal;

                        pts.append
                        (
                            Vb
                            (
                                externalPtE,
                                foamyHexMesh_.vertexCount() + pts.size(),
                                Vb::vtExternalFeaturePoint,
                                Pstream::myProcNo()
                            )
                        );

                        ftPtPairs_.addPointPair
                        (
                            internalPtBIndex,
                            pts.last().index()
                        );
                    }
                }
            }
        }

        pts.append
        (
            Vb
            (
                concaveEdgeExternalPt,
                foamyHexMesh_.vertexCount() + pts.size(),
                Vb::vtExternalFeaturePoint,
                Pstream::myProcNo()
            )
        );

        ftPtPairs_.addPointPair
        (
            internalPtBIndex,
            pts.last().index()
        );

        ftPtPairs_.addPointPair
        (
            internalPtAIndex,
            pts.last().index()
        );

        const label concaveEdgeExternalPtIndex(pts.last().index());

        const scalar totalAngle = radToDeg
        (
            constant::mathematical::pi
          + radAngleBetween(concaveEdgePlaneANormal, concaveEdgePlaneBNormal)
        );

        if (totalAngle > foamyHexMeshControls_.maxQuadAngle())
        {
            // Add additional mitreing points
            //scalar angleSign = 1.0;


            vector convexEdgesPlaneNormal =
                0.5*(convexEdgePlaneCNormal + convexEdgePlaneDNormal);

            plane planeM(featPt, convexEdgesPlaneNormal);

//            if
//            (
//                geometryToConformTo_.outside
//                (
//                    featPt - convexEdgesPlaneNormal*ppDist
//                )
//            )
//            {
//                angleSign = -1.0;
//            }

//            scalar phi =
//                angleSign*acos(concaveEdgeDir & -convexEdgesPlaneNormal);
//
//            scalar guard =
//            (
//                1.0 + sin(phi)*ppDist/mag
//                (
//                    concaveEdgeLocalFeatPt - concaveEdgeExternalPt
//                )
//            )/cos(phi) - 1.0;

            const Foam::point internalPtF =
                concaveEdgeExternalPt
            //+ (2.0 + guard)*(concaveEdgeLocalFeatPt - concaveEdgeExternalPt);
              + 2.0*(concaveEdgeLocalFeatPt - concaveEdgeExternalPt);

            pts.append
            (
                Vb
                (
                    internalPtF,
                    foamyHexMesh_.vertexCount() + pts.size(),
                    Vb::vtInternalFeaturePoint,
                    Pstream::myProcNo()
                )
            );

            const label internalPtFIndex(pts.last().index());

            ftPtPairs_.addPointPair
            (
                concaveEdgeExternalPtIndex,
                pts.last().index()
            );

            const Foam::point externalPtG =
                internalPtF
              + 2.0*planeM.distance(internalPtF)*convexEdgesPlaneNormal;

            pts.append
            (
                Vb
                (
                    externalPtG,
                    foamyHexMesh_.vertexCount() + pts.size(),
                    Vb::vtExternalFeaturePoint,
                    Pstream::myProcNo()
                )
            );

            ftPtPairs_.addPointPair
            (
                internalPtFIndex,
                pts.last().index()
            );
        }

        if (debug)
        {
            for (label ptI = initialNumOfPoints; ptI < pts.size(); ++ptI)
            {
                Info<< "Point " << ptI << " : ";
                meshTools::writeOBJ(Info, topoint(pts[ptI].point()));
            }
        }

        return true;
    }
void Foam::featurePointConformer::createMasterAndSlavePoints
(
    const extendedFeatureEdgeMesh& feMesh,
    const label ptI,
    DynamicList<Vb>& pts
) const
{
    typedef DynamicList<autoPtr<plane> >          planeDynList;
    typedef indexedVertexEnum::vertexType         vertexType;
    typedef extendedFeatureEdgeMesh::edgeStatus   edgeStatus;

    const Foam::point& featPt = feMesh.points()[ptI];

    if
    (
        (
            Pstream::parRun()
         && !foamyHexMesh_.decomposition().positionOnThisProcessor(featPt)
        )
     || geometryToConformTo_.outside(featPt)
    )
    {
        return;
    }

    const scalar ppDist = foamyHexMesh_.pointPairDistance(featPt);

    // Maintain a list of master points and the planes to relect them in
    DynamicList<Foam::point> masterPoints;
    DynamicList<vertexType> masterPointsTypes;
    Map<planeDynList> masterPointReflections;

    const labelList& featPtEdges = feMesh.featurePointEdges()[ptI];

    pointFeatureEdgesTypes pointEdgeTypes(feMesh, ptI);

    const List<extendedFeatureEdgeMesh::edgeStatus> allEdStat =
        pointEdgeTypes.calcPointFeatureEdgesTypes();

//    Info<< nl << featPt << "  " << pointEdgeTypes;

    const_circulator<labelList> circ(featPtEdges);

    // Loop around the edges of the feature point
    if (circ.size()) do
    {
//        const edgeStatus eStatusPrev = feMesh.getEdgeStatus(circ.prev());
        const edgeStatus eStatusCurr = feMesh.getEdgeStatus(circ());
//        const edgeStatus eStatusNext = feMesh.getEdgeStatus(circ.next());

//        Info<< "    Prev = "
//            << extendedFeatureEdgeMesh::edgeStatusNames_[eStatusPrev]
//            << " Curr = "
//            << extendedFeatureEdgeMesh::edgeStatusNames_[eStatusCurr]
////            << " Next = "
////            << extendedFeatureEdgeMesh::edgeStatusNames_[eStatusNext]
//            << endl;

        // Get the direction in which to move the point in relation to the
        // feature point
        label sign = getSign(eStatusCurr);

        const vector n = sharedFaceNormal(feMesh, circ(), circ.next());

        const vector pointMotionDirection = sign*0.5*ppDist*n;

//        Info<< "    Shared face normal      = " << n << endl;
//        Info<< "    Direction to move point = " << pointMotionDirection
//            << endl;

        if (masterPoints.empty())
        {
            // Initialise with the first master point
            Foam::point pt = featPt + pointMotionDirection;

            planeDynList firstPlane;
            firstPlane.append(autoPtr<plane>(new plane(featPt, n)));

            masterPoints.append(pt);

            masterPointsTypes.append
            (
                sign == 1
              ? Vb::vtExternalFeaturePoint // true
              : Vb::vtInternalFeaturePoint // false
            );

            //Info<< "    " << " " << firstPlane << endl;

//            const Foam::point reflectedPoint = reflectPointInPlane
//            (
//                masterPoints.last(),
//                firstPlane.last()()
//            );

            masterPointReflections.insert
            (
                masterPoints.size() - 1,
                firstPlane
            );
        }
//        else if
//        (
//            eStatusPrev == extendedFeatureEdgeMesh::INTERNAL
//         && eStatusCurr == extendedFeatureEdgeMesh::EXTERNAL
//        )
//        {
//            // Insert a new master point.
//            Foam::point pt = featPt + pointMotionDirection;
//
//            planeDynList firstPlane;
//            firstPlane.append(autoPtr<plane>(new plane(featPt, n)));
//
//            masterPoints.append(pt);
//
//            masterPointsTypes.append
//            (
//                sign == 1
//              ? Vb::vtExternalFeaturePoint // true
//              : Vb::vtInternalFeaturePoint // false
//            );
//
//            masterPointReflections.insert
//            (
//                masterPoints.size() - 1,
//                firstPlane
//            );
//        }
//        else if
//        (
//            eStatusPrev == extendedFeatureEdgeMesh::EXTERNAL
//         && eStatusCurr == extendedFeatureEdgeMesh::INTERNAL
//        )
//        {
//
//        }
        else
        {
            // Just add this face contribution to the latest master point

            masterPoints.last() += pointMotionDirection;

            masterPointReflections[masterPoints.size() - 1].append
            (
                autoPtr<plane>(new plane(featPt, n))
            );
        }

    } while (circ.circulate(CirculatorBase::CLOCKWISE));

    addMasterAndSlavePoints
    (
        masterPoints,
        masterPointsTypes,
        masterPointReflections,
        pts,
        ptI
    );
}