/*============================================================================
 *
 *  Copyright (C) 1999-2000 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:       npatch.cpp
 *  Content:    Implementation for N-Patches
 *
 ****************************************************************************/

#include "pch.cpp"
#pragma hdrstop

//-----------------------------------------------------------------------------
// RefDev::ProcessTessPrimitive
//-----------------------------------------------------------------------------
HRESULT 
RefDev::ProcessTessPrimitive( LPD3DHAL_DP2DRAWPRIMITIVE pDP )
{
    HRESULT hr = S_OK;

    if( RDVSD_ISLEGACY( m_CurrentVShaderHandle ) )
    {
        //
        // The legacy FVF style: The Zero'th Stream is implied
        //
        DWORD dwFVF    = m_CurrentVShaderHandle;
        RDVStream& Stream = m_VStream[0];
        DWORD dwStride = Stream.m_dwStride;
        DWORD dwFVFSize = GetFVFVertexSize( dwFVF );

        if( Stream.m_pData == NULL || dwStride == 0 )
        {
            DPFERR( "Zero'th stream doesnt have valid VB set" );
            return DDERR_INVALIDPARAMS;
        }
        if( dwStride < dwFVFSize )
        {
            DPFERR( "The stride set for the vertex stream is less than"
                    " the FVF vertex size" );
            return E_FAIL;
        }
    }

    BYTE *pVerts = 0, *pNorms = 0;
    unsigned vstride, nstride;

    // Figure out where the positions and normals are
    RDVDeclaration &Decl = m_pCurrentVShader->m_Declaration;
    for(unsigned e = 0; e < Decl.m_dwNumElements; ++e)
    {
        RDVElement &velem = Decl.m_VertexElements[e];
        if(velem.m_dwRegister == D3DVSDE_POSITION) // Position
        {
            RDVStream &vstream = m_VStream[velem.m_dwStreamIndex];
            pVerts = vstream.m_pSavedData + pDP->VStart * vstream.m_dwStride + velem.m_dwOffset;
            vstride = vstream.m_dwStride;
        }
        else if(velem.m_dwRegister == D3DVSDE_NORMAL) // Normal
        {
            RDVStream &vstream = m_VStream[velem.m_dwStreamIndex];
            pNorms = vstream.m_pSavedData + pDP->VStart * vstream.m_dwStride + velem.m_dwOffset;
            nstride = vstream.m_dwStride;
        }
    }

    if(pVerts == 0 || pNorms == 0)
    {
        DPFERR("This tessellation scheme needs positions and normals explicitely specified");
        return DDERR_INVALIDPARAMS;
    }

    switch(pDP->primType)
    {
    case D3DPT_TRIANGLELIST:
        {
            for(unsigned i = 0; i < pDP->PrimitiveCount; ++i)
            {
                FLOAT *pV[3], *pN[3];
                unsigned iM[3], iN[3];
                
                pV[0] = (FLOAT*)(pVerts + i * 3 * vstride);
                pV[1] = (FLOAT*)(pVerts + (i * 3 + 1) * vstride);
                pV[2] = (FLOAT*)(pVerts + (i * 3 + 2) * vstride);
                pN[0] = (FLOAT*)(pNorms + i * 3 * nstride);
                pN[1] = (FLOAT*)(pNorms + (i * 3 + 1) * nstride);
                pN[2] = (FLOAT*)(pNorms + (i * 3 + 2) * nstride);
                iM[0] = pDP->VStart + i * 3;     iN[0] = 0;
                iM[1] = pDP->VStart + i * 3 + 1; iN[1] = 0;
                iM[2] = pDP->VStart + i * 3 + 2; iN[2] = 0;

                RDNPatch patch(pV, pN, GetRS()[D3DRS_POSITIONORDER], GetRS()[D3DRS_NORMALORDER]);
                
                hr = DrawNPatch(patch, 0, iM, iN, unsigned(GetRSf()[D3DRS_PATCHSEGMENTS]));
                if(FAILED(hr))
                {
                    return hr;
                }
            }
        }
        break;
    case D3DPT_TRIANGLEFAN:
        {
            for(unsigned i = 0; i < pDP->PrimitiveCount; ++i)
            {
                FLOAT *pV[3], *pN[3];
                unsigned iM[3], iN[3];
                
                pV[0] = (FLOAT*)(pVerts);
                pV[1] = (FLOAT*)(pVerts + (i + 1) * vstride);
                pV[2] = (FLOAT*)(pVerts + (i + 2) * vstride);
                pN[0] = (FLOAT*)(pNorms);
                pN[1] = (FLOAT*)(pNorms + (i + 1) * nstride);
                pN[2] = (FLOAT*)(pNorms + (i + 2) * nstride);
                iM[0] = pDP->VStart;         iN[0] = 0;
                iM[1] = pDP->VStart + i + 1; iN[1] = 0;
                iM[2] = pDP->VStart + i + 2; iN[2] = 0;
                
                RDNPatch patch(pV, pN, GetRS()[D3DRS_POSITIONORDER], GetRS()[D3DRS_NORMALORDER]);

                hr = DrawNPatch(patch, 0, iM, iN, unsigned(GetRSf()[D3DRS_PATCHSEGMENTS]));
                if(FAILED(hr))
                {
                    return hr;
                }
            }
        }
        break;
    case D3DPT_TRIANGLESTRIP:
        {
            for(unsigned i = 0; i < pDP->PrimitiveCount; ++i)
            {
                FLOAT *pV[3], *pN[3];
                unsigned iM[3], iN[3];

                pV[0] = (FLOAT*)(pVerts + i * vstride);
                pN[0] = (FLOAT*)(pNorms + i * nstride);
                iM[0] = pDP->VStart + i; iN[0] = 0;
                iM[0] = pDP->VStart + i; iN[0] = 0;
                if((i & 1) != 0)
                {
                    pV[1] = (FLOAT*)(pVerts + (i + 2) * vstride);
                    pV[2] = (FLOAT*)(pVerts + (i + 1) * vstride);
                    pN[1] = (FLOAT*)(pNorms + (i + 2) * nstride);
                    pN[2] = (FLOAT*)(pNorms + (i + 1) * nstride);
                    iM[1] = pDP->VStart + i + 2; iN[1] = 0;
                    iM[2] = pDP->VStart + i + 1; iN[2] = 0;
                }
                else
                {
                    pV[1] = (FLOAT*)(pVerts + (i + 1) * vstride);
                    pV[2] = (FLOAT*)(pVerts + (i + 2) * vstride);
                    pN[1] = (FLOAT*)(pNorms + (i + 1) * nstride);
                    pN[2] = (FLOAT*)(pNorms + (i + 2) * nstride);
                    iM[1] = pDP->VStart + i + 1; iN[1] = 0;
                    iM[2] = pDP->VStart + i + 2; iN[2] = 0;
                }

                RDNPatch patch(pV, pN, GetRS()[D3DRS_POSITIONORDER], GetRS()[D3DRS_NORMALORDER]);

                hr = DrawNPatch(patch, 0, iM, iN, unsigned(GetRSf()[D3DRS_PATCHSEGMENTS]));
                if(FAILED(hr))
                {
                    return hr;
                }
            }
        }
        break;
    default:
        _ASSERT(FALSE, "Unsupported primitive type");        
        hr = E_FAIL;
    }
    
    return hr;
}

