/*
 * NodeNurbsCurve.cpp
 *
 * Copyright (C) 2003 Th. Rothermel
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file "COPYING" for details); if 
 * not, write to the Free Software Foundation, Inc., 675 Mass Ave, 
 * Cambridge, MA 02139, USA.
 */

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#ifndef _WIN32
# include "stdlib.h"
#endif

#include "stdafx.h"
#include "NodeNurbsCurve.h"
#include "Scene.h"
#include "MoveCommand.h"
#include "FieldValue.h"
#include "SFInt32.h"
#include "MFFloat.h"
#include "MFInt32.h"
#include "MFVec2f.h"
#include "MFVec3f.h"
#include "SFNode.h"
#include "SFBool.h"
#include "SFVec3f.h"
#include "Vec2f.h"
#include "Vec3f.h"
#include "RenderState.h"
#include "DuneApp.h"
#include "NodeCoordinate.h"
#include "NodeNormal.h"
#include "NodeTextureCoordinate.h"
#include "NodeNurbsSurface.h"
#include "NurbsMakeRevolvedSurface.h"
#include "NurbsCurveDegreeElevate.h"
#include "Util.h"
#include "NodeIndexedLineSet.h"
#include "NodePositionInterpolator.h"
#include "NodeOrientationInterpolator.h"
#include "NodeSuperExtrusion.h"
#include "NodeSuperRevolver.h"
#include "resource.h"

ProtoNurbsCurve::ProtoNurbsCurve(Scene *scene)
  : ChainBasedProto(scene, "NurbsCurve")
{
    controlPoint.set(
          addExposedField(MFVEC3F, "controlPoint", new MFVec3f()));
    setFieldFlags(controlPoint, FF_VRML_ONLY);
    controlPointX3D.set(
          addExposedField(SFNODE, "controlPoint", new SFNode(NULL), 
                          COORDINATE_NODE));
    setFieldFlags(controlPointX3D, FF_X3D_ONLY);
    tessellation.set(
          addExposedField(SFINT32, "tessellation", new SFInt32(0)));
    weight.set(
          addExposedField(MFFLOAT, "weight", new MFFloat(), new SFFloat(0.0f)));
    closed.set(
          addField(SFBOOL, "closed", new SFBool(false)));
    setFieldFlags(closed, FF_X3D_ONLY);
    knot.set(
          addField(MFFLOAT, "knot", new MFFloat()));
    order.set(
          addField(SFINT32, "order", new SFInt32(3), new SFInt32(2)));
}

Node *
ProtoNurbsCurve::create(Scene *scene)
{
    return new NodeNurbsCurve(scene, this); 
}

NodeNurbsCurve::NodeNurbsCurve(Scene *scene, Proto *proto)
  : ChainBasedNode(scene, proto)
{
    _isInternal = false;
    _x3d = scene->isX3d();
}

MFVec3f *
NodeNurbsCurve::getControlPoints(void)
{
    if (_x3d) {
        Node *pointNode = controlPointX3D()->getValue();
        if (pointNode)
            return ((NodeCoordinate *)pointNode)->point();
        else
            return NULL;
    }
    return controlPoint();
}

void
NodeNurbsCurve::setControlPoints(const MFVec3f *points)
{
    if (_x3d) {
        NodeCoordinate *coord = (NodeCoordinate *)controlPointX3D()->getValue();
        if (coord == NULL) {
            createControlPoints(points);
            return;
        }
        _scene->setField(coord, coord->point_Field(), new MFVec3f(points));
    } else
        _scene->setField(this, controlPoint_Field(), new MFVec3f(points));
}

void
NodeNurbsCurve::createControlPoints(const MFVec3f *points)
{
    if (_x3d)
        controlPointX3D(new SFNode(_scene->createNode("Coordinate"))); 
    setControlPoints(points);
}

