Raw File
NURBSCurve.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 "NURBSCurve.h"
#include "LineSegment.h"

namespace NURBS
{
//----------------------------------------------------------------------------
template <typename Real>
NURBSCurve<Real>::NURBSCurve (
    const Array1D_Vector3 & ctrlPoint, const Array1D_Real & ctrlWeight, int degree,
    bool loop, bool open)
    :
    SingleCurve<Real>((Real)0, (Real)1),
    mLoop(loop)
{
    int numCtrlPoints = ctrlPoint.size();

    assertion(numCtrlPoints >= 2, "Invalid input\n");
    assertion(1 <= degree && degree <= numCtrlPoints-1, "Invalid input\n");

    mNumCtrlPoints = numCtrlPoints;
    mReplicate = (loop ? (open ? 1 : degree) : 0);
    CreateControl(ctrlPoint, ctrlWeight);
    mBasis.Create(mNumCtrlPoints + mReplicate, degree, open);
}
//----------------------------------------------------------------------------
template <typename Real>
NURBSCurve<Real>::NURBSCurve (int numCtrlPoints,
    const Array1D_Vector3 & ctrlPoint, const Array1D_Real & ctrlWeight, int degree,
    bool loop, const Real* knot)
    :
    SingleCurve<Real>((Real)0, (Real)1),
    mLoop(loop)
{
    assertion(numCtrlPoints >= 2, "Invalid input\n");
    assertion(1 <= degree && degree <= numCtrlPoints-1, "Invalid input\n");

    mNumCtrlPoints = numCtrlPoints;
    mReplicate = (loop ? 1 : 0);
    CreateControl(ctrlPoint, ctrlWeight);
    mBasis.Create(mNumCtrlPoints + mReplicate, degree, knot);
}

//----------------------------------------------------------------------------
template <typename Real>
NURBSCurve<Real> NURBSCurve<Real>::createCurve( Vector3 from, Vector3 to, int steps )
{
    std::vector<Vector3> ctrlPoint;

    int degree = 3;

    Vector3 delta = (to - from) / steps;

    for(int i = 0; i <= steps; i++)
        ctrlPoint.push_back(from + (delta * i));

    std::vector<Real> ctrlWeight(ctrlPoint.size(), 1.0);
    return NURBSCurve(ctrlPoint, ctrlWeight, degree, false, true);
}
//----------------------------------------------------------------------------
template <typename Real>
void NURBSCurve<Real>::CreateControl (const Array1D_Vector3 & ctrlPoint, const Array1D_Real & ctrlWeight)
{
    int newNumCtrlPoints = mNumCtrlPoints + mReplicate;
    newNumCtrlPoints = newNumCtrlPoints;

    mCtrlPoint = ctrlPoint;

    mCtrlWeight = ctrlWeight;

    for (int i = 0; i < mReplicate; ++i)
    {
        mCtrlPoint[mNumCtrlPoints+i] = ctrlPoint[i];
        mCtrlWeight[mNumCtrlPoints+i] = ctrlWeight[i];
    }
}
//----------------------------------------------------------------------------
template <typename Real>
int NURBSCurve<Real>::GetNumCtrlPoints () const
{
    return mNumCtrlPoints;
}
//----------------------------------------------------------------------------
template <typename Real>
int NURBSCurve<Real>::GetDegree () const
{
    return mBasis.GetDegree();
}
//----------------------------------------------------------------------------
template <typename Real>
bool NURBSCurve<Real>::IsOpen () const
{
    return mBasis.IsOpen();
}
//----------------------------------------------------------------------------
template <typename Real>
bool NURBSCurve<Real>::IsUniform () const
{
    return mBasis.IsUniform();
}
//----------------------------------------------------------------------------
template <typename Real>
bool NURBSCurve<Real>::IsLoop () const
{
    return mLoop;
}
//----------------------------------------------------------------------------
template <typename Real>
void NURBSCurve<Real>::SetControlPoint (int i, const Vector3& ctrl)
{
    if (0 <= i && i < mNumCtrlPoints)
    {
        // Set the control point.
        mCtrlPoint[i] = ctrl;

        // Set the replicated control point.
        if (i < mReplicate)
        {
            mCtrlPoint[mNumCtrlPoints+i] = ctrl;
        }
    }
}
//----------------------------------------------------------------------------
template <typename Real>
Vector3 NURBSCurve<Real>::GetControlPoint (int i) const
{
    if (0 <= i && i < mNumCtrlPoints)
    {
        return mCtrlPoint[i];
    }

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

        // Set the replicated control weight.
        if (i < mReplicate)
        {
            mCtrlWeight[mNumCtrlPoints+i] = weight;
        }
    }
}
//----------------------------------------------------------------------------
template <typename Real>
Real NURBSCurve<Real>::GetControlWeight (int i) const
{
    if (0 <= i && i < mNumCtrlPoints)
    {
        return mCtrlWeight[i];
    }

    return std::numeric_limits<Real>::max();
}
//----------------------------------------------------------------------------
template <typename Real>
void NURBSCurve<Real>::SetKnot (int i, Real knot)
{
    mBasis.SetKnot(i, knot);
}
//----------------------------------------------------------------------------
template <typename Real>
Real NURBSCurve<Real>::GetKnot (int i) const
{
    return mBasis.GetKnot(i);
}
//----------------------------------------------------------------------------
template <typename Real>
void NURBSCurve<Real>::Get (Real t, Vector3* pos,
    Vector3* der1, Vector3* der2, Vector3* der3)
{
    int i, imin, imax;
    if (der3)
    {
        mBasis.Compute(t, 0, imin, imax);
        mBasis.Compute(t, 1, imin, imax);
        mBasis.Compute(t, 2, imin, imax);
        mBasis.Compute(t, 3, imin, imax);
    }
    else if (der2)
    {
        mBasis.Compute(t, 0, imin, imax);
        mBasis.Compute(t, 1, imin, imax);
        mBasis.Compute(t, 2, imin, imax);
    }
    else if (der1)
    {
        mBasis.Compute(t, 0, imin, imax);
        mBasis.Compute(t, 1, imin, imax);
    }
    else  // pos
    {
        mBasis.Compute(t, 0, imin, imax);
    }

    Real tmp;

    // Compute position.
    Vector3 X = Vector3_ZERO;
    Real w = (Real)0;
    for (i = imin; i <= imax; ++i)
    {
        tmp = mBasis.GetD0(i)*mCtrlWeight[i];
        X += tmp*mCtrlPoint[i];
        w += tmp;
    }
    Real invW = ((Real)1)/w;
    Vector3 P = invW*X;
    if (pos)
    {
        *pos = P;
    }

    if (!der1 && !der2 && !der3)
    {
        return;
    }

    // Compute first derivative.
    Vector3 XDer1 = Vector3_ZERO;
    Real wDer1 = (Real)0;
    for (i = imin; i <= imax; ++i)
    {
        tmp = mBasis.GetD1(i)*mCtrlWeight[i];
        XDer1 += tmp*mCtrlPoint[i];
        wDer1 += tmp;
    }
    Vector3 PDer1 = invW*(XDer1 - wDer1*P);
    if (der1)
    {
        *der1 = PDer1;
    }

    if (!der2 && !der3)
    {
        return;
    }

    // Compute second derivative.
    Vector3 XDer2 = Vector3_ZERO;
    Real wDer2 = (Real)0;
    for (i = imin; i <= imax; ++i)
    {
        tmp = mBasis.GetD2(i)*mCtrlWeight[i];
        XDer2 += tmp*mCtrlPoint[i];
        wDer2 += tmp;
    }
    Vector3 PDer2 = invW*(XDer2 - ((Real)2)*wDer1*PDer1 - wDer2*P);
    if (der2)
    {
        *der2 = PDer2;
    }

    if (!der3)
    {
        return;
    }

    // Compute third derivative.
    Vector3 XDer3 = Vector3_ZERO;
    Real wDer3 = (Real)0;
    for (i = imin; i <= imax; ++i)
    {
        tmp = mBasis.GetD3(i)*mCtrlWeight[i];
        XDer3 += tmp*mCtrlPoint[i];
        wDer3 += tmp;
    }
    if (der3)
    {
        *der3 = invW*(XDer3 - ((Real)3)*wDer1*PDer2 -
            ((Real)3)*wDer2*PDer1 - wDer3*P);
    }
}
//----------------------------------------------------------------------------
template <typename Real>
BSplineBasis<Real>& NURBSCurve<Real>::GetBasis ()
{
    return mBasis;
}
//----------------------------------------------------------------------------
template <typename Real>
Vector3 NURBSCurve<Real>::GetPosition (Real t)
{
    Vector3 pos;
    Get(t, &pos, 0, 0, 0);
    return pos;
}
//----------------------------------------------------------------------------
template <typename Real>
Vector3 NURBSCurve<Real>::GetFirstDerivative (Real t)
{
    Vector3 der1;
    Get(t, 0, &der1, 0, 0);
    return der1;
}
//----------------------------------------------------------------------------
template <typename Real>
Vector3 NURBSCurve<Real>::GetSecondDerivative (Real t)
{
    Vector3 der2;
    Get(t, 0, 0, &der2, 0);
    return der2;
}
//----------------------------------------------------------------------------
template <typename Real>
Vector3 NURBSCurve<Real>::GetThirdDerivative (Real t)
{
    Vector3 der3;
    Get(t, 0, 0, 0, &der3);
    return der3;
}
//----------------------------------------------------------------------------
template <typename Real>
std::vector<Vector3> NURBSCurve<Real>::getControlPoints()
{
    return mCtrlPoint;
}

