/*
 * NodeSuperExtrusion.cpp
 *
 * Copyright (C) 1999 Stephen F. White, 2004 J. "MUFTI" Scheurich
 * 
 * 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 <stdio.h>
#include "stdafx.h"

#include "NodeSuperExtrusion.h"
#include "Proto.h"
#include "DuneApp.h"
#include "Scene.h"
#include "FieldValue.h"
#include "SFFloat.h"
#include "SFInt32.h"
#include "SFBool.h"
#include "Vec2f.h"
#include "NodeNurbsSurface.h"
#include "Mesh.h"
#include "Util.h"
#include "RenderState.h"
#include "resource.h"

ProtoSuperExtrusion::ProtoSuperExtrusion(Scene *scene)
  : Proto(scene, "SuperExtrusion")
{
    a.set( 
          addExposedField(SFFLOAT, "a", new SFFloat(1.0f)));
    b.set( 
          addExposedField(SFFLOAT, "b", new SFFloat(1.0f)));
    m.set( 
          addExposedField(SFFLOAT, "m", new SFFloat(0.0f)));
    n1.set( 
          addExposedField(SFFLOAT, "n1", new SFFloat(1.0f)));
    n2.set( 
          addExposedField(SFFLOAT, "n2", new SFFloat(1.0f)));
    n3.set( 
          addExposedField(SFFLOAT, "n3", new SFFloat(1.0f)));

    border.set( 
          addExposedField(SFFLOAT, "border", new SFFloat(M_PI),
                          new SFFloat(-M_PI), new SFFloat(M_PI)));
    bottomBorder.set( 
          addExposedField(SFFLOAT, "bottomBorder", new SFFloat(-M_PI),
                          new SFFloat(-M_PI), new SFFloat(M_PI)));

    superTessellation.set(
          addExposedField(SFINT32, "superTessellation", new SFInt32(0)));
    spineTessellation.set(
          addExposedField(SFINT32, "spineTessellation", new SFInt32(0)));

    controlPoint.set(
          addExposedField(MFVEC3F, "controlPoint", new MFVec3f()));
    weight.set(
          addExposedField(MFFLOAT, "weight", new MFFloat(), new SFFloat(0.0f)));
    knot.set(
          addField(MFFLOAT, "knot", new MFFloat()));
    order.set(
          addField(SFINT32, "order", new SFInt32(3), new SFInt32(2)));

    creaseAngle.set( 
          addField(SFFLOAT, "creaseAngle", new SFFloat(0.7854f), 
                   new SFFloat(0.0f)));
    beginCap.set(
          addField(SFBOOL, "beginCap", new SFBool(true)));
    endCap.set(
          addField(SFBOOL, "endCap", new SFBool(true)));

    solid.set(
          addField(SFBOOL, "solid", new SFBool(true)));

    float values3[] = { 1.0, 1.0 };
    float *v = new float[2];  
    memcpy(v, values3, 2 * sizeof(float));
    scale.set(
          addField(MFVEC2F, "scale", new MFVec2f(v, 2), new SFFloat(0.0f)));
}

Node *
ProtoSuperExtrusion::create(Scene *scene)
{
    return new NodeSuperExtrusion(scene, this); 
}

NodeSuperExtrusion::NodeSuperExtrusion(Scene *scene, Proto *def)
  : MeshMorphingNode(scene, def)
{
    _extrusionDirty = true;
    _extrusion = (NodeExtrusion *) scene->createNode("Extrusion");
    _nurbsCurve = (NodeNurbsCurve *) scene->createNode("NurbsCurve");
    _nurbsCurve->setInternal(true);
}

NodeSuperExtrusion::~NodeSuperExtrusion()
{
    delete _extrusion;
    delete _nurbsCurve;
}

static float superFormula(float angle, float a, float b, float m, 
                          float n1, float n2, float n3)
    {
    float f = m * angle / 4.0;
    float c = cos(f);
    float s = sin(f);
    return pow(pow(fabs(c / a), n2) + pow(fabs(s / b), n3), -1.0f / n1);
    }

void
NodeSuperExtrusion::createExtrusion()
{
    if (_extrusion == NULL)
        _extrusion = (NodeExtrusion *) _scene->createNode("Extrusion");

    if (_nurbsCurve == NULL)
        _nurbsCurve = (NodeNurbsCurve *) _scene->createNode("NurbsCurve");

    int superTess = superTessellation()->getValue();
    int spineTess = spineTessellation()->getValue();

    if (superTess <= 0) superTess = TheApp->getTessellation();
    if (spineTess <= 0) spineTess = TheApp->getTessellation();

    superTess++;

    if (superTess < 3) return;

    int size = superTess;
    float *vert = new float[size * 2];

    float low = bottomBorder()->getValue();
    float high = border()->getValue();
    
    float inc1 = (high - low) / (superTess-1);
    if (inc1 < EPSILON) return;
    int a1;
    float fa = a()->getValue();
    float fb = b()->getValue();
    float fm = m()->getValue();
    float fn1 = n1()->getValue();
    float fn2 = n2()->getValue();
    float fn3 = n3()->getValue();
    Array<float> r1(superTess);
    Array<float> c1(superTess);
    Array<float> s1(superTess);
    for (a1 = 0; a1 < superTess; a1++) {
        float angle1 = low + a1 * inc1;
        r1[a1] = superFormula(angle1, fa, fb, fm, fn1, fn2, fn3);
        c1[a1] = cos(angle1 + M_PI / 2.0);
        s1[a1] = sin(angle1 + M_PI / 2.0);
    }
    int index = 0;
    for (a1 = superTess - 1; a1 >= 0; a1--) {
        vert[index] = r1[a1]*c1[a1];
        vert[index+1] = r1[a1]*s1[a1];
        index = index + 2;
    }

    int i;

    _extrusion->crossSection(new MFVec2f(vert, size * 2));
    
    bool bsolid = false;
    if ((high == (float)M_PI) && beginCap()->getValue() && endCap()->getValue())
        bsolid = true;

    _extrusion->solid(new SFBool(bsolid));
    _extrusion->convex(new SFBool(false));
    _extrusion->creaseAngle(new SFFloat(creaseAngle()->getValue()));
    _extrusion->beginCap(new SFBool(beginCap()->getValue()));
    _extrusion->endCap(new SFBool(endCap()->getValue()));
    _nurbsCurve->tessellation(new SFInt32(spineTessellation()->getValue()));
    int len = controlPoint()->getSize();
    float *points = new float[len];
    for (i = 0; i < len; i++)
         points[i] = controlPoint()->getValues()[i];
    _nurbsCurve->setControlPoints(new MFVec3f(points, len));
    float *weights = new float[weight()->getSize()];
    for (i = 0; i < weight()->getSize(); i++)
         weights[i] = weight()->getValues()[i];
    _nurbsCurve->weight(new MFFloat(weights, weight()->getSize()));
    float *knots = new float[knot()->getSize()];
    for (i = 0; i < knot()->getSize(); i++)
         knots[i] = knot()->getValues()[i];
    _nurbsCurve->knot(new MFFloat(knots, knot()->getSize()));
    _nurbsCurve->order(new SFInt32(order()->getValue()));
    const Vec3f *chain = _nurbsCurve->getChain();
    int chainLength = _nurbsCurve->getChainLength();
    float *fchain = new float[chainLength * 3];
    for (i = 0; i < chainLength; i++) {
         fchain[i * 3    ] = chain[i].x;
         fchain[i * 3 + 1] = chain[i].y;
         fchain[i * 3 + 2] = chain[i].z;
    }   
    _extrusion->spine(new MFVec3f(fchain, chainLength * 3));
    if (scale()->getSFSize() > 0) {
        float *scales;
        int scaleLen = scale()->getSFSize();
        if (scaleLen == 1)
            scales = new float[2];
        else
           scales = new float[2 * (spineTess + 1)];
        scales[0] = scale()->getValues()[0];
        scales[1] = scale()->getValues()[1];
        if (scaleLen > 1) {
            float tess_inc = 1.0 / (spineTess);
            float scale_inc = 1.0 / (scaleLen - 1);
            float scale_left = 0.0;
            int scale_next_index = 1;
            for (i = 1; i <= spineTess; i++) {
                float left_scale_x; 
                left_scale_x = scale()->getValues()[scale_next_index * 2 - 2];
                float scale_diff_x;
                scale_diff_x = scale()->getValues()[scale_next_index * 2 + 0] - 
                               left_scale_x;
                scales[i * 2 + 0] = left_scale_x + scale_diff_x * 
                                    ((i * tess_inc - scale_left) / scale_inc);

                float left_scale_y; 
                left_scale_y = scale()->getValues()[scale_next_index * 2 - 1];
                float scale_diff_y;
                scale_diff_y = scale()->getValues()[scale_next_index * 2 + 1] - 
                               left_scale_y;
                scales[i * 2 + 1] = left_scale_y + scale_diff_y * 
                                    ((i * tess_inc - scale_left) / scale_inc);

                if ((i * tess_inc) >= (scale_next_index * scale_inc)) {
                    scale_next_index++;
                    scale_left += scale_inc;
                }              
            }
        }
        _extrusion->scale(new MFVec2f(scales, scaleLen == 1 ? 2 : 
                                              (spineTess + 1) * 2));
    }
    _extrusionDirty = false;
}

int
NodeSuperExtrusion::writeProto(int f)
{
    return ((Node *)this)->writeProto(f, "", "scriptedNodes"
#ifdef HAVE_SCRIPTED_NODES_PROTO_URL
                                            , HAVE_SCRIPTED_NODES_PROTO_URL
#endif
                                            );
}

void
NodeSuperExtrusion::setField(int index, FieldValue *value)
{
    _extrusionDirty = true;
    Node::setField(index, value);
    if (_scene->getXSymetricMode()) {
        if (index == border_Field())
            Node::setField(bottomBorder_Field(),
                           new SFFloat(-border()->getValue()));
        if (index == bottomBorder_Field())
            Node::setField(border_Field(),
                           new SFFloat(-bottomBorder()->getValue()));
    }
    update();
}

void
NodeSuperExtrusion::draw()
{
    if (_extrusionDirty) {
        createExtrusion();
        _extrusionDirty = false;
    }

    if (!_extrusion) return;

    _extrusion->draw();
}

void
NodeSuperExtrusion::drawAHandles()
{
    float fa = a()->getValue();
    RenderState state;

    int spineLen = _extrusion->spine()->getSFSize();
    if (spineLen <= 0) 
        return;
    const float *spine = _extrusion->spine()->getValues(); 
    int startHandles = controlPoint()->getSize() / 3;
         
    glPushMatrix();
    glPushAttrib(GL_LIGHTING);
    glDisable(GL_LIGHTING);
    glPushName(0);
        
    state.startDrawHandles();
    for (int i = 0; i < 4; i++) {
        float x = 0;
        float y = 0;
        float z = 0;
        switch (i) {
          case 0:
            x = -fa;
            break;
          case 1:
            x = fa;
            break;
          case 2:
            z = -fa;
            break;
          case 3:
            z = fa;
            break;
        }
        Vec3f handlePosition(spine[0] + x, spine[1] + y, spine[2] + z);
        state.setHandleColor(_scene, startHandles + i);
        glLoadName(startHandles + i);
        state.drawHandle(handlePosition);
    }
    state.endDrawHandles();
    glPopName();
    glPopAttrib();
    glPopMatrix(); 
}

void
NodeSuperExtrusion::drawScaleHandles()
{
    RenderState state;

    int spineLen = _extrusion->spine()->getSFSize();
    if (spineLen <= 0) 
        return;
    const float *spine = _extrusion->spine()->getValues(); 

    int scaleLen = scale()->getSFSize();
    if (scaleLen <= 1) 
        return;
    const float *fscale = scale()->getValues(); 
    int startHandles = controlPoint()->getSize() / 3 + 4;
         
    glPushMatrix();
    glPushAttrib(GL_LIGHTING);
    glDisable(GL_LIGHTING);
    glPushName(0);

    float yLen = spine[(spineLen - 1) * 3 + 1] - spine[1];        
    state.startDrawHandles();
    for (int i = 0; i < scaleLen; i++) {
        float x = fscale[i * 2];
        float y = spine[1] + yLen * i / (scaleLen - 1);
        float z = fscale[i * 2 + 1];
        Vec3f handlePosition(spine[0] + x, spine[1] + y, spine[2] + z);
        state.setHandleColor(_scene, startHandles + i);
        glLoadName(startHandles + i);
        state.drawHandle(handlePosition);
    }
    state.endDrawHandles();
    glPopName();
    glPopAttrib();
    glPopMatrix(); 
}

void  
NodeSuperExtrusion::drawHandles()
{
    if (_extrusionDirty) {
        createExtrusion();
        _extrusionDirty = false;
    }

    if (!_extrusion) return;

    drawScaleHandles();
    _nurbsCurve->drawHandles();
    drawAHandles();
}

Vec3f
NodeSuperExtrusion::getHandle(int handle, int *constraint, int *field)
{
    *field = -1;

    int numControlPoints = controlPoint()->getSize() / 3;
    if (handle >= 0 && handle < numControlPoints) {
        *field = controlPoint_Field();
        Vec3f ret((Vec3f)controlPoint()->getValue(handle) / 
                   weight()->getValue(handle));
        TheApp->PrintMessageWindowsVertex(IDS_VERTEX_SELECTED, "controlPoint", 
                                          handle);
        return ret;
    } else if ((handle >= numControlPoints) && 
               (handle < (numControlPoints + 4))) {
        *field = a_Field();
        float x = 0;
        float z = 0;
        float fa = a()->getValue();
        switch (handle - numControlPoints) {
          case 0:
            x = -fa;
            break; 
          case 1:
            x = fa;
            break;
          case 2:
            z = -fa;
            break;
          case 3:
            z = fa; 
            break;    
        }
        Vec3f ret(x, 0, z);
        return ret;
    } else if (handle < (numControlPoints + 4 + scale()->getSFSize())) {
        *field = scale_Field();
        const float *fscale = scale()->getValues(); 
        int scaleHandle = handle - numControlPoints - 4;
        const float *spine = _extrusion->spine()->getValues(); 
        Vec3f ret(fscale[scaleHandle * 2] - spine[0], 
                  0, 
                  fscale[scaleHandle * 2 + 1] - spine[2]);
        return ret;        
    }

    return Vec3f(0.0f, 0.0f, 0.0f);
}


void
NodeSuperExtrusion::setHandle(int handle, const Vec3f &v) 
{
    int numControlPoints = controlPoint()->getSize() / 3;

    if (handle >= 0 && handle < numControlPoints) {
        Vec3f v2 = v * weight()->getValue(handle); 
        const float *fcontrolPoint = controlPoint()->getValues(); 
        MFVec3f *newValue = new MFVec3f();
        for (int i = 0; i < controlPoint()->getSFSize(); i++)
            newValue->setSFValue(i, fcontrolPoint[i * 3], 
                                    fcontrolPoint[i * 3 + 1],
                                    fcontrolPoint[i * 3 + 2]);
        _extrusionDirty = true;
        newValue->setValue(handle * 3, v2.x);
        newValue->setValue(handle * 3 + 1, v2.y);
        newValue->setValue(handle * 3 + 2, v2.z);
        _scene->setField(this, controlPoint_Field(), newValue);
    } else if ((handle >= numControlPoints) &&
               (handle < (numControlPoints + 4))) {
        float fa = a()->getValue();
        switch (handle - numControlPoints) {
          case 0:
            fa = -v.x;
            break; 
          case 1:
            fa = v.x;
            break;
          case 2:
            fa = -v.z;
            break;
          case 3:
            fa = v.z; 
            break;    
        }
        _scene->setField(this, a_Field(), new SFFloat(fa));
    } else if (handle < (numControlPoints + 4 + scale()->getSFSize())) {
        int scaleHandle = handle - numControlPoints - 4;
        const float *spine = _extrusion->spine()->getValues(); 
        const float *fscale = scale()->getValues(); 
        MFVec2f *newValue = new MFVec2f();
        for (int i = 0; i < scale()->getSFSize(); i++)
            newValue->setSFValue(i, fscale[i * 2], fscale[i * 2 + 1]);
        newValue->setSFValue(scaleHandle, v.x - spine[0], v.z - spine[2]);
        _scene->setField(this, scale_Field(), newValue); 

    }
}

Node *
NodeSuperExtrusion::toExtrusion(void)
{
    if (_extrusionDirty) {
        createExtrusion();
        _extrusionDirty = false;
    }

    if (!_extrusion) 
        return NULL;

    return _extrusion->copy();
}

Vec3f   
NodeSuperExtrusion::getMinBoundingBox(void)
{
    if (_extrusionDirty) {
        createExtrusion();
        _extrusionDirty = false;
    }
 
    return _extrusion->getMinBoundingBox();
}

Vec3f   
NodeSuperExtrusion::getMaxBoundingBox(void)
{
    if (_extrusionDirty) {
        createExtrusion();
        _extrusionDirty = false;
    }
 
    return _extrusion->getMaxBoundingBox();
}

void
NodeSuperExtrusion::flip(int index)
{
    if (controlPoint())
        controlPoint()->flip(index);
    _extrusionDirty = true;    
}

void
NodeSuperExtrusion::swap(int fromTo)
{
    if (controlPoint())
        controlPoint()->swap(fromTo);
    _extrusionDirty = true;    
}

int
NodeSuperExtrusion::countPolygons()
{
    if (_extrusionDirty) {
        createExtrusion();
        _extrusionDirty = false;
    }

    if (!_extrusion) return 0;

    return _extrusion->countPolygons();
}

Node   *
NodeSuperExtrusion::toNurbsCurve(void)
{
  NodeNurbsCurve *node = (NodeNurbsCurve *) _scene->createNode("NurbsCurve");
  
  int i;
  float *tmpControlPoints = new float[controlPoint()->getSize()];
  float *tmpWeights = new float[weight()->getSize()];
  float *tmpKnots = new float[knot()->getSize()];
  int tmpOrder = order()->getValue();  
  
  for(i=0; i<(controlPoint()->getSize()); i++){
    tmpControlPoints[i] = controlPoint()->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->tessellation_Field(), 
                 new SFInt32(spineTessellation()->getValue()));
  node->knot(new MFFloat(tmpKnots, knot()->getSFSize()));
  node->setField(node->order_Field(), new SFInt32(tmpOrder));
  node->setField(node->closed_Field(), new SFBool(true));
  node->createControlPoints(new MFVec3f(tmpControlPoints, 
                                        controlPoint()->getSize()));
  node->weight(new MFFloat(tmpWeights, weight()->getSFSize()));

  return node;
}

Node*
NodeSuperExtrusion::toNurbs(int uTessel, int vTessel, int uDegree, int vDegree)
{   
    NodeNurbsSurface *node = (NodeNurbsSurface *) _scene->createNode(
                             "NurbsSurface");

    int vOrder = vDegree + 1;
    int uOrder = uDegree + 1;

    int oldUTess = superTessellation()->getValue();
    int oldVTess = spineTessellation()->getValue();

    int uTess = uTessel;
    int vTess = vTessel; 

    superTessellation(new SFInt32(uTess));
    spineTessellation(new SFInt32(vTess));
    createMesh(false);
    _meshDirty = true;
    if (_mesh == NULL)
        return NULL;

    uTess += 1;
    vTess += 1;

    int size = _mesh->getVertices()->getSize();
    int uDimension = uTess;
    int vDimension = vTess;
    float *controlPoints = new float[size];
    float *weights = new float[size];
    float *uKnots = new float[uDimension + uOrder]; 
    float *vKnots = new float[vDimension + vOrder]; 

    int i;
    for (i = 0; i < size; i++) {
        controlPoints[i] = _mesh->getVertices()->getValues()[i];
        weights[i] = 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);  
    } 
    //set v-knotvektor
    for(i = 0; i < vOrder; i++){
        vKnots[i] = 0.0f;
        vKnots[i + vDimension] = (float) (vDimension - vOrder + 1);
    }
    for(i = 0; i < (vDimension - vOrder); i++){
        vKnots[i + vOrder] = (float) (i + 1);  
    } 
    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->setField(node->uClosed_Field(), new SFBool(true));
    node->setField(node->vClosed_Field(), new SFBool(true));
    node->createControlPoints(new MFVec3f(controlPoints, 
                                          uDimension * vDimension * 3));
    node->weight(new MFFloat(weights, uDimension * vDimension));
    node->ccw(new SFBool(_mesh->ccw()));
    node->solid(new SFBool(_mesh->solid()));

    node->setField(node->uTessellation_Field(), new SFInt32(uTess - 1));
    node->setField(node->vTessellation_Field(), new SFInt32(vTess - 1));

    for (int iteration = 0; iteration < 32; iteration++) {
        node->reInit();
        node->createMesh(false);    
        if (node->getVertices() != NULL) {
            assert(size == node->getVertices()->getSize());
            float *vert = new float[size];
            MFVec3f *nurbsControlPoints = node->controlPoint();    
            for (i = 0; i < size; i++) {
                vert[i] = node->getVertices()->getValues()[i];
                float meshValue = _mesh->getVertices()->getValues()[i];
                float nurbsValue = nurbsControlPoints->getValues()[i];
                if (fabs(vert[i]) > EPSILON)
                    vert[i] = nurbsValue * meshValue / vert[i];
            }
            for (i = 0; i < size; i++) {
                nurbsControlPoints->setValue(i, vert[i]);
            }
        }
    }
    node->setField(node->uTessellation_Field(), new SFInt32(0));
    node->setField(node->vTessellation_Field(), new SFInt32(0));

    superTessellation(new SFInt32(oldUTess));
    spineTessellation(new SFInt32(oldVTess));

    return node;   
}

void
NodeSuperExtrusion::createMesh(bool cleanDoubleVertices)
{
    if (_extrusionDirty) {
        createExtrusion();
        _extrusionDirty = false;
    }

    if (!_extrusion) return;

    _mesh = _extrusion->getMesh();
    _meshDirty = false;
}

void
NodeSuperExtrusion::createMesh(SuperExtrusionData &data)
{
    int i;
    a(new SFFloat(data.a));
    b(new SFFloat(data.b));
    m(new SFFloat(data.m));
    n1(new SFFloat(data.n1));
    n2(new SFFloat(data.n2));
    n3(new SFFloat(data.n3));
    border(new SFFloat(data.border));
    bottomBorder(new SFFloat(data.bottomBorder));
    float *controlPoints = new float[controlPoint()->getSize()];
    for (i = 0; i < controlPoint()->getSize(); i++)
         controlPoints[i] = data.controlPoint[i];
    controlPoint(new MFVec3f(controlPoints, controlPoint()->getSize()));
    float *weights = new float[weight()->getSize()];
    for (i = 0; i < weight()->getSize(); i++)
         weights[i] = data.weight[i];
    weight(new MFFloat(weights, weight()->getSize()));
    float *knots = new float[knot()->getSize()];
    for (i = 0; i < knot()->getSize(); i++)
        knots[i] = data.knot[i];
    knot(new MFFloat(knots, knot()->getSize()));
    createMesh();
}

void
NodeSuperExtrusion::copyData(SuperExtrusionData *data)
{
    data->a = a()->getValue();
    data->b = b()->getValue();
    data->m = m()->getValue();
    data->n1 = n1()->getValue();
    data->n2 = n2()->getValue();
    data->n3 = n3()->getValue();
    data->border = border()->getValue();
    data->bottomBorder = bottomBorder()->getValue();
    MFVec3f *controlPoints = (MFVec3f *) controlPoint()->copy();
    data->controlPoint = (float *) controlPoints->getValues();
    MFFloat *weights = (MFFloat *) weight()->copy();
    data->weight = (float *) weights->getValues();
    MFFloat *knots = (MFFloat *) knot()->copy();
    data->knot = (float *) knots->getValues();
}

void *
NodeSuperExtrusion::initializeData(void)
{
    SuperExtrusionData *data = new SuperExtrusionData();

    copyData(data);
    copyData(&_tempStoreData);

    return data;
}

void
NodeSuperExtrusion::loadDataFromInterpolators(void *superExtrusionData, 
                                              Interpolator *inter,
                                              int field, float key)
{
    SuperExtrusionData *data = (SuperExtrusionData *)superExtrusionData;
    if (field == a_Field())
        inter->interpolate(key, &(data->a));
    else if (field == b_Field())
        inter->interpolate(key, &(data->b));
    else if (field == m_Field())
        inter->interpolate(key, &(data->m));
    else if (field == n1_Field())
        inter->interpolate(key, &(data->n1));
    else if (field == n2_Field())
        inter->interpolate(key, &(data->n2));
    else if (field == n3_Field())
        inter->interpolate(key, &(data->n3));
    else if (field == border_Field())
        inter->interpolate(key, &(data->border));
    else if (field == bottomBorder_Field())
        inter->interpolate(key, &(data->bottomBorder));
    else if (field == controlPoint_Field())
        inter->interpolate(key, data->controlPoint);
}

void
NodeSuperExtrusion::createMeshFromData(void* superExtrusionData, bool optimize)
{
    _meshDirty = true;
    SuperExtrusionData *data = (SuperExtrusionData *)superExtrusionData;
    createMesh(*data);
}

void
NodeSuperExtrusion::finalizeData(void* data)
{
    _extrusionDirty = true;
    createMesh(&_tempStoreData);
    delete (SuperExtrusionData *)data;
}

void 
NodeSuperExtrusion::flatten(int direction, bool zero)
{
    MFVec3f *newValue = (MFVec3f *) controlPoint()->copy();
 
    if (newValue->getSFSize() == 0)
        return;

    _extrusionDirty = 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);
    } 
    _scene->setField(this, controlPoint_Field(), newValue);
}

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

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

    NodeSuperExtrusion *node = (NodeSuperExtrusion *)
                               _scene->createNode("SuperExtrusion");

    NodeNurbsCurve *curve = (NodeNurbsCurve *)
                            _nurbsCurve->degreeElevate(newDegree);

    if (curve == NULL)
        return NULL;

    //load new node
    node->a(new SFFloat(a()->getValue()));
    node->b(new SFFloat(b()->getValue()));
    node->m(new SFFloat(m()->getValue()));
    node->n1(new SFFloat(n1()->getValue()));
    node->n2(new SFFloat(n2()->getValue()));
    node->n3(new SFFloat(n3()->getValue()));
    node->border(new SFFloat(border()->getValue()));
    node->bottomBorder(new SFFloat(bottomBorder()->getValue()));
    node->superTessellation(new SFInt32(superTessellation()->getValue()));

    node->spineTessellation(new SFInt32(spineTessellation()->getValue()));
    node->controlPoint(new MFVec3f(curve->getControlPoints()));
    MFFloat *w = (MFFloat *)(curve->weight());
    node->weight(new MFFloat(w->getValues(), w->getSize()));
    MFFloat *k = (MFFloat *)(curve->knot());
    node->knot(new MFFloat(k->getValues(), k->getSize()));
    node->order(new SFInt32(curve->order()->getValue()));

    node->creaseAngle(new SFFloat(creaseAngle()->getValue()));
    node->beginCap(new SFBool(beginCap()->getValue()));
    node->endCap(new SFBool(endCap()->getValue()));
    MFVec2f *s = (MFVec2f *)(scale()->copy());
    node->scale(new MFVec2f(s->getValues(), s->getSize()));
 
    return node;
}