void
NodeNurbsCurve::createChain()
{
    if (getControlPoints() == NULL)
        return;
    int iTess = tessellation()->getValue();
    float *weights = NULL;
    int iDimension = getControlPoints()->getSize() / 3;
    int i;

    if (weight()->getSize() == 0) {
        weights = new float[iDimension];
        for (i=0; i<iDimension; i++){
            weights[i] = 1.0f;
        }
    } 
    else if(weight()->getSize() != iDimension) {
        return;
    }
    
    if (iTess<=0)
        iTess = TheApp->getTessellation();

    int size = iTess + 1;
    Vec3f *tess = new Vec3f[size];
    
    const float *knots = knot()->getValues();
    
    float inc = (knots[knot()->getSize()-1] - knots[0]) / iTess;
    
    float u;
    
    const float *w = weights ? weights : weight()->getValues();
    
    for (i = 0, u = knots[0]; i <= iTess; i++, u = u + inc) {
        tess[i] = curvePoint(iDimension, order()->getValue(), knots, 
                             (const Vec3f *) getControlPoints()->getValues(),
                             w, u);
    }

    _chain.resize(size);  
    for (i=0; i<size; i++)
        _chain[i] = tess[i];
}  
  

void
NodeNurbsCurve::drawHandles()
{
    if (getControlPoints() == NULL)
        return;
    int iDimension = getControlPoints()->getSize() / 3;
    RenderState state;

    if (weight()->getSize() != iDimension) {
        return;
    }

    glPushName(iDimension + 1);
    glDisable(GL_LIGHTING);
    Util::myGlColor3f(1.0f, 1.0f, 1.0f);
    glLoadName(NO_HANDLE);
    glBegin(GL_LINE_STRIP);
    for (int i = 0; i < iDimension; i++) {
        const float *v = getControlPoints()->getValue(i);
        float w = weight()->getValue(i);
        glVertex3f(v[0] / w, v[1] / w, v[2] / w);
    }
    glEnd();
    
    int ci;

    state.startDrawHandles();
    for (ci = 0; ci < iDimension; ci++) {
        state.setHandleColor(_scene, ci);
        glLoadName(ci);
        state.drawHandle(Vec3f(getControlPoints()->getValue(ci)) / 
                               weight()->getValue(ci));
    }
    state.endDrawHandles();
    glPopName();
    glEnable(GL_LIGHTING);
}


Vec3f
NodeNurbsCurve::getHandle(int handle, int *constraint, int *field)
{
    *field = controlPoint_Field() ;

    if (handle >= 0 && handle < getControlPoints()->getSize() / 3) {
        Vec3f ret((Vec3f)getControlPoints()->getValue(handle) / 
                   weight()->getValue(handle));
        TheApp->PrintMessageWindowsVertex(IDS_VERTEX_SELECTED, "controlPoint", 
                                          handle);
        return ret;
    } else {
        *field = -1;
        return Vec3f(0.0f, 0.0f, 0.0f);
    }
}

void
NodeNurbsCurve::setHandle(int handle, const Vec3f &v)
{
    MFVec3f *newValue = new MFVec3f(*getControlPoints());

    float epsilon = TheApp->GetHandleEpsilon();
    int numPoints = getControlPoints()->getSize() / 3; 
    if (handle >= 0 && handle < numPoints) {
        float w = weight()->getValue(handle);
        Vec3f oldV = getControlPoints()->getValue(handle);
        if (w != 0)
            oldV = oldV / w;
        Vec3f newV = v * w;
        if (_scene->getXSymetricMode() && (fabs(oldV.x) < epsilon))
            newValue->setValue(handle * 3, 0);
        else
            newValue->setValue(handle * 3, newV.x);
        newValue->setValue(handle * 3+1, newV.y);
        newValue->setValue(handle * 3+2, newV.z);
        // set other handles for symetric modelling
        // which also snap handles at the same place
        setHandle(newValue, handle, w, newV, oldV, true);
    }
    if (_isInternal && hasParent())
        getParent()->update();
}

void
NodeNurbsCurve::update()
{
    _chainDirty = true;
    if (_isInternal && hasParent())
        getParent()->update();
}