template <typename Real>
Real NURBSCurve<Real>::timeAt( const Vector3 & pos )
{
	int segmentCount = 100;

	std::vector<Real> times;
	this->SubdivideByLengthTime(segmentCount, times);

    int minIdx = 0;
    double t = 0.0;
    Vector3 d(0,0,0);
    Scalar minDist = std::numeric_limits<Scalar>::max();

    for(int i = 0; i < ((int)times.size()) - 1; i++)
    {
        Line segment(this->GetPosition(times[i]), this->GetPosition(times[i+1]));
        segment.ClosestPoint(pos, t, d);

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

        if(dist < minDist){
            minDist = dist;
            minIdx = i;
        }
    }

	Line closestSegment(this->GetPosition(times[minIdx]), this->GetPosition(times[minIdx+1]));

    closestSegment.ClosestPoint(pos, t, d);
    return qRanged(0.0, ((1-t) * times[minIdx]) + (t * times[minIdx+1]), 1.0);
}

template <typename Real>
Real NURBSCurve<Real>::fastTimeAt( const Vector3 & pos )
{
	int segmentCount = 6;
	std::vector<Real> times;
	this->SubdivideByLengthTime(segmentCount, times);
	int minIdx = 0;
	double t = 0.0;
	Vector3 d(0,0,0);
	Scalar minDist = std::numeric_limits<Scalar>::max();
	for(int i = 0; i < ((int)times.size()) - 1; i++){
		Line segment(this->GetPosition(times[i]), this->GetPosition(times[i+1]));
		segment.ClosestPoint(pos, t, d);
		Scalar dist = (pos - d).norm();
		if(dist < minDist){
			minDist = dist;
			minIdx = i;
		}
	}
	Line closestSegment(this->GetPosition(times[minIdx]), this->GetPosition(times[minIdx+1]));
	closestSegment.ClosestPoint(pos, t, d);
	return qRanged(0.0, ((1-t) * times[minIdx]) + (t * times[minIdx+1]), 1.0);
}