//-----------------------------------------------------------------------------
// RefDev::ProcessTessIndexedPrimitive
//-----------------------------------------------------------------------------
HRESULT 
RefDev::ProcessTessIndexedPrimitive( LPD3DHAL_DP2DRAWINDEXEDPRIMITIVE pDIP )
{
    HRESULT hr = S_OK;

    if( RDVSD_ISLEGACY( m_CurrentVShaderHandle ) )
    {
        //
        // The legacy FVF style: The Zero'th Stream is implied
        //
        DWORD dwFVF    = m_CurrentVShaderHandle;
        RDVStream& Stream = m_VStream[0];
        DWORD dwStride = Stream.m_dwStride;
        DWORD dwFVFSize = GetFVFVertexSize( dwFVF );

        if( Stream.m_pData == NULL || dwStride == 0 )
        {
            DPFERR( "Zero'th stream doesnt have valid VB set" );
            return DDERR_INVALIDPARAMS;
        }
        if( dwStride < dwFVFSize )
        {
            DPFERR( "The stride set for the vertex stream is less than"
                    " the FVF vertex size" );
            return E_FAIL;
        }
        
        if( m_IndexStream.m_pData == NULL )
        {
            DPFERR( "Indices are not available" );
            return E_FAIL;
        }
    }

    BYTE *pVerts = 0, *pNorms = 0;
    unsigned vstride, nstride;

    // Figure out where the positions and normals are
    RDVDeclaration &Decl = m_pCurrentVShader->m_Declaration;
    for(unsigned e = 0; e < Decl.m_dwNumElements; ++e)
    {
        RDVElement &velem = Decl.m_VertexElements[e];
        if(velem.m_dwRegister == D3DVSDE_POSITION) // Position
        {
            RDVStream &vstream = m_VStream[velem.m_dwStreamIndex];
            pVerts = vstream.m_pSavedData + pDIP->BaseVertexIndex * vstream.m_dwStride + velem.m_dwOffset;
            vstride = vstream.m_dwStride;
        }
        else if(velem.m_dwRegister == D3DVSDE_NORMAL) // Normal
        {
            RDVStream &vstream = m_VStream[velem.m_dwStreamIndex];
            pNorms = vstream.m_pSavedData + pDIP->BaseVertexIndex * vstream.m_dwStride + velem.m_dwOffset;
            nstride = vstream.m_dwStride;
        }
    }

    if(pVerts == 0 || pNorms == 0)
    {
        DPFERR("This tessellation scheme needs positions and normals explicitely specified");
        return DDERR_INVALIDPARAMS;
    }

    RRIndexAccessor Index(m_IndexStream.m_pData, m_IndexStream.m_dwStride, pDIP->StartIndex);

    switch(pDIP->primType)
    {
    case D3DPT_TRIANGLELIST:
        {
            for(unsigned i = 0; i < pDIP->PrimitiveCount; ++i)
            {
                FLOAT *pV[3], *pN[3];
                unsigned iM[3], iN[3];

                pV[0] = (FLOAT*)(pVerts + Index[i * 3] * vstride);
                pV[1] = (FLOAT*)(pVerts + Index[i * 3 + 1] * vstride);
                pV[2] = (FLOAT*)(pVerts + Index[i * 3 + 2] * vstride);
                pN[0] = (FLOAT*)(pNorms + Index[i * 3] * nstride);
                pN[1] = (FLOAT*)(pNorms + Index[i * 3 + 1] * nstride);
                pN[2] = (FLOAT*)(pNorms + Index[i * 3 + 2] * nstride);
                iM[0] = pDIP->BaseVertexIndex + Index[i * 3];     iN[0] = 0;
                iM[1] = pDIP->BaseVertexIndex + Index[i * 3 + 1]; iN[1] = 0;
                iM[2] = pDIP->BaseVertexIndex + Index[i * 3 + 2]; iN[2] = 0;

                RDNPatch patch(pV, pN, GetRS()[D3DRS_POSITIONORDER], GetRS()[D3DRS_NORMALORDER]);
                
                hr = DrawNPatch(patch, 0, iM, iN, unsigned(GetRSf()[D3DRS_PATCHSEGMENTS]));
                if(FAILED(hr))
                {
                    return hr;
                }
            }
        }
        break;
    case D3DPT_TRIANGLEFAN:
        {
            for(unsigned i = 0; i < pDIP->PrimitiveCount; ++i)
            {
                FLOAT *pV[3], *pN[3];
                unsigned iM[3], iN[3];
                
                pV[0] = (FLOAT*)(pVerts + Index[0] * vstride);
                pV[1] = (FLOAT*)(pVerts + Index[i + 1] * vstride);
                pV[2] = (FLOAT*)(pVerts + Index[i + 2] * vstride);
                pN[0] = (FLOAT*)(pNorms + Index[0] * nstride);
                pN[1] = (FLOAT*)(pNorms + Index[i + 1] * nstride);
                pN[2] = (FLOAT*)(pNorms + Index[i + 2] * nstride);
                iM[0] = pDIP->BaseVertexIndex + Index[0];     iN[0] = 0;
                iM[1] = pDIP->BaseVertexIndex + Index[i + 1]; iN[1] = 0;
                iM[2] = pDIP->BaseVertexIndex + Index[i + 2]; iN[2] = 0;
                
                RDNPatch patch(pV, pN, GetRS()[D3DRS_POSITIONORDER], GetRS()[D3DRS_NORMALORDER]);

                hr = DrawNPatch(patch, 0, iM, iN, unsigned(GetRSf()[D3DRS_PATCHSEGMENTS]));
                if(FAILED(hr))
                {
                    return hr;
                }
            }
        }
        break;
    case D3DPT_TRIANGLESTRIP:
        {
            for(unsigned i = 0; i < pDIP->PrimitiveCount; ++i)
            {
                FLOAT *pV[3], *pN[3];
                unsigned iM[3], iN[3];

                pV[0] = (FLOAT*)(pVerts + Index[i] * vstride);
                pN[0] = (FLOAT*)(pNorms + Index[i] * nstride);
                iM[0] = pDIP->BaseVertexIndex + Index[i]; iN[0] = 0;
                iM[0] = pDIP->BaseVertexIndex + Index[i]; iN[0] = 0;
                if((i & 1) != 0)
                {
                    pV[1] = (FLOAT*)(pVerts + Index[i + 2] * vstride);
                    pV[2] = (FLOAT*)(pVerts + Index[i + 1] * vstride);
                    pN[1] = (FLOAT*)(pNorms + Index[i + 2] * nstride);
                    pN[2] = (FLOAT*)(pNorms + Index[i + 1] * nstride);
                    iM[1] = pDIP->BaseVertexIndex + Index[i + 2]; iN[1] = 0;
                    iM[2] = pDIP->BaseVertexIndex + Index[i + 1]; iN[2] = 0;
                }
                else
                {
                    pV[1] = (FLOAT*)(pVerts + Index[i + 1] * vstride);
                    pV[2] = (FLOAT*)(pVerts + Index[i + 2] * vstride);
                    pN[1] = (FLOAT*)(pNorms + Index[i + 1] * nstride);
                    pN[2] = (FLOAT*)(pNorms + Index[i + 2] * nstride);
                    iM[1] = pDIP->BaseVertexIndex + Index[i + 1]; iN[1] = 0;
                    iM[2] = pDIP->BaseVertexIndex + Index[i + 2]; iN[2] = 0;
                }

                RDNPatch patch(pV, pN, GetRS()[D3DRS_POSITIONORDER], GetRS()[D3DRS_NORMALORDER]);

                hr = DrawNPatch(patch, 0, iM, iN, unsigned(GetRSf()[D3DRS_PATCHSEGMENTS]));
                if(FAILED(hr))
                {
                    return hr;
                }
            }
        }
        break;
    default:
        _ASSERT(FALSE, "Unsupported primitive type");        
        hr = E_FAIL;
    }
    
    return hr;
}