void
NodeNurbsCurve::setHandle(MFVec3f *value, int handle, float newWeight,
                            const Vec3f &newV, const Vec3f &oldV,
                            bool already_changed)
{
    bool changed = false;
    MFVec3f *newValue = (MFVec3f *) value->copy();

    if (_scene->getXSymetricMode()) {
        float epsilon = TheApp->GetHandleEpsilon();
        int numPoints = newValue->getSize() / 3; 
        for (int i = 0; i < numPoints; i++) {
            if (i != handle) {
                Vec3f vPoint = getControlPoints()->getValue(i);
                float wPoint = weight()->getValue(i);
                float w = wPoint;
                if (wPoint != 0)
                   vPoint = vPoint / wPoint;
                if (newWeight != 0)
                   w = w / newWeight;
                if (   (fabs(vPoint.z - oldV.z) < epsilon) 
                    && (fabs(vPoint.y - oldV.y) < epsilon)) {
                    if (fabs(vPoint.x + oldV.x) < epsilon) {
                        changed = true;
                        if (fabs(oldV.x) < epsilon)
                            newValue->setValue(i * 3,   0);
                        else
                            newValue->setValue(i * 3,   - newV.x * w);
                        newValue->setValue(i * 3+1,   newV.y * w);
                        newValue->setValue(i * 3+2,   newV.z * w);
                    } else if (fabs(vPoint.x - oldV.x) < epsilon) {
                        changed = true;
                        if (fabs(oldV.x) < epsilon)
                            newValue->setValue(i * 3,   0);
                        else
                            newValue->setValue(i * 3,   newV.x * w);
                        newValue->setValue(i * 3+1, newV.y * w);
                        newValue->setValue(i * 3+2, newV.z * w);
                    }         
                }                 
            }
        }
    }
    if (already_changed)
        changed = true;

    if (changed) {
        if (_x3d) {
            NodeCoordinate *pointNode = (NodeCoordinate *)
                                        controlPointX3D()->getValue();
            if (pointNode == NULL)
                createControlPoints(newValue);
            else
                _scene->setField(pointNode, pointNode->point_Field(), newValue);
        } else
            _scene->setField(this, controlPoint_Field(), newValue);
        _chainDirty = true;
    }
}

void
NodeNurbsCurve::setHandle(float newWeight, 
                            const Vec3f &newV, const Vec3f &oldV)
{
    setHandle(controlPoint(), -1, newWeight, newV, oldV);
}

int
NodeNurbsCurve::findSpan(int dimension, int order, float u, const float knots[])
{
    int low, mid, high;
    int n = dimension + order - 1;

    if (u >= knots[n]) {
        return n - order;
    }
    low = order - 1;
    high = n - order + 1;

    mid = (low + high) / 2;

    int oldLow = low;
    int oldHigh = high;
    int oldMid = mid;
    while (u < knots[mid] || u >= knots[mid+1]) {
        if (u < knots[mid]) {
            high = mid;
        } else {
            low = mid;
        }
        mid = (low+high)/2;
        // emergency abort of loop, otherwise a endless loop can occure
        if ((low == oldLow) && (high == oldHigh) && (mid == oldMid))
            break;

        oldLow = low;
        oldHigh = high;
        oldMid = mid;
    }
    return mid;
}

void
NodeNurbsCurve::basisFuns(int span, float u, int order, const float knots[], 
                          float basis[], float deriv[])
{
    float *left = (float *) malloc(order * sizeof(float));
    float *right = (float *) malloc(order * sizeof(float));

    if ((left==NULL) || (right==NULL))
       return;
    basis[0] = 1.0f;
    for (int j = 1; j < order; j++) {
        left[j] = u - knots[span+1-j];
        right[j] = knots[span+j]-u;
        float saved = 0.0f, dsaved = 0.0f;
        for (int r = 0; r < j; r++) {
            float temp = basis[r] / (right[r+1] + left[j-r]);
            basis[r] = saved + right[r+1] * temp;
            deriv[r] = dsaved - j * temp;
            saved = left[j-r] * temp;
            dsaved = j * temp;
        }
        basis[j] = saved;
        deriv[j] = dsaved;
    }
    free(left);
    free(right);
}

Vec3f
NodeNurbsCurve::curvePoint(int dimension, int order, const float knots[],
                           const Vec3f controlPoints[],
                           const float weight[], float u)
{
    int i; 
    float *basis = (float *) malloc(order * sizeof(float));
    float *deriv = (float *) malloc(order * sizeof(float));

    if((basis==NULL) || (deriv==NULL)){
      return NULL;
    }
    
    int span = findSpan(dimension, order, u, knots);

    basisFuns(span, u, order, knots, basis, deriv);

    Vec3f C(0.0f, 0.0f, 0.0f);
    float w = 0.0f;
    for(i=0; i<order; i++){
      C += controlPoints[span-order+1+i] * basis[i];
      w += weight[span-order+1+i] * basis[i];
    }   
    
    C = C / w;

    free(basis);
    free(deriv);
    
    return C;
}