template <typename Real>
void NURBSCurve<Real>::translate( const Vector3 & delta )
{
    for(int i = 0; i < (int)mCtrlPoint.size(); i++)
        mCtrlPoint[i] += delta;
}

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

template <typename Real>
void NURBSCurve<Real>::scaleInPlace( Scalar scaleFactor, int placeCtrlPoint )
{
    Vector3 delta = mCtrlPoint[placeCtrlPoint];
    translate( -delta );
    scale(scaleFactor);
    translate( delta );
}

template <typename Real>
void NURBSCurve<Real>::translateTo( const Vector3 & newPos, int cpIDX )
{
    Vector3d cp = mCtrlPoint[cpIDX];
    Vector3d delta = newPos - cp;
    for(int i = 0; i < GetNumCtrlPoints(); i++)
        mCtrlPoint[i] += delta;
}

template <typename Real>
std::vector < std::vector<Vector3> > NURBSCurve<Real>::toSegments( Scalar resolution )
{
    std::vector< std::vector<Vector3> > segments;


    double firstTwoDist = (mCtrlPoint[0] - mCtrlPoint[1]).norm();
    if(firstTwoDist < resolution * 0.0001){
        segments.push_back(this->mCtrlPoint);
        return segments;
    }

    Scalar curveLength = this->GetLength(0,1);

    // For singular cases
    if(curveLength < resolution){
        segments.push_back(this->mCtrlPoint);
        return segments;
    }

    // Get a curve polyline for given resolution
    int np = 1 + (curveLength / resolution);

    std::vector<Vector3> pts;
    this->SubdivideByLength(np, pts);

    for(int i = 0; i < np - 1; i++)
    {
        std::vector<Vector3> segment;

        segment.push_back(pts[i]);
        segment.push_back(pts[i+1]);

        segments.push_back(segment);
    }

    return segments;
}