//-----------------------------------------------------------------------------
// RDCubicBezierTriangle::SamplePosition
//-----------------------------------------------------------------------------
void RDCubicBezierTriangle::SamplePosition(double u, double v, FLOAT *Q) const
{
    for(unsigned e = 0; e < 3; ++e)
    {
        Q[e] = FLOAT(m_B[0][0][e] * Basis(0, 0, u, v) +
                     m_B[0][3][e] * Basis(0, 3, u, v) +
                     m_B[3][0][e] * Basis(3, 0, u, v) +
                     m_B[0][1][e] * Basis(0, 1, u, v) +
                     m_B[0][2][e] * Basis(0, 2, u, v) +
                     m_B[1][2][e] * Basis(1, 2, u, v) +
                     m_B[2][1][e] * Basis(2, 1, u, v) +
                     m_B[2][0][e] * Basis(2, 0, u, v) +
                     m_B[1][0][e] * Basis(1, 0, u, v) +
                     m_B[1][1][e] * Basis(1, 1, u, v));
    }
}

//-----------------------------------------------------------------------------
// RDCubicBezierTriangle::Sample
//-----------------------------------------------------------------------------
void RDCubicBezierTriangle::Sample(DWORD dwDataType, double u, double v, const BYTE* const B[], BYTE *Q) const
{
    double w = 1.0 - u - v;
    unsigned dwElements = 0;
    switch(dwDataType)
    {
        case D3DVSDT_FLOAT4:
            ++dwElements;
        case D3DVSDT_FLOAT3:
            ++dwElements;
        case D3DVSDT_FLOAT2:
            ++dwElements;
        case D3DVSDT_FLOAT1:
            ++dwElements;
            {
                for(unsigned e = 0; e < dwElements; ++e)
                {
                    ((FLOAT*)Q)[e] = FLOAT(w * double(((FLOAT*)B[0])[e]) + v * double(((FLOAT*)B[1])[e]) + u * double(((FLOAT*)B[2])[e]));
                }
            }
            break;
        case D3DVSDT_D3DCOLOR:
        case D3DVSDT_UBYTE4:
            dwElements = 4;
            {
                for(unsigned e = 0; e < 4; ++e)
                {
                    int t = int(w * double(B[0][e]) + v * double(B[1][e]) + u * double(B[2][e]));
                    Q[e] = BYTE(t < 0 ? 0 : (t > 255 ? 255 : t));
                }
            }
            break;
        case D3DVSDT_SHORT4:
            dwElements += 2;
        case D3DVSDT_SHORT2:
            dwElements += 2;
            {
                for(unsigned e = 0; e < dwElements; ++e)
                {
                    ((SHORT*)Q)[e] = SHORT(w * double(((SHORT*)B[0])[e]) + v * double(((SHORT*)B[1])[e]) + u * double(((SHORT*)B[2])[e]));
                }
            }
            break;
        default:
            _ASSERT(FALSE, "Ununderstood vertex element data type");
    }
}

