Raw File
NURBSRectangle.cpp
// Geometric Tools, LLC
// Copyright (c) 1998-2012
// Distributed under the Boost Software License, Version 1.0.
// http://www.boost.org/LICENSE_1_0.txt
// http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
//
// File Version: 5.0.1 (2010/10/01)


#include "NURBSRectangle.h"
#include "NURBSCurve.h"

namespace NURBS
{

//----------------------------------------------------------------------------
template <typename Real>
NURBSRectangle<Real>::NURBSRectangle (Array2D_Vector3 ctrlPoint, Array2D_Real ctrlWeight,
    int uDegree, int vDegree, bool uLoop, bool vLoop, bool uOpen, bool vOpen)
    :
    ParametricSurface<Real>((Real)0, (Real)1, (Real)0, (Real)1, true)
{
    int numUCtrlPoints = ctrlPoint.size();
    int numVCtrlPoints = ctrlPoint.front().size();

    assertion(numUCtrlPoints >= 2, "Invalid input\n");
    assertion(1 <= uDegree && uDegree <= numUCtrlPoints - 1,
        "Invalid input\n");
    assertion(numVCtrlPoints >= 2, "Invalid input\n");
    assertion(1 <= vDegree && vDegree <= numVCtrlPoints - 1,
        "Invalid input\n");

	mLoop.resize(2);
    mLoop[0] = uLoop;
    mLoop[1] = vLoop;

    mNumUCtrlPoints = numUCtrlPoints;
    mNumVCtrlPoints = numVCtrlPoints;
    mUReplicate = (uLoop ? (uOpen ? 1 : uDegree) : 0);
    mVReplicate = (vLoop ? (vOpen ? 1 : vDegree) : 0);
    CreateControl(ctrlPoint, ctrlWeight);

	mBasis.resize(2);

    mBasis[0].Create(mNumUCtrlPoints + mUReplicate, uDegree, uOpen);
    mBasis[1].Create(mNumVCtrlPoints + mVReplicate, vDegree, vOpen);
}
//----------------------------------------------------------------------------
template <typename Real>
NURBSRectangle<Real>::NURBSRectangle (int numUCtrlPoints,
    int numVCtrlPoints, Array2D_Vector3 ctrlPoint, Array2D_Real ctrlWeight,
    int uDegree, int vDegree, bool uLoop, bool vLoop, bool uOpen, Real* vKnot)
    :
    ParametricSurface<Real>((Real)0, (Real)1, (Real)0, (Real)1, true)
{
    assertion(numUCtrlPoints >= 2, "Invalid input\n");
    assertion(1 <= uDegree && uDegree <= numUCtrlPoints - 1,
        "Invalid input\n");
    assertion(numVCtrlPoints >= 2, "Invalid input\n");
    assertion(1 <= vDegree && vDegree <= numVCtrlPoints - 1,
        "Invalid input\n");

    mLoop[0] = uLoop;
    mLoop[1] = vLoop;

    mNumUCtrlPoints = numUCtrlPoints;
    mNumVCtrlPoints = numVCtrlPoints;
    mUReplicate = (uLoop ? (uOpen ? 1 : uDegree) : 0);
    mVReplicate = (vLoop ? 1 : 0);
    CreateControl(ctrlPoint, ctrlWeight);

    mBasis[0].Create(mNumUCtrlPoints + mUReplicate, uDegree, uOpen);
    mBasis[1].Create(mNumVCtrlPoints + mVReplicate, vDegree, vKnot);
}
//----------------------------------------------------------------------------
template <typename Real>
NURBSRectangle<Real>::NURBSRectangle (int numUCtrlPoints,
    int numVCtrlPoints, Array2D_Vector3 ctrlPoint, Array2D_Real ctrlWeight,
    int uDegree, int vDegree, bool uLoop, bool vLoop, Real* uKnot, bool vOpen)
    :
    ParametricSurface<Real>((Real)0, (Real)1, (Real)0, (Real)1, true)
{
    assertion(numUCtrlPoints >= 2, "Invalid input\n");
    assertion(1 <= uDegree && uDegree <= numUCtrlPoints - 1,
        "Invalid input\n");
    assertion(numVCtrlPoints >= 2, "Invalid input\n");
    assertion(1 <= vDegree && vDegree <= numVCtrlPoints - 1,
        "Invalid input\n");

    mLoop[0] = uLoop;
    mLoop[1] = vLoop;

    mNumUCtrlPoints = numUCtrlPoints;
    mNumVCtrlPoints = numVCtrlPoints;
    mUReplicate = (uLoop ? 1 : 0);
    mVReplicate = (vLoop ? (vOpen ? 1 : vDegree) : 0);
    CreateControl(ctrlPoint, ctrlWeight);

    mBasis[0].Create(mNumUCtrlPoints + mUReplicate, uDegree, uKnot);
    mBasis[1].Create(mNumVCtrlPoints + mVReplicate, vDegree, vOpen);
}
//----------------------------------------------------------------------------
template <typename Real>
NURBSRectangle<Real>::NURBSRectangle (int numUCtrlPoints,
    int numVCtrlPoints, Array2D_Vector3 ctrlPoint, Array2D_Real ctrlWeight,
    int uDegree, int vDegree, bool uLoop, bool vLoop, Real* uKnot,
    Real* vKnot)
    :
    ParametricSurface<Real>((Real)0, (Real)1, (Real)0, (Real)1, true)
{
    assertion(numUCtrlPoints >= 2, "Invalid input\n");
    assertion(1 <= uDegree && uDegree <= numUCtrlPoints - 1,
        "Invalid input\n");
    assertion(numVCtrlPoints >= 2, "Invalid input\n");
    assertion(1 <= vDegree && vDegree <= numVCtrlPoints - 1,
        "Invalid input\n");

    mLoop[0] = uLoop;
    mLoop[1] = vLoop;

    mNumUCtrlPoints = numUCtrlPoints;
    mNumVCtrlPoints = numVCtrlPoints;
    mUReplicate = (uLoop ? 1 : 0);
    mVReplicate = (vLoop ? 1 : 0);
    CreateControl(ctrlPoint,ctrlWeight);

    mBasis[0].Create(mNumUCtrlPoints + mUReplicate, uDegree, uKnot);
    mBasis[1].Create(mNumVCtrlPoints + mVReplicate, vDegree, vKnot);
}

//----------------------------------------------------------------------------
template <typename Real>
NURBSRectangle<Real> NURBSRectangle<Real>::createSheet(Scalar width, Scalar length, Vector3 center, Vector3 dU, Vector3 dV, int nU, int nV)
{
    int degree = 3;

    Vector3 corner = center - (dU * width * 0.5) - (dV * length * 0.5);

    Scalar aspect_U = 1.0, aspect_V = 1.0;
    if(width > length) aspect_U = length / width;
    if(length > width) aspect_V = width / length;

    nU *= 1.0 / aspect_U;
    nV *= 1.0 / aspect_V;

    // Rectangular surface
    std::vector< Array1D_Vector3 > pts( nU, Array1D_Vector3( nV, Vector3(0,0,0) ) );
    std::vector< std::vector<Scalar> > weights( nU, std::vector<Scalar>( nV, 1.0 ) );

    Vector3 deltaU = (width  / (nU-1)) * dU;
    Vector3 deltaV = (length / (nV-1)) * dV;

    for(int y = 0; y < nV; y++){
        for(int x = 0; x < nU; x++)
        {
            pts[x][y] = corner + (deltaU * x) + (deltaV * y);
        }
    }

    return NURBSRectangle<Real>(pts, weights, degree, degree, false, false, true, true);
}

template <typename Real>
NURBSRectangle<Real> NURBSRectangle<Real>::createSheet( Vector3d corner1, Vector3d corner2, int stepsU, int stepsV )
{
    int nU = stepsU, nV = stepsV;
    int degree = 3;

    Vector3 center = (corner1 + corner2) * 0.5;
    Vector3 corner = corner1;

    Vector3 d = corner1 - corner2;
    assert(d.norm() > 0);

    QVector<Scalar> vals;
    vals.push_back(fabs(d.x()));
    vals.push_back(fabs(d.y()));
    vals.push_back(fabs(d.z()));
    qSort(vals);
    if(vals[1] == 0.0) vals[1] = 1e-16;
    if(vals[1] == vals[2]) vals[2] += 1e-6;

    double width = vals[2];
    double length = vals[1];

    Vector3 xyz[3] = { Vector3(0,0,1), Vector3(0,1,0), Vector3(1,0,0) };
    Vector3 dU = xyz[ vals.indexOf(width ) ];
    Vector3 dV = xyz[ vals.indexOf(length) ];

    Scalar aspect_U = 1.0, aspect_V = 1.0;
    if(width > length) aspect_U = length / width;
    if(length > width) aspect_V = width / length;

    nU *= 1.0 / aspect_U;
    nV *= 1.0 / aspect_V;

    // Rectangular surface
    std::vector< std::vector<Vector3> > pts( nU, std::vector<Vector3>( nV, Vector3(0,0,0) ) );
    std::vector< std::vector<Scalar> > weights( nU, std::vector<Scalar>( nV, 1.0 ) );

    Vector3 deltaU = (width  / (nU-1)) * dU;
    Vector3 deltaV = (length / (nV-1)) * dV;

    for(int y = 0; y < nV; y++){
        for(int x = 0; x < nU; x++)
        {
            pts[x][y] = corner + (deltaU * x) + (deltaV * y);
        }
    }

    return NURBSRectangle<Real>(pts, weights, degree, degree, false, false, true, true);
}

template <typename Real>
NURBSRectangle<Real> NURBS::NURBSRectangle<Real>::createSheetFromPoints( Array2D_Vector3 ctrlPoint )
{
	int degree = 3;
	int nU = ctrlPoint.size(), nV = ctrlPoint.front().size();
	std::vector< std::vector<Scalar> > weights( nU, std::vector<Scalar>( nV, 1.0 ) );
	return NURBSRectangle<Real>(ctrlPoint, weights, degree, degree, false, false, true, true); 
}

//----------------------------------------------------------------------------
template <typename Real>
void NURBSRectangle<Real>::CreateControl (Array2D_Vector3 ctrlPoint, Array2D_Real ctrlWeight)
{
    int newNumUCtrlPoints = mNumUCtrlPoints + mUReplicate;
    int newNumVCtrlPoints = mNumVCtrlPoints + mVReplicate;

    mCtrlPoint = new2<Vector3>(newNumUCtrlPoints, newNumVCtrlPoints, Vector3(0,0,0));
    mCtrlWeight = new2<Real>(newNumUCtrlPoints, newNumVCtrlPoints, 1.0);

    for (int iu = 0; iu < newNumUCtrlPoints; iu++)
    {
        int uOld = iu % mNumUCtrlPoints;
        for (int iv = 0; iv < newNumVCtrlPoints; iv++)
        {
            int vOld = iv % mNumVCtrlPoints;
            mCtrlPoint[iu][iv] = ctrlPoint[uOld][vOld];
            mCtrlWeight[iu][iv] = ctrlWeight[uOld][vOld];
        }
    }
}
//----------------------------------------------------------------------------
template <typename Real>
int NURBSRectangle<Real>::GetNumCtrlPoints (int dim) const
{
    return mBasis[dim].GetNumCtrlPoints();
}
//----------------------------------------------------------------------------
template <typename Real>
int NURBSRectangle<Real>::GetDegree (int dim) const
{
    return mBasis[dim].GetDegree();
}
//----------------------------------------------------------------------------
template <typename Real>
bool NURBSRectangle<Real>::IsOpen (int dim) const
{
    return mBasis[dim].IsOpen();
}
//----------------------------------------------------------------------------
template <typename Real>
bool NURBSRectangle<Real>::IsUniform (int dim) const
{
    return mBasis[dim].IsUniform();
}
//----------------------------------------------------------------------------
template <typename Real>
bool NURBSRectangle<Real>::IsLoop (int dim) const
{
    return mLoop[dim];
}
//----------------------------------------------------------------------------
template <typename Real>
void NURBSRectangle<Real>::SetControlPoint (int uIndex, int vIndex,
    const Vector3& ctrl)
{
    if (0 <= uIndex && uIndex < mNumUCtrlPoints
    &&  0 <= vIndex && vIndex < mNumVCtrlPoints)
    {
        // Set the control point.
        mCtrlPoint[uIndex][vIndex] = ctrl;

        // Set the replicated control point.
        bool doUReplicate = (uIndex < mUReplicate);
        bool doVReplicate = (vIndex < mVReplicate);
        int uExt = 0, vExt = 0;

        if (doUReplicate)
        {
            uExt = mNumUCtrlPoints + uIndex;
            mCtrlPoint[uExt][vIndex] = ctrl;
        }
        if (doVReplicate)
        {
            vExt = mNumVCtrlPoints + vIndex;
            mCtrlPoint[uIndex][vExt] = ctrl;
        }
        if (doUReplicate && doVReplicate)
        {
            mCtrlPoint[uExt][vExt] = ctrl;
        }
    }
}
//----------------------------------------------------------------------------
template <typename Real>
Vector3 NURBSRectangle<Real>::GetControlPoint (int uIndex,
    int vIndex) const
{
    if (0 <= uIndex && uIndex < mNumUCtrlPoints
    &&  0 <= vIndex && vIndex < mNumVCtrlPoints)
    {
        return mCtrlPoint[uIndex][vIndex];
    }

    return Vector3(std::numeric_limits<Real>::max(), std::numeric_limits<Real>::max(),
        std::numeric_limits<Real>::max());
}
//----------------------------------------------------------------------------
template <typename Real>
void NURBSRectangle<Real>::SetControlWeight (int uIndex, int vIndex,
    Real weight)
{
    if (0 <= uIndex && uIndex < mNumUCtrlPoints
    &&  0 <= vIndex && vIndex < mNumVCtrlPoints)
    {
        // Set the control weight.
        mCtrlWeight[uIndex][vIndex] = weight;

        // Set the replicated control weight.
        bool doUReplicate = (uIndex < mUReplicate );
        bool doVReplicate = (vIndex < mVReplicate);
        int uExt = 0, vExt = 0;

        if (doUReplicate)
        {
            uExt = mNumUCtrlPoints + uIndex;
            mCtrlWeight[uExt][vIndex] = weight;
        }
        if (doVReplicate)
        {
            vExt = mNumVCtrlPoints + vIndex;
            mCtrlWeight[uIndex][vExt] = weight;
        }
        if (doUReplicate && doVReplicate)
        {
            mCtrlWeight[uExt][vExt] = weight;
        }
    }
}
//----------------------------------------------------------------------------
template <typename Real>
Real NURBSRectangle<Real>::GetControlWeight (int uIndex, int vIndex) const
{
    if (0 <= uIndex && uIndex < mNumUCtrlPoints
    &&  0 <= vIndex && vIndex < mNumVCtrlPoints)
    {
        return mCtrlWeight[uIndex][vIndex];
    }

    return std::numeric_limits<Real>::max();
}
//----------------------------------------------------------------------------
template <typename Real>
void NURBSRectangle<Real>::SetKnot (int dim, int i, Real knot)
{
    if (0 <= dim && dim <= 1)
    {
        mBasis[dim].SetKnot(i,knot);
    }
}
//----------------------------------------------------------------------------
template <typename Real>
Real NURBSRectangle<Real>::GetKnot (int dim, int i) const
{
    if (0 <= dim && dim <= 1)
    {
        return mBasis[dim].GetKnot(i);
    }

    return std::numeric_limits<Real>::max();
}
//----------------------------------------------------------------------------
template <typename Real>
void NURBSRectangle<Real>::Get (Real u, Real v, Vector3* pos,
    Vector3* derU, Vector3* derV, Vector3* derUU,
    Vector3* derUV, Vector3* derVV)
{
    int iu, iumin, iumax;
    if (derUU)
    {
        mBasis[0].Compute(u, 0, iumin, iumax);
        mBasis[0].Compute(u, 1, iumin, iumax);
        mBasis[0].Compute(u, 2, iumin, iumax);
    }
    else if (derUV || derU)
    {
        mBasis[0].Compute(u, 0, iumin, iumax);
        mBasis[0].Compute(u, 1, iumin, iumax);
    }
    else
    {
        mBasis[0].Compute(u, 0, iumin, iumax);
    }

    int iv, ivmin, ivmax;
    if (derVV)
    {
        mBasis[1].Compute(v, 0, ivmin, ivmax);
        mBasis[1].Compute(v, 1, ivmin, ivmax);
        mBasis[1].Compute(v, 2, ivmin, ivmax);
    }
    else if (derUV || derV)
    {
        mBasis[1].Compute(v, 0, ivmin, ivmax);
        mBasis[1].Compute(v, 1, ivmin, ivmax);
    }
    else
    {
        mBasis[1].Compute(v, 0, ivmin, ivmax);
    }

    Real tmp;

    Vector3 X = Vector3_ZERO;
    Real w = (Real)0;
    for (iu = iumin; iu <= iumax; ++iu)
    {
        for (iv = ivmin; iv <= ivmax; ++iv)
        {
            tmp = mBasis[0].GetD0(iu)*mBasis[1].GetD0(iv)*mCtrlWeight[iu][iv];
            X += tmp*mCtrlPoint[iu][iv];
            w += tmp;
        }
    }
    Real invW = ((Real)1)/w;
    Vector3 P = invW*X;
    if (pos)
    {
        *pos = P;
    }

    if (!derU && !derV && !derUU && !derUV && !derVV)
    {
        return;
    }

    Real wDerU = (Real)0;
    Real wDerV = (Real)0;
    Vector3 PDerU = Vector3_ZERO;
    Vector3 PDerV = Vector3_ZERO;

    if (derU || derUU || derUV)
    {
        Vector3 XDerU = Vector3_ZERO;
        for (iu = iumin; iu <= iumax; ++iu)
        {
            for (iv = ivmin; iv <= ivmax; ++iv)
            {
                tmp = mBasis[0].GetD1(iu)*mBasis[1].GetD0(iv)*
                    mCtrlWeight[iu][iv];
                XDerU += tmp*mCtrlPoint[iu][iv];
                wDerU += tmp;
            }
        }
        PDerU = invW*(XDerU - wDerU*P);
        if (derU)
        {
            *derU = PDerU;
        }
    }

    if (derV || derVV || derUV)
    {
        Vector3 XDerV = Vector3_ZERO;
        for (iu = iumin; iu <= iumax; ++iu)
        {
            for (iv = ivmin; iv <= ivmax; ++iv)
            {
                tmp = mBasis[0].GetD0(iu)*mBasis[1].GetD1(iv)*
                    mCtrlWeight[iu][iv];
                XDerV += tmp*mCtrlPoint[iu][iv];
                wDerV += tmp;
            }
        }
        PDerV = invW*(XDerV - wDerV*P);
        if (derV)
        {
            *derV = PDerV;
        }
    }

    if (!derUU && !derUV && !derVV)
    {
        return;
    }

    if (derUU)
    {
        Vector3 XDerUU = Vector3_ZERO;
        Real wDerUU = (Real)0;
        for (iu = iumin; iu <= iumax; ++iu)
        {
            for (iv = ivmin; iv <= ivmax; ++iv)
            {
                tmp = mBasis[0].GetD2(iu)*mBasis[1].GetD0(iv)*
                    mCtrlWeight[iu][iv];
                XDerUU += tmp*mCtrlPoint[iu][iv];
                wDerUU += tmp;
            }
        }
        *derUU = invW*(XDerUU - ((Real)2)*wDerU*PDerU - wDerUU*P);
    }

    if (derUV)
    {
        Vector3 XDerUV = Vector3_ZERO;
        Real wDerUV = (Real)0;
        for (iu = iumin; iu <= iumax; ++iu)
        {
            for (iv = ivmin; iv <= ivmax; ++iv)
            {
                tmp = mBasis[0].GetD1(iu)*mBasis[1].GetD1(iv)*
                    mCtrlWeight[iu][iv];
                XDerUV += tmp*mCtrlPoint[iu][iv];
                wDerUV += tmp;
            }
        }
        *derUV = invW*(XDerUV - wDerU*PDerV - wDerV*PDerU - wDerUV*P);
    }

    if (derVV)
    {
        Vector3 XDerVV = Vector3_ZERO;
        Real wDerVV = (Real)0;
        for (iu = iumin; iu <= iumax; ++iu)
        {
            for (iv = ivmin; iv <= ivmax; ++iv)
            {
                tmp = mBasis[0].GetD0(iu)*mBasis[1].GetD2(iv)*
                    mCtrlWeight[iu][iv];
                XDerVV += tmp*mCtrlPoint[iu][iv];
                wDerVV += tmp;
            }
        }
        *derVV = invW*(XDerVV - ((Real)2)*wDerV*PDerV - wDerVV*P);
    }
}
//----------------------------------------------------------------------------
template <typename Real>
Vector3 NURBSRectangle<Real>::P (Real u, Real v)
{
    Vector3 pos;
    Get(u, v, &pos, 0, 0, 0, 0, 0);
    return pos;
}
//----------------------------------------------------------------------------
template <typename Real>
Vector3 NURBSRectangle<Real>::PU (Real u, Real v)
{
    Vector3 derU;
    Get(u, v, 0, &derU, 0, 0, 0, 0);
    return derU;
}
//----------------------------------------------------------------------------
template <typename Real>
Vector3 NURBSRectangle<Real>::PV (Real u, Real v)
{
    Vector3 derV;
    Get(u, v, 0, 0, &derV, 0, 0, 0);
    return derV;
}
//----------------------------------------------------------------------------
template <typename Real>
Vector3 NURBSRectangle<Real>::PUU (Real u, Real v)
{
    Vector3 derUU;
    Get(u, v, 0, 0, 0, &derUU, 0, 0);
    return derUU;
}
//----------------------------------------------------------------------------
template <typename Real>
Vector3 NURBSRectangle<Real>::PUV (Real u, Real v)
{
    Vector3 derUV;
    Get(u, v, 0, 0, 0, 0, &derUV, 0);
    return derUV;
}
//----------------------------------------------------------------------------
template <typename Real>
Vector3 NURBSRectangle<Real>::PVV (Real u, Real v)
{
    Vector3 derVV;
    Get(u, v, 0, 0, 0, 0, 0, &derVV);
    return derVV;
}
//----------------------------------------------------------------------------

template <typename Real>
std::vector< std::vector<Vector3> > NURBSRectangle<Real>::generateSurfaceTris( Scalar resolution )
{
    std::vector< std::vector<Vector3> > tris;

    std::vector<Real> valU,valV;
    uniformCoordinates(valU, valV, resolution);

    for(int y = 0; y < (int)valV.size() - 1; y++)
    {
        for(int x = 0; x < (int)valU.size() - 1; x++)
        {
            std::vector<Vector3> pos(4);

            pos[0] = P(valU[x]	, valV[y]	);
            pos[1] = P(valU[x+1], valV[y]	);
            pos[2] = P(valU[x+1], valV[y+1]	);
            pos[3] = P(valU[x]	, valV[y+1]	);

            std::vector<Vector3> f1,f2;

            f1.push_back(pos[0]); f1.push_back(pos[2]); f1.push_back(pos[1]);
            f2.push_back(pos[0]); f2.push_back(pos[3]); f2.push_back(pos[2]);

            tris.push_back(f1);
            tris.push_back(f2);
        }
    }

    return tris;
}

template <typename Real>
void NURBSRectangle<Real>::generateSurfaceQuads(double resolution)
{
    std::vector<Real> valU,valV;
    uniformCoordinates(valU, valV, resolution);

    for(int y = 0; y < (int)valV.size() - 1; y++)
    {
        for(int x = 0; x < (int)valU.size() - 1; x++)
        {
            std::vector<Vector3> pos = Array1D_Vector3(4, Vector3(0,0,0));
            std::vector<Vector3> dU = pos, dV = pos, normal = pos;

            this->GetFrame(valU[x]	, valV[y]	,	pos[0], dU[0], dV[0], normal[0]);
            this->GetFrame(valU[x+1]	, valV[y]	,	pos[1], dU[1], dV[1], normal[1]);
            this->GetFrame(valU[x+1]	, valV[y+1]	,	pos[2], dU[2], dV[2], normal[2]);
            this->GetFrame(valU[x]	, valV[y+1]	,	pos[3], dU[3], dV[3], normal[3]);

            SurfaceQuad quad;

            for(int k = 0; k < 4; k++){
                quad.p[k] = pos[k];
                quad.n[k] = normal[k];
            }

            quads.push_back(quad);
        }
    }
}

template <typename Real>
void NURBSRectangle<Real>::uniformCoordinates( std::vector<Real> & valU, std::vector<Real> & valV, double resolution, int u, int v )
{
    std::vector<Vector3> cptsU = GetControlPointsU(u);
    std::vector<Vector3> cptsV = GetControlPointsV(v);

    // Uniform across given U, V
    NURBSCurve<Real> curveU( cptsU, std::vector<Scalar>(cptsU.size(), 1) );
    NURBSCurve<Real> curveV( cptsV, std::vector<Scalar>(cptsV.size(), 1) );

    double lengthU = curveU.GetLength(0,1), lengthV = curveV.GetLength(0,1);

    // In case of zero sheet, which could be a curve
    if (lengthU < resolution || lengthV < resolution)
        return;

    double resU = resolution, resV = resolution;

    int nU = lengthU / resolution;
    int nV = lengthV / resolution;
    resU = lengthU / nU;
    resV = lengthV / nV;

    for(int i = 0; i <= nU; i++)
    {
        double distance = resU * i;
        valU.push_back(curveU.GetTime(distance));
    }
    for(int i = 0; i <= nV; i++)
    {
        double distance = resV * i;
        valV.push_back(curveV.GetTime(distance));
    }
}

template <typename Real>
void NURBSRectangle<Real>::generateSurfacePointsCoords( Scalar stepSize, std::vector< Array1D_Vector4d > & points )
{
    std::vector<Real> valU,valV;
    uniformCoordinates(valU, valV, stepSize);

    points.resize(valU.size(), Array1D_Vector4d(valV.size()));

    for(int y = 0; y < (int)valV.size(); y++)
        for(int x = 0; x < (int)valU.size(); x++)
            points[x][y] = Vector4d(valU[x], valV[y], 0, 0);
}

template <typename Real>
std::vector<Vector3> NURBSRectangle<Real>::GetControlPointsV( int vIndex )
{
    return mCtrlPoint[vIndex];
}

template <typename Real>
std::vector<Vector3> NURBSRectangle<Real>::GetControlPointsU( int uIndex )
{
    std::vector<Vector3> pts;

	for(int i = 0; i < (int)mCtrlPoint.size(); i++)
		pts.push_back(mCtrlPoint[i][uIndex]);

	return pts;
}


template <typename Real>
std::vector<Scalar> NURBS::NURBSRectangle<Real>::GetControlWeightsU( int uIndex )
{
	std::vector<Scalar> weights;

	for(int i = 0; i < (int)mCtrlWeight.size(); i++)
		weights.push_back(mCtrlWeight[i][uIndex]);


	return weights;
}

template <typename Real>
std::vector<Scalar> NURBS::NURBSRectangle<Real>::GetControlWeightsV( int vIndex )
{
	return mCtrlWeight[vIndex];
}


template <typename Real>
Array1D_Vector3 NURBSRectangle<Real>::intersect( NURBSRectangle<Real> & other, double resolution,
    Array1D_Vector4d & coordMe, Array1D_Vector4d & coordOther )
{
    Array1D_Vector3 samples;

    std::vector< std::vector<Vector3> > pnts1, pnts2;
    std::vector< std::vector< std::vector<Real> > > val( 2, std::vector< std::vector<Real> >(2) );
    std::vector<Real> valU1,valV1,valU2,valV2;
    std::vector<Real> iU1,iV1,iU2,iV2;

    generateSurfacePoints(resolution, pnts1, valU1, valV1);
    other.generateSurfacePoints(resolution, pnts2, valU2, valV2);

    val[0][0] = valU1; val[0][1] = valV1;
    val[1][0] = valU2; val[1][1] = valV2;

    std::vector< std::pair< std::pair<Scalar,Scalar>, Vector3d> > mysamples, othersamples;

    for(int y1 = 0; y1 < (int)valV1.size(); y1++){
        for(int x1 = 0; x1 < (int)valU1.size(); x1++){
            mysamples.push_back(std::make_pair( std::make_pair(valU1[x1], valV1[y1]), pnts1[x1][y1] ));
        }
    }

    for(int y2 = 0; y2 < (int)valV2.size(); y2++){
        for(int x2 = 0; x2 < (int)valU2.size(); x2++){
            othersamples.push_back(std::make_pair( std::make_pair(valU2[x2], valV2[y2]), pnts2[x2][y2] ));
        }
    }

    for(int i = 0; i < (int)mysamples.size(); i++)
    {
        for(int j = 0; j < (int)othersamples.size(); j++)
        {
            if(sphereTest(mysamples[i].second, othersamples[j].second, resolution * 0.5, resolution * 0.5))
                samples.push_back(mysamples[i].second);
        }
    }

    std::vector<size_t> corner_xrefs;
    weld(samples, corner_xrefs, std::hash_Vector3d(), std::equal_to<Vector3d>());

    Vector3d p(0,0,0);
    double threshold = resolution * 0.5;

    // Project onto other surface
    Array1D_Vector4d otheruv = other.timeAt(samples, threshold);
    Array1D_Vector3 projectionOther;
    for(int i = 0; i < (int)otheruv.size(); i++){
        other.Get(otheruv[i][0], otheruv[i][1], &p);
        projectionOther.push_back(p);
    }

    samples.clear();

    // Project on me
    coordMe = this->timeAt(projectionOther, threshold);
    Array1D_Vector3 projectionMe;
    for(int i = 0; i < (int)coordMe.size(); i++){
        this->Get(coordMe[i][0], coordMe[i][1], &p);
        samples.push_back(p);
    }

    weld(samples, corner_xrefs, std::hash_Vector3d(), std::equal_to<Vector3d>());

    // Cluster and average
    std::vector<bool> visited(samples.size(), false);
    QMap<int, QVector<Vector3d> > group;
    for(int i = 0; i < (int)samples.size(); i++){
        if(!visited[i]){
            for(int j = 0; j < (int)samples.size(); j++){
                if((samples[i] - samples[j]).norm() < resolution * 2){
                    samples[j] = samples[i];
                    visited[j] = true;
                    group[i].push_back(samples[j]);
                }
            }
            group[i].push_back(samples[i]);
            visited[i] = true;
        }
    }
    samples.clear();
    foreach(QVector<Vector3d> pnts, group.values()){
        Vector3d avg(0,0,0);
        foreach(Vector3d p, pnts){	avg += p; }
        avg /= pnts.size();
        samples.push_back(avg);
    }

    weld(samples, corner_xrefs, std::hash_Vector3d(), std::equal_to<Vector3d>());

    Array1D_Vector3 clusterdSamples = samples;

    if(samples.size() == 0)
        return samples;

    coordMe = timeAt(samples, threshold);
    coordOther = other.timeAt(samples, threshold);
    return samples;
}

template <typename Real>
void NURBSRectangle<Real>::generateSurfacePoints( Scalar stepSize, std::vector< std::vector<Vector3> > & points,
                                                  std::vector<Real> & valU, std::vector<Real> & valV )
{
    uniformCoordinates(valU, valV, stepSize);

    points.resize(valU.size(), std::vector<Vector3>(valV.size()));

    for(int y = 0; y < (int)valV.size(); y++)
    {
        for(int x = 0; x < (int)valU.size(); x++)
        {
            points[x][y] = P(valU[x], valV[y]);
        }
    }
}

template <typename Real>
Vector4d NURBSRectangle<Real>::timeAt( const Vector3 & pos )
{
    std::vector< std::vector<Vector3> > pts;

    std::vector<Real> valU, valV;
    Scalar stepSize = 0.01 * (mCtrlPoint.front().front() - mCtrlPoint.back().back()).norm();
    generateSurfacePoints(stepSize, pts, valU, valV);

    int minIdxU = 0, minIdxV = 0;
    Scalar minDist = std::numeric_limits<Scalar>::max();

    // Approximate area search
    for(int x = 0; x < (int)valU.size(); x++){
        for(int y = 0; y < (int)valV.size(); y++){
            Scalar dist = (pts[x][y] - pos).norm();

            if(dist < minDist){
                minDist = dist;
                minIdxU = x;
                minIdxV = y;
            }
        }
    }

    // More precise search
    Vector4d minRange( valU[qMax(0, minIdxU - 1)], valV[qMax(0, minIdxV - 1)], 0, 0);
    Vector4d maxRange( valU[qMin((int)valU.size() - 1, minIdxU + 1)], valV[qMin((int)valV.size() - 1, minIdxV + 1)], 0, 0);

    Vector4d bestUV( valU[minIdxU], valV[minIdxV], 0, 0 );

    return timeAt(pos, bestUV, minRange, maxRange, minDist);
}

template <typename Real>
Vector4d NURBSRectangle<Real>::timeAt( const Vector3 & pos, Vector4d & bestUV, Vector4d & minRange, Vector4d & maxRange, Real currentDist, Real threshold )
{
    // 1) Subdivide
    int searchSteps = 10;
    Scalar du = (maxRange[0] - minRange[0]) / searchSteps;
    Scalar dv = (maxRange[1] - minRange[1]) / searchSteps;

    // 2) Compare all distances
    Scalar curU = bestUV[0], curV = bestUV[1];
    Scalar minDist = currentDist;

    for(int y = 0; y <= searchSteps; y++)
    {
        for(int x = 0; x <= searchSteps; x++)
        {
            Scalar u = minRange[0] + (x * du);
            Scalar v = minRange[1] + (y * dv);

            Vector3 curPos = P(u,v);

            Scalar dist = (curPos - pos).norm();

            if(dist < minDist){
                minDist = dist;
                curU = u;
                curV = v;
            }
        }
    }

    // 3) If minimum distance == current or less than threshold return best match
    if(minDist >= currentDist)
        return bestUV;
    else if(minDist < threshold)
        return Vector4d(curU, curV, 0, 0);
    else
    {
        // 4) Otherwise recursive search in smaller range
        Vector4d minRange( qMax(0.0, curU - du), qMax(0.0, curV - dv), 0, 0 );
        Vector4d maxRange( qMin(1.0, curU + du), qMin(1.0, curV + dv), 0, 0 );

        Vector4d crd(curU, curV, 0, 0);

        return timeAt(pos, crd, minRange, maxRange, minDist, threshold);
    }
}

template <typename Real>
Array1D_Vector4d NURBSRectangle<Real>::timeAt( const std::vector<Vector3> & positions, Real threshold )
{
    Array1D_Vector4d times;

    std::vector< std::vector<Vector3> > pts;
    std::vector<Real> valU, valV;
    Scalar stepSize = 0.01 * (mCtrlPoint.front().front() - mCtrlPoint.back().back()).norm();
    generateSurfacePoints(stepSize, pts, valU, valV);

    for(int i = 0; i < (int)positions.size(); i++)
    {
        Vector3 pos = positions[i];

        int minIdxU = 0, minIdxV = 0;
        Scalar minDist = std::numeric_limits<Scalar>::max();

        // Approximate area search
        for(int x = 0; x < (int)valU.size(); x++){
            for(int y = 0; y < (int)valV.size(); y++){
                Scalar dist = (pts[x][y] - pos).norm();

                if(dist < minDist){
                    minDist = dist;
                    minIdxU = x;
                    minIdxV = y;
                }
            }
        }

        // More precise search
        Vector4d minRange( valU[qMax(0, minIdxU - 1)], valV[qMax(0, minIdxV - 1)], 0, 0);
        Vector4d maxRange( valU[qMin((int)valU.size() - 1, minIdxU + 1)], valV[qMin((int)valV.size() - 1, minIdxV + 1)], 0, 0);

        Vector4d bestUV( valU[minIdxU], valV[minIdxV], 0, 0 );

        times.push_back( timeAt(pos, bestUV, minRange, maxRange, minDist, threshold) );
    }

    return times;
}

template <typename Real>
Vector4d NURBSRectangle<Real>::fastTimeAt( const Vector3 & pos )
{
	std::vector< std::vector<Vector3> > pts;
	std::vector<Real> valU, valV;
	Scalar stepSize = 0.1 * (mCtrlPoint.front().front() - mCtrlPoint.back().back()).norm();
	generateSurfacePoints(stepSize, pts, valU, valV);
	int minIdxU = 0, minIdxV = 0;
	Scalar minDist = std::numeric_limits<Scalar>::max();

	// Approximate area search
	for(int x = 0; x < (int)valU.size(); x++){
		for(int y = 0; y < (int)valV.size(); y++){
			Scalar dist = (pts[x][y] - pos).norm();
			if(dist < minDist){
				minDist = dist;
				minIdxU = x;
				minIdxV = y;
			}
		}
	}

	if(!valU.size() || !valV.size()) return Vector4d(0,0,0,0);

	Vector4d bestUV( valU[minIdxU], valV[minIdxV], 0, 0 );
	return bestUV;
}

template <typename Real>
std::vector< std::vector<Vector3> > NURBSRectangle<Real>::triangulateControlCage()
{
    int width = GetNumCtrlPoints(0);
    int length = GetNumCtrlPoints(1);

    std::vector< std::vector<Vector3> > tris;
    std::vector<Vector3> emptyTri(3, Vector3(0,0,0));

    // Draw surface of control cage
    for(int j = 0; j < length - 1; j++)
    {
        for(int i = 0; i < width - 1; i++)
        {
            // Triangle 1
            tris.push_back(emptyTri);
            tris.back()[0] = GetControlPoint(i,j);
            tris.back()[1] = GetControlPoint(i+1,j);
            tris.back()[2] = GetControlPoint(i,j+1);

            // Triangle 2
            tris.push_back(emptyTri);
            tris.back()[0] = GetControlPoint(i+1,j);
            tris.back()[1] = GetControlPoint(i+1,j+1);
            tris.back()[2] = GetControlPoint(i,j+1);
        }
    }

    return tris;
}

template <typename Real>
Vector3 NURBSRectangle<Real>::projectOnControl( Real u, Real v )
{
    std::vector< std::vector<Vector3> > tris = NURBSRectangle::triangulateControlCage();

    Vector3 pos(0,0,0), t0(0,0,0), t1(0,0,0), normal(0,0,0);
    this->GetFrame(u,v, pos, t0,t1, normal);
    normal.normalize();

    QMap<double, Vector3d> hits;
    Vector3d isect(0,0,0);

    foreach( std::vector<Vector3> tri, tris )
    {
        if(intersectRayTri(tri, pos, normal, isect))
            hits[ (isect - pos).norm() ] = isect;
    }

    if(hits.size())
        return hits.values().front();
    else{
        qDebug() << "No intersection at u = " << u << ", v = " << v;
        return pos;
    }
}

template <typename Real>
void NURBSRectangle<Real>::translate( const Vector3d & delta )
{
    for(int y = 0; y < (int)mCtrlPoint.size(); y++)
        for(int x = 0; x < (int)mCtrlPoint[0].size(); x++)
            mCtrlPoint[y][x] += delta;
}

template <typename Real>
void NURBSRectangle<Real>::scale( Scalar scaleFactor )
{
    for(int y = 0; y < (int)mCtrlPoint.size(); y++)
        for(int x = 0; x < (int)mCtrlPoint[0].size(); x++)
            mCtrlPoint[y][x] *= scaleFactor;
}

template <typename Real>
Array2D_Vector3 NURBSRectangle<Real>::swapUV( const Array2D_Vector3 & controlPoints )
{
	Array2D_Vector3 tmp(controlPoints.front().size(), Array1D_Vector3(controlPoints.size(), Vector3(0,0,0)));

	for(int i = 0; i < (int)tmp.size(); i++)
		for(int j = 0; j < (int)tmp.front().size(); j++)
			tmp[i][j] = controlPoints[j][i];
	
	return tmp;
}

template <typename Real>
void NURBSRectangle<Real>::refineU( Array1D_Real & insknts, Array2D_Vector3 & Qw )
{
	Qw.clear();
	Qw.resize( mNumUCtrlPoints + insknts.size(), Array1D_Vector3(mNumVCtrlPoints, Vector3(0,0,0)) );

	for(int v = 0; v < mNumVCtrlPoints; v++)
	{
		NURBSCurve<Real> curveU = NURBSCurve<Real>::createCurveFromPoints( GetControlPointsU(v) );
		
		Array1D_Vector3 _Qw;
		Array1D_Real _Ubar;
		curveU.refine(insknts, _Qw, _Ubar);

		for(int i = 0; i < (int)_Qw.size(); i++)
			Qw[i][v] = _Qw[i];
	}
}

template <typename Real>
void NURBSRectangle<Real>::refine( Array1D_Real & insknts, Array2D_Vector3 & Qw, int dir )
{
	NURBSRectangle<Real> rect = *this;

	// Swap on V
	if(dir != 0) rect = NURBSRectangle<Real>::createSheetFromPoints( swapUV(rect.mCtrlPoint) );
	
	rect.refineU(insknts, Qw);
}

template <typename Real>
Array2D_Vector3 NURBSRectangle<Real>::midPointRefined()
{
	Array2D_Vector3 Qw = mCtrlPoint;

	std::vector<Real> inskntsU;
	Array1D_Real oldKnotVecU = GetKnotVectorU(true);
	for(int i = 0; i < (int)oldKnotVecU.size() - 1; i++){
		double range = (oldKnotVecU[i+1] - oldKnotVecU[i]);
		inskntsU.push_back( (0.5 * range) + oldKnotVecU[i] );
	}
	refine(inskntsU, Qw, 0);

	NURBSRectangle<Real> rectRefinedU = NURBSRectangle<Real>::createSheetFromPoints( Qw );

	std::vector<Real> inskntsV;
	Array1D_Real oldKnotVecV = rectRefinedU.GetKnotVectorV(true);
	for(int i = 0; i < (int)oldKnotVecV.size() - 1; i++){
		double range = (oldKnotVecV[i+1] - oldKnotVecV[i]);
		inskntsV.push_back( (0.5 * range) + oldKnotVecV[i] );
	}
	rectRefinedU.refine(inskntsV, Qw, 1);

	return swapUV(Qw);
}

template <typename Real>
Array1D_Real NURBSRectangle<Real>::GetKnotVectorU(bool isInnerOnly)
{
	BSplineBasis<Real> b = mBasis[0];
	if(isInnerOnly){
		int d = this->GetDegree(0);
		Array1D_Real result(b.mKnot.begin() + d, b.mKnot.end() - d);
		return result;
	}
	else
		return b.mKnot;
}

template <typename Real>
Array1D_Real NURBSRectangle<Real>::GetKnotVectorV(bool isInnerOnly)
{
	BSplineBasis<Real> b = mBasis[1];
	if(isInnerOnly){
		int d = this->GetDegree(1);
		Array1D_Real result(b.mKnot.begin() + d, b.mKnot.end() - d);
		return result;
	}
	else
		return b.mKnot;
}

template <typename Real>
Array2D_Vector3 NURBSRectangle<Real>::simpleRefine( int k, int dir )
{
	if(k <= 0) return this->mCtrlPoint;

	NURBSRectangle<Real> curRect = *this;

	for(int i = 0; i < k; i++)
	{
		Array1D_Real knotVector = curRect.GetKnotVectorU(true);
		if(dir == 1) knotVector = curRect.GetKnotVectorV(true);

		Array1D_Vector3 midSegments = curRect.GetControlPointsU( 0.5 * curRect.mNumVCtrlPoints );
		if(dir == 1) midSegments = curRect.GetControlPointsV( 0.5 * curRect.mNumUCtrlPoints );

		// Find longest segment
		int segment = 0;
		double maxDist = -DBL_MAX;
		for(int j = 0; j < (int)midSegments.size() - 1; j++)
		{
			double dist = (midSegments[j+1] - midSegments[j]).norm();
			if(dist > maxDist){
				maxDist = dist;
				segment = j;
			}
		}

		double t = double(segment) / (midSegments.size()-2);

		int idx = t * (knotVector.size()-2);
		double range = knotVector[idx+1] - knotVector[idx];
		double u = (0.5 * range) + knotVector[idx];

		Array2D_Vector3 newPts;

        Array1D_Real insk = Array1D_Real(1,u);
        curRect.refine(insk, newPts, dir);
		if(dir == 1) newPts = NURBSRectangle<Real>::swapUV( newPts );

		curRect = NURBSRectangle<Real>::createSheetFromPoints( newPts );
	}

	return curRect.mCtrlPoint;
}

template <typename Real>
Array2D_Vector3 NURBS::NURBSRectangle<Real>::simpleRemove( int idx, int dir )
{
	int nU = mNumUCtrlPoints;
	int nV = mNumVCtrlPoints;

	Array2D_Vector3 Qw( nU - (dir == 0 ? 1 : 0), Array1D_Vector3(nV - (dir == 1 ? 1 : 0), Vector3(0,0,0)) );

	int REMOVE_ROW = (dir == 0 ? idx : -1);
	int REMOVE_COLUMN = (dir == 1 ? idx : -1);

	int p = 0;
	for( int i = 0; i < nU; ++i){
		if (i == REMOVE_ROW) continue;

		int q = 0;
		for( int j = 0; j < nV; ++j){
			if (j == REMOVE_COLUMN)	continue;

			Qw[p][q] = mCtrlPoint[i][j];
			++q;
		}

		++p;
	}

	return Qw;
}


//----------------------------------------------------------------------------
// Explicit instantiation.
//----------------------------------------------------------------------------
//template
//class NURBSRectangle<float>;

template
class NURBSRectangle<double>;
//----------------------------------------------------------------------------
}
back to top