template <typename Real>
Array1D_Real NURBSCurve<Real>::GetKnotVector(bool isInnerOnly)
{
	if(isInnerOnly)
	{
		int d = this->GetDegree();

		Array1D_Real result(mBasis.mKnot.begin() + d, mBasis.mKnot.end() - d);
		
		return result;
	}
	else
		return this->mBasis.mKnot;
}

template <typename Real>
int NURBSCurve<Real>::findSpan(int n, int p, Real u, Array1D_Real U)
{
	int low, high, mid;

	if (u == U[n+1]) return(n);

	low = p;
	high = n+1;
	mid = (int)(low+high)/2;
	while (u < U[mid] || u >= U[mid+1]) 
	{
		if (u < U[mid]) 
			high = mid;
		else low = mid;
		mid = (int)(low+high)/2;
	}
	return mid;
}

template <typename Real>
int NURBSCurve<Real>::findSpan(Real u) 
{
	return findSpan(this->mCtrlPoint.size() - 1, GetDegree(), u, GetKnotVector() );
}

template <typename Real>
Array1D_Vector3 NURBS::NURBSCurve<Real>::midPointRefined()
{
	Array1D_Vector3 newCtrlPoints;
	Array1D_Real Ubar;
	computeMidPointRefine(newCtrlPoints, Ubar);
	return newCtrlPoints;
}

template <typename Real>
void NURBSCurve<Real>::computeMidPointRefine( Array1D_Vector3 & Qw, Array1D_Real & Ubar )
{
	std::vector<Real> insknts;

	Array1D_Real oldKnotVec = GetKnotVector(true);

	for(int i = 0; i < (int)oldKnotVec.size() - 1; i++){
		double range = (oldKnotVec[i+1] - oldKnotVec[i]);
		insknts.push_back( (0.5 * range) + oldKnotVec[i] );
	}

	refine(insknts, Qw, Ubar);
}

template <typename Real>
void NURBSCurve<Real>::refine(Array1D_Real & insknts, Array1D_Vector3 & Qw, Array1D_Real & Ubar )
{
	// Input
	Array1D_Real X		= insknts;
	Array1D_Vector3 Pw	= this->mCtrlPoint;
	Array1D_Real U		= GetKnotVector();

	int s = insknts.size();
	int order = this->GetDegree() + 1;
	int p = order - 1;
	int n = Pw.size() - 1;
	int r = X.size() - 1;
	int m = n+p+1;

	// Output
	Qw.resize	( n+s+1, Vector3(0,0,0) );
	Ubar.resize	( m+s+1, 0 );

	int a = findSpan(n,p,X[0],U);
	int b = findSpan(n,p,X[r],U) + 1;
	
	int j = 0;
	for(j=0	 ; j<=a-p; j++)  Qw[j]		 = Pw[j];
	for(j=b-1; j<=n	 ; j++)  Qw[j+r+1]	 = Pw[j];
	for(j=0	 ; j<=a	 ; j++)  Ubar[j]	 = U[j];
	for(j=b+p; j<=m	 ; j++)  Ubar[j+r+1] = U[j];

	int i = b+p-1;
	int k = b+p+r;

	Real alfa = 0;

	for (int j=r; j>=0; j--) 
	{
		while (X[j] <= U[i] && i > a) {
			Qw[k-p-1] = Pw[i-p-1];
			Ubar[k] = U[i];
			k = k - 1;
			i = i - 1;
		}

		Qw[k-p-1] = Qw[k-p];

		for (int l=1; l<=p; l++) 
		{
			int ind = k-p+l;
			alfa = Ubar[k+l] - X[j];
            if (fabs(alfa) == 0.0)
			{
				Qw[ind-1] = Qw[ind];
			}
			else 
			{
				alfa = alfa / (Ubar[k+l] - U[i-p+l]);
				Qw[ind-1] = alfa * Qw[ind-1] + (1.0 - alfa) * Qw[ind];
			}
		}
		Ubar[k] = X[j];
		k = k - 1;
	}
}