//-----------------------------------------------------------------------------
// RDNPatch::RDNPatch
//-----------------------------------------------------------------------------
RDNPatch::RDNPatch(const FLOAT* const pV[], const FLOAT* const pN[], 
                   const DWORD PositionOrder, const DWORD NormalOrder)
{
    _ASSERT((PositionOrder == D3DORDER_LINEAR) || (PositionOrder == D3DORDER_CUBIC), 
            "Unsupported position order in NPatch");        
    _ASSERT((NormalOrder == D3DORDER_LINEAR) || (NormalOrder == D3DORDER_QUADRATIC), 
            "Unsupported normal order in NPatch");        

    m_PositionOrder = PositionOrder;
    m_NormalOrder = NormalOrder;
    // Assign corner points
    m_B[0][0][0] = double(pV[0][0]);
    m_B[0][0][1] = double(pV[0][1]);
    m_B[0][0][2] = double(pV[0][2]);
    m_B[0][3][0] = double(pV[1][0]);
    m_B[0][3][1] = double(pV[1][1]);
    m_B[0][3][2] = double(pV[1][2]);
    m_B[3][0][0] = double(pV[2][0]);
    m_B[3][0][1] = double(pV[2][1]);
    m_B[3][0][2] = double(pV[2][2]);

    if (PositionOrder == D3DORDER_CUBIC)
    {
        // Compute edge control points
        ComputeEdgeControlPoint(0, 1, pV, pN, 0, 1);
        ComputeEdgeControlPoint(1, 0, pV, pN, 0, 2);
        ComputeEdgeControlPoint(1, 2, pV, pN, 1, 2);
        ComputeEdgeControlPoint(2, 1, pV, pN, 2, 1);
        ComputeEdgeControlPoint(2, 0, pV, pN, 2, 0);
        ComputeEdgeControlPoint(0, 2, pV, pN, 1, 0);

        // Compute central control point
        m_B[1][1][0] = (m_B[2][0][0] + m_B[1][0][0] + m_B[0][2][0] + m_B[0][1][0] + m_B[2][1][0] + m_B[1][2][0]) / 4.0 -
                        (m_B[3][0][0] + m_B[0][3][0] + m_B[0][0][0]) / 6.0;
        m_B[1][1][1] = (m_B[2][0][1] + m_B[1][0][1] + m_B[0][2][1] + m_B[0][1][1] + m_B[2][1][1] + m_B[1][2][1]) / 4.0 -
                        (m_B[3][0][1] + m_B[0][3][1] + m_B[0][0][1]) / 6.0;
        m_B[1][1][2] = (m_B[2][0][2] + m_B[1][0][2] + m_B[0][2][2] + m_B[0][1][2] + m_B[2][1][2] + m_B[1][2][2]) / 4.0 -
                        (m_B[3][0][2] + m_B[0][3][2] + m_B[0][0][2]) / 6.0;
    }
    if (NormalOrder == D3DORDER_QUADRATIC)
    {
        // Compute central control point
        Normalize(*(RDVECTOR3*)pN[0]);
        Normalize(*(RDVECTOR3*)pN[1]);
        Normalize(*(RDVECTOR3*)pN[2]);
        m_N002 = *(RDVECTOR3*)pN[0];
        m_N020 = *(RDVECTOR3*)pN[1];
        m_N200 = *(RDVECTOR3*)pN[2];
        ComputeNormalControlPoint(&m_N110, 1, 2, pV, pN);
        ComputeNormalControlPoint(&m_N101, 2, 0, pV, pN);
        ComputeNormalControlPoint(&m_N011, 0, 1, pV, pN);
    }
}