int
NodeNurbsCurve::writeProto(int f)
{
    RET_ONERROR( mywritestr(f ,"EXTERNPROTO NurbsCurve[\n") )    
    TheApp->incSelectionLinenumber();
    RET_ONERROR( writeProtoArguments(f) )
    RET_ONERROR( mywritestr(f ," ]\n") )
    TheApp->incSelectionLinenumber();
    RET_ONERROR( mywritestr(f ,"[\n") )
    TheApp->incSelectionLinenumber();
    RET_ONERROR( mywritestr(f ," \"urn:web3d:vrml97:node:NurbsCurve\",\n") )
    TheApp->incSelectionLinenumber();
    RET_ONERROR( mywritestr(f ," \"urn:inet:blaxxun.com:node:NurbsCurve\",\n") )
    TheApp->incSelectionLinenumber();
    RET_ONERROR( mywritestr(f ," \"urn:ParaGraph:NurbsCURVE\",\n") )
    TheApp->incSelectionLinenumber();
#ifdef HAVE_VRML97_AMENDMENT1_PROTO_URL
    RET_ONERROR( mywritestr(f ," \"") )
    RET_ONERROR( mywritestr(f ,HAVE_VRML97_AMENDMENT1_PROTO_URL) )
    RET_ONERROR( mywritestr(f ,"/NurbsCurvePROTO.wrl") )
    RET_ONERROR( mywritestr(f ,"\"\n") )
    TheApp->incSelectionLinenumber();
#else
    RET_ONERROR( mywritestr(f ," \"NurbsCurvePROTO.wrl\",\n") )
    TheApp->incSelectionLinenumber();
#endif
    RET_ONERROR( mywritestr(f ," \"http://129.69.35.12/dune/docs/vrml97Amendment1/NurbsCurvePROTO.wrl\"\n") )
    TheApp->incSelectionLinenumber();
    RET_ONERROR( mywritestr(f ,"]\n") )
    TheApp->incSelectionLinenumber();
    return 0;
}

int             
NodeNurbsCurve::write(int filedes, int indent)
{
    if (_scene->isPureVRML()) {
        Node * node = toIndexedLineSet();
        RET_ONERROR( node->write(filedes, indent) )
        node->unref();
    } else
        RET_ONERROR( NodeData::write(filedes, indent) )
    return 0;
}