/*	@param u           Parametric coordinate of breaking point
	@param k           Knot index of insertion point
	@param s           Knot multiplicity at k
	@param r           Number of times a knot should be inserted */
template <typename Real>
Array1D_Real NURBSCurve<Real>::insertKnot(Real u, int k, int s, int r, Array1D_Vector3 & Qw, int * uq)
{
	Array1D_Real	UP = GetKnotVector();
	Array1D_Vector3 Pw = mCtrlPoint;
	Array1D_Real	UQ;

	Array1D_Vector3 Rw;
	Scalar alpha;
	int L = 0;

	int order = this->GetDegree() + 1;
	int p = order - 1;
	int np = Pw.size() - 1;

	int mp = np+p+1;
	int nq = np+r;

	// Define array
	UQ.resize(mp+r+1);
	Qw.resize(nq+1);
	Rw.resize(p+1);

	// Load new knot vector
	for (int i=0; i<=k; i++)	UQ[i]	= UP[i];
	for (int i=1; i<=r; i++)	UQ[k+i] = u;
	for (int i=k+1; i<=mp; i++)	UQ[i+r] = UP[i];

	// Save unaltered control points
	for (int i=0; i<=k-p; i++)	Qw[i]	= Pw[i];
	for (int i=k-s; i<=np; i++)	Qw[i+r] = Pw[i];
	for (int i=0; i<=p-s; i++)	Rw[i]	= Pw[k-p+i];

	// Insert the knot r times
	for (int j=1; j<=r; j++) 
	{
		L = k-p+j;
		for (int i=0; i<=p-j-s; i++) 
		{
			alpha = (u-UP[L+i])/(UP[i+k+1]-UP[L+i]);
			Rw[i] = alpha*Rw[i+1] + (1.0 - alpha) * Rw[i];
		}
		Qw[L] = Rw[0];
		Qw[k+r-j-s] = Rw[p-j-s];
	}

	// Load remaining control points
	for (int i=L+1; i<k-s; i++)
		Qw[i] = Rw[i-L];

	if(uq) *uq = k-p+1;

	return UQ;
}

template <typename Real>
Array1D_Vector3 NURBSCurve<Real>::simpleRefine( int k )
{
	if(k <= 0) return this->mCtrlPoint;

	NURBSCurve<Real> curCurve = *this;

	for(int i = 0; i < k ; i++)
	{
		int segment = 0;
		double maxDist = -DBL_MAX;

		// Find longest segment
		for(int j = 0; j < (int)(curCurve.mCtrlPoint.size() - 1); j++)
		{
			double dist = (curCurve.mCtrlPoint[j+1] - curCurve.mCtrlPoint[j]).norm();

			if(dist > maxDist){
				maxDist = dist;
				segment = j;
			}
		}

		// Insert knot at middle of longest segment
		Array1D_Real U = curCurve.GetKnotVector(true);

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

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

		Array1D_Vector3 newPnts;
		Array1D_Real Ubar;
        Array1D_Real insk = Array1D_Real(1,u);
        curCurve.refine(insk, newPnts, Ubar);

		// Update control points
		curCurve = NURBSCurve<Real>::createCurveFromPoints(newPnts);
	}

	return Array1D_Vector3(curCurve.mCtrlPoint.begin(),curCurve.mCtrlPoint.end());
}