//-----------------------------------------------------------------------------
// RDNPatch::SamplePosition
//-----------------------------------------------------------------------------
void RDNPatch::SamplePosition(double u, double v, FLOAT *Q) const
{
    if (m_PositionOrder == D3DORDER_CUBIC)
        RDCubicBezierTriangle::SamplePosition(u, v, Q);
    else
    {
        double w = 1.0 - u - v;
        Q[0] = m_B[0][0][0] * w + m_B[0][3][0] * v + m_B[3][0][0] * u;
        Q[1] = m_B[0][0][1] * w + m_B[0][3][1] * v + m_B[3][0][1] * u;
        Q[2] = m_B[0][0][2] * w + m_B[0][3][2] * v + m_B[3][0][2] * u;
    }
}

//-----------------------------------------------------------------------------
// RDNPatch::SampleNormal
//-----------------------------------------------------------------------------
void RDNPatch::SampleNormal(double u, double v, const BYTE* const B[], FLOAT *Q) const
{
    if (m_NormalOrder == D3DORDER_LINEAR)
        RDCubicBezierTriangle::Sample(D3DVSDT_FLOAT3, u, v, B, (BYTE*)Q);
    else
    {
        // Computed by article "Curved PN Triangles" (Chas Boyd, ...)
        double w = 1.0 - u - v;
        double ww = w*w;
        double uu = u*u;
        double vv = v*v;
        double uv = u*v;
        double wu = w*u;
        double wv = w*v;
        Q[0] = m_N200.x * uu + m_N020.x * vv + m_N002.x * ww + m_N110.x * uv + m_N011.x * wv + m_N101.x * wu;
        Q[1] = m_N200.y * uu + m_N020.y * vv + m_N002.y * ww + m_N110.y * uv + m_N011.y * wv + m_N101.y * wu;
        Q[2] = m_N200.z * uu + m_N020.z * vv + m_N002.z * ww + m_N110.z * uv + m_N011.z * wv + m_N101.z * wu;
        Normalize(*(RDVECTOR3*)Q);
    }
}