Node*
NodeNurbsCurve::toNurbs(int narcs, int pDegree, float rDegree, 
                        Vec3f &P1, Vec3f &P2)
{
  NodeNurbsSurface *node = (NodeNurbsSurface *)
                            _scene->createNode("NurbsSurface");
  
  int i;
  Vec3f *tmpControlPoints = new Vec3f[getControlPoints()->getSFSize()];
  float *tmpWeights = new float[weight()->getSize()];
  float *vKnots = new float [knot()->getSize()];
  int vOrder = order()->getValue();
  int vDimension = weight()->getSize();
  
  
  for(i=0; i<(getControlPoints()->getSFSize()); i++){
    tmpControlPoints[i] = getControlPoints()->getValue(i);
  }

  for(i=0; i<(weight()->getSFSize()); i++){
    tmpWeights[i] = weight()->getValue(i);
  }
  
  for(i=0; i<(knot()->getSFSize()); i++){
    vKnots[i] = knot()->getValue(i);
  }
    
  Vec3f point = P1;
  Vec3f vector = P2 - P1;
  vector.normalize();
  if((vector.x==0) && (vector.y==0) && (vector.z==0)) {return NULL;}
  
  NurbsMakeRevolvedSurface surface(tmpControlPoints, tmpWeights, vDimension, 
                                   narcs, rDegree, pDegree, point, vector);
  if (!surface.isValid())
      return NULL;

  int uOrder = pDegree + 1;
  float *controlPoints = new float[surface.getPointSize()];
  float *weights = new float[surface.getWeightSize()];
  int uDimension = surface.getWeightSize() / vDimension;
  float *uKnots = new float[uDimension + uOrder]; 

  //get results of rotation
  //get control points
  for(i=0; i<(surface.getPointSize()); i++){
    controlPoints[i] = surface.getControlPoints(i);
  }
  //weights
  for(i=0; i<(surface.getWeightSize()); i++){
    weights[i] = surface.getWeights(i);
  }
  if (pDegree == 1){
    //set u-knotvektor
    for(i=0; i<uOrder; i++){
      uKnots[i] = 0.0f;
      uKnots[i+uDimension] = (float) (uDimension - uOrder +1);
    }
    for(i=0; i<(uDimension-uOrder); i++){
      uKnots[i+uOrder] = (float) (i + 1);  
    } 
  }  
  else {
    //u-knotenvector
    for(i=0; i<(surface.getKnotSize()); i++){
      uKnots[i] = surface.getKnots(i);
    }
  }
  
  node->setField(node->uDimension_Field(), new SFInt32(uDimension));
  node->setField(node->vDimension_Field(), new SFInt32(vDimension));
  node->uKnot(new MFFloat(uKnots, uDimension + uOrder));
  node->vKnot(new MFFloat(vKnots, vDimension + vOrder));
  node->setField(node->uOrder_Field(), new SFInt32(uOrder));
  node->setField(node->vOrder_Field(), new SFInt32(vOrder));
  node->createControlPoints(new MFVec3f(controlPoints, 
                                        uDimension * vDimension * 3));
  node->weight(new MFFloat(weights, uDimension * vDimension));

  return node;
}  

Node*
NodeNurbsCurve::toSuperExtrusion(void)
{
  NodeSuperExtrusion *node = (NodeSuperExtrusion *)
                            _scene->createNode("SuperExtrusion");
  
  int i;
  float *tmpControlPoints = new float[getControlPoints()->getSize()];
  float *tmpWeights = new float[weight()->getSize()];
  float *tmpKnots = new float[knot()->getSize()];
  int tmpOrder = order()->getValue();  
  
  for(i=0; i<(getControlPoints()->getSize()); i++){
    tmpControlPoints[i] = getControlPoints()->getValues()[i];
  }

  for(i=0; i<(weight()->getSFSize()); i++){
    tmpWeights[i] = weight()->getValue(i);
  }
  
  for(i=0; i<(knot()->getSFSize()); i++){
    tmpKnots[i] = knot()->getValue(i);
  }
    
  node->setField(node->spineTessellation_Field(), 
                 new SFInt32(tessellation()->getValue()));
  node->knot(new MFFloat(tmpKnots, knot()->getSFSize()));
  node->setField(node->order_Field(), new SFInt32(tmpOrder));
  node->controlPoint(new MFVec3f(tmpControlPoints, 
                                 getControlPoints()->getSize()));
  node->weight(new MFFloat(tmpWeights, weight()->getSFSize()));

  return node;
}  

Node*
NodeNurbsCurve::toSuperRevolver(void)
{
  NodeSuperRevolver *node = (NodeSuperRevolver *)
                            _scene->createNode("SuperRevolver");
  
  int i;
  float *tmpControlPoints = new float[getControlPoints()->getSFSize() * 2];
  float *tmpWeights = new float[weight()->getSize()];
  float *tmpKnots = new float[knot()->getSize()];
  int tmpOrder = order()->getValue();  
  
  for(i=0; i<(getControlPoints()->getSFSize()); i++){
    tmpControlPoints[2 * i] = getControlPoints()->getValues()[3 * i];
    tmpControlPoints[2 * i + 1] = getControlPoints()->getValues()[3 * i + 1];
  }

  for(i=0; i<(weight()->getSFSize()); i++){
    tmpWeights[i] = weight()->getValue(i);
  }
  
  for(i=0; i<(knot()->getSFSize()); i++){
    tmpKnots[i] = knot()->getValue(i);
  }
    
  node->setField(node->nurbsTessellation_Field(), 
                 new SFInt32(tessellation()->getValue()));
  node->knot(new MFFloat(tmpKnots, knot()->getSFSize()));
  node->setField(node->order_Field(), new SFInt32(tmpOrder));
  node->controlPoint(new MFVec2f(tmpControlPoints, 
                                 getControlPoints()->getSFSize() * 2));
  node->weight(new MFFloat(tmpWeights, weight()->getSFSize()));

  return node;
}  

void 
NodeNurbsCurve::flatten(int direction, bool zero)
{
    if (_x3d) {
        NodeCoordinate *ncoord = (NodeCoordinate *)
                                 controlPointX3D()->getValue();
        if (ncoord)
            if (ncoord->getType() == VRML_COORDINATE) 
                 ncoord->flatten(direction, zero);
        _chainDirty = true;
        return;
    }

    MFVec3f *newValue = (MFVec3f *) getControlPoints()->copy();
 
    if (newValue->getSFSize() == 0)
        return;

    _chainDirty = true;
 
    float value = 0;

    if (!zero) {
        for (int i = 0; i < newValue->getSFSize(); i++)
            value += newValue->getValue(i)[direction] / newValue->getSFSize();
    }
    for (int i = 0; i < newValue->getSFSize(); i++) {
        SFVec3f vec(newValue->getValue(i));
        vec.setValue(direction, value);
        newValue->setSFValue(i, &vec);
    } 
    if (_x3d) {
        NodeCoordinate *coord = (NodeCoordinate *)controlPointX3D()->getValue();
        if (coord == NULL)
            createControlPoints(newValue);
        else 
            _scene->setField(coord, coord->point_Field(), newValue);
    } else
        _scene->setField(this, controlPoint_Field(), newValue);
}

void 
NodeNurbsCurve::revolveFlatter(int change, int zero)
{
    MFVec3f *values = getControlPoints();
    for (int i = 0; i < values->getSFSize(); i++) {
        SFVec3f vec(values->getValue(i));
        float len = sqrt(vec.getValue(change) * vec.getValue(change)  
                       + vec.getValue(zero)   * vec.getValue(zero));
        vec.setValue(change,  len);
        vec.setValue(zero, 0);
        SFVec3f result(vec.getValue(0), vec.getValue(1), vec.getValue(2));
        values->setSFValue(i, &result);
    } 
}

bool
NodeNurbsCurve::revolveFlatten(int direction)
{
    switch(direction) {
      case NURBS_ROT_X_AXIS:
        revolveFlatter(2, 1);
        break;
      case NURBS_ROT_Y_AXIS:
        revolveFlatter(0, 2);
        break;
      case NURBS_ROT_Z_AXIS:
        revolveFlatter(1, 0);
        break;
      case NURBS_ROT_POINT_TO_POINT:
        return false; 
      default:
        assert(0);
    }
    return true;
}

void
NodeNurbsCurve::flip(int index)
{
    if (_x3d) {
        NodeCoordinate *ncoord = (NodeCoordinate *)
                                 controlPointX3D()->getValue();
        if (ncoord)
            if (ncoord->getType() == VRML_COORDINATE) 
                 ncoord->flip(index);
        _chainDirty = true;
        return;
    }

    if (controlPoint())
        controlPoint()->flip(index);
    _chainDirty = true;
    
}

void
NodeNurbsCurve::swap(int fromTo)
{
    if (_x3d) {
        NodeCoordinate *ncoord = (NodeCoordinate *)
                                 controlPointX3D()->getValue();
        if (ncoord)
            if (ncoord->getType() == VRML_COORDINATE) 
                 ncoord->swap(fromTo);
        _chainDirty = true;
        return;
    }

    if (getControlPoints())
        getControlPoints()->swap(fromTo);
    _chainDirty = true;
    
}

Node*
NodeNurbsCurve::degreeElevate(int newDegree)
{
  //Nothing but simple application of existing class "NurbsCurveDegreeElevate" 

  if(newDegree <= ((order()->getValue())-1)){
    return NULL;
  }

  NodeNurbsCurve *node = (NodeNurbsCurve *)
                            _scene->createNode("NurbsCurve");

  if(newDegree > ((order()->getValue())-1)){
    
    //load old values
    int i;
    int dimension = getControlPoints()->getSize() / 3;
    Vec3f *tPoints = new Vec3f[dimension];
    float *tWeights = new float[dimension];
    int knotSize = knot()->getSize();
    Array<float> tKnots(knotSize);
    int deg = order()->getValue() - 1;
    int upDegree = newDegree - deg;

    for (i=0; i<dimension; i++){
      tPoints[i] = getControlPoints()->getValue(i);
      tWeights[i] =weight()->getValue(i);
    }
    
    for (i=0; i<knotSize; i++){
      tKnots[i] = knot()->getValue(i);
    }

    //elevate curve
    NurbsCurveDegreeElevate elevatedCurve(tPoints, tWeights, tKnots, dimension, deg, upDegree);

    //get new values
    int newDimension = elevatedCurve.getPointSize();
    int newKnotSize = elevatedCurve.getKnotSize();
    int newOrder = newDegree + 1;

    float *newControlPoints = new float[newDimension * 3];
    float *newWeights = new float[newDimension];
    float *newKnots = new float[newDimension + newOrder]; 

    for (i=0; i<newDimension; i++){
      newControlPoints[(i*3)]   = elevatedCurve.getControlPoints(i).x;
      newControlPoints[(i*3)+1] = elevatedCurve.getControlPoints(i).y;
      newControlPoints[(i*3)+2] = elevatedCurve.getControlPoints(i).z;
      newWeights[i] = elevatedCurve.getWeights(i);
    }
    
    for (i=0; i<newKnotSize; i++){
      newKnots[i] = elevatedCurve.getKnots(i);
    }

    //load new node
    node->createControlPoints(new MFVec3f(newControlPoints, newDimension * 3));
    node->weight(new MFFloat(newWeights, newDimension));
    node->knot(new MFFloat(newKnots, newDimension + newOrder));
    node->order(new SFInt32(newOrder));
    node->tessellation(new SFInt32(tessellation()->getValue()));
 
    return node;
  }
  return NULL;
}

Node *
NodeNurbsCurve::convert2X3d(void)
{
    if (_isInternal)
        return NULL;
    if (_x3d == true)
        return NULL;
    MFVec3f *points = controlPoint();
    NodeCoordinate *node = (NodeCoordinate *)_scene->createNode("Coordinate");
    int len = points->getSize();
    float *floats = new float[len];
    for (int i = 0; i < len; i++)
        floats[i] = points->getValues()[i];
    node->point(new MFVec3f(floats, len));
    SFNode *old = this->controlPointX3D();
    if (old != NULL)
        if (old->getValue() != NULL)
            _scene->execute(new MoveCommand(old->getValue(), 
                                            this, controlPointX3D_Field(),
                                            NULL, -1));
    _scene->execute(new MoveCommand(node, NULL, -1, this, 
                                    controlPointX3D_Field()));
    _scene->changeRoutes(node, node->point_Field(), this, controlPoint_Field(),
                         false);
    _scene->UpdateViews(NULL, UPDATE_REDRAW, NULL);
    return NULL;
}

Node *
NodeNurbsCurve::convert2Vrml(void) 
{
//    if (_isInternal)
//        return NULL;
    if (_x3d == false)
        return NULL;
    NodeCoordinate *node = (NodeCoordinate *)controlPointX3D()->getValue();
    _x3d = false;
    if (node != NULL) {
        int len = node->point()->getSize();
        float *floats = new float[len];
        for (int i = 0; i < len; i++)
            floats[i] = node->point()->getValues()[i];
        controlPoint(new MFVec3f(floats, len));
        _scene->changeRoutes(this, controlPoint_Field(), 
                             node, node->point_Field(), false);
        _scene->execute(new MoveCommand(node, this, controlPointX3D_Field(),
                                        NULL, -1));
    } else
        controlPoint(new MFVec3f());
    _scene->UpdateViews(NULL, UPDATE_REDRAW, NULL);
    return NULL;
}

void
NodeNurbsCurve::backupFieldsAppend(int field)
{
    if ((!_x3d) || (field != controlPoint_Field()))
        _scene->backupFieldsAppend(this, field);
    else {
        NodeCoordinate *coord = (NodeCoordinate *)controlPointX3D()->getValue();
        _scene->backupFieldsAppend(coord, coord->point_Field());
    }
}