template <typename Real>
Real NURBSCurve<Real>::KnotRemovalError( int r, int s )
{
	Array1D_Real	U = GetKnotVector();
	Array1D_Vector3 P = mCtrlPoint;

	Array1D_Vector3 temp(P.size(), Vector3(0,0,0)) ;
	
	int deg_ = GetDegree();

	int ord = deg_+1 ;
	int last = r-s ;
	int first = r-deg_ ;
	int off ; 
	int i,j,ii,jj ;
	Real alfi,alfj ;
	Real u ;

	u = U[r] ;

	off = first-1;
	temp[0] = P[off] ;
	temp[last+1-off] = P[last+1] ;

	i=first ; j=last ;
	ii=1 ; jj=last-off ;

	while(j-i>0){
		alfi = (u-U[i])/(U[i+ord]-U[i]) ;
		alfj = (u-U[j])/(U[j+ord]-U[j]) ;
		temp[ii] = (P[i]-(1.0-alfi)*temp[ii-1])/alfi ; 
		temp[jj] = (P[j]-alfj*temp[jj+1])/(1.0-alfj) ;
		++i ; ++ii ;
		--j ; --jj ;
	}
	if(j-i<0){
		return (temp[ii-1]-temp[jj+1]).norm() ;
	}
	else{
		alfi=(u-U[i])/(U[i+ord]-U[i]) ;
		return (P[i]-alfi*temp[ii+1]+(1.0-alfi)*temp[ii-1]).norm() ;
	}

}

template <typename Real>
Array1D_Vector3 NURBSCurve<Real>::removeKnots(int iterations)
{
	NURBSCurve<Real> curCurve = *this;

	for(int itr = 0; itr < iterations; itr++)
	{
		Array1D_Real	U = curCurve.GetKnotVector();
		Array1D_Vector3 P = curCurve.mCtrlPoint;
		int p = curCurve.GetDegree();

		QMap<Real, int> errors;
		for(int i = p + 1; i < (int)U.size() - 1; i++)
		{
			if(U[i] < U[i+1])
				errors[ curCurve.KnotRemovalError(i, 1) ] = i;
		}

		int r = errors.values().at(itr); // Knot to remove

		int num = 1;
		int s = 1;

		int deg_ = curCurve.GetDegree();
		int m = U.size() ;
		int ord = deg_+1 ;
		int fout = (2*r-s-deg_)/2 ;
		int last = r-s ;
		int first = r-deg_ ;
		Real alfi, alfj ;
		int i,j,k,ii,jj,off ;
		Real u ;

		Array1D_Vector3 temp( 2*deg_+1, Vector3(0,0,0) ) ;

		u = U[r] ;

		int t;
		for(t=0;t<num;++t)
		{
			off = first-1 ;
			temp[0] = P[off] ;
			temp[last+1-off] = P[last+1] ;
			i = first; j = last ;
			ii = 1 ; jj = last-off ;
			while(j-i > t){
				alfi = (u-U[i])/(U[i+ord+t]-U[i]) ;
				alfj = (u-U[j-t])/(U[j+ord]-U[j-t]) ;
				temp[ii] = (P[i]-(1.0-alfi)*temp[ii-1])/alfi ;
				temp[jj] = (P[j]-alfj*temp[jj+1])/(1.0-alfj) ;
				++i ; ++ii ;
				--j ; --jj ;
			}
			i = first ; j = last ;
			while(j-i>t){
				P[i] = temp[i-off] ;
				P[j] = temp[j-off] ;
				++i; --j ;
			}
			--first ; ++last ;
		}
		if(t == 0)
			return P;

		for(k=r+1; k<m ; ++k)
			U[k-t] = U[k] ;
		j = fout ; i=j ; // Pj thru Pi will be overwritten
		for(k=1; k<t; k++)
			if( (k%2) == 1)
				++i ;
			else
				--j ;
		for(k=i+1; k<(int)P.size() ; k++) {// Shift
			P[j++] = P[k] ; 
		}

		P.resize(P.size() - t);
		curCurve = createCurveFromPoints(P);
	}

	return curCurve.mCtrlPoint;
}

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

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