//-----------------------------------------------------------------------------
// RDNPatch::ComputeNormalControlPoint
//-----------------------------------------------------------------------------
void RDNPatch::ComputeNormalControlPoint(RDVECTOR3* cp, unsigned i, unsigned j,
                                         const FLOAT* const pV[], 
                                         const FLOAT* const pN[])
{
    RDVECTOR3 Pji, Nij;
    SubtractVector(*(RDVECTOR3*)pV[j], *(RDVECTOR3*)pV[i], Pji);
    AddVector(*(RDVECTOR3*)pN[j], *(RDVECTOR3*)pN[i], Nij);
    FLOAT v = 2.0f * DotProduct(Pji, Nij) / DotProduct(Pji, Pji);
    SubtractVector(Nij, ScaleVector(Pji, v), *cp);
}

//-----------------------------------------------------------------------------
// RDNPatch::ComputeEdgeControlPoint
//-----------------------------------------------------------------------------
void RDNPatch::ComputeEdgeControlPoint(unsigned a, unsigned b, const FLOAT* const pV[], const FLOAT* const pN[], unsigned u, unsigned v)
{
    static const double Tension = 1.0 / 3.0; 
    double t, Edge[3];

    Edge[0] = double(pV[b][0]) - double(pV[a][0]);
    Edge[1] = double(pV[b][1]) - double(pV[a][1]);
    Edge[2] = double(pV[b][2]) - double(pV[a][2]);

    t = Edge[0] * double(pN[a][0]) + Edge[1] * double(pN[a][1]) + Edge[2] * double(pN[a][2]);

    m_B[u][v][0] = double(pV[a][0]) + (Edge[0] - t * double(pN[a][0])) * Tension;
    m_B[u][v][1] = double(pV[a][1]) + (Edge[1] - t * double(pN[a][1])) * Tension;
    m_B[u][v][2] = double(pV[a][2]) + (Edge[2] - t * double(pN[a][2])) * Tension;
}