Logo Search packages:      
Sourcecode: whitedune version File versions  Download package

Scene.cpp

/*
 * Scene.cpp
 *
 * Copyright (C) 1999 Stephen F. White
 * 
 * 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 <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "stdafx.h"
#ifndef WIN32
# include <unistd.h>
#endif
#include "Util.h"

#ifdef HAVE_LIBZ
extern "C" {
# include "zlib.h"
}
#endif

#include "swt.h"

#include "Matrix.h"
#include "Scene.h"
#include "SceneView.h"
#include "FieldValue.h"
#include "FieldCommand.h"
#include "MFieldCommand.h"
#include "CommandList.h"
#include "RouteCommand.h"
#include "UnRouteCommand.h"
#include "MoveCommand.h"
#include "Node.h"
#include "SFNode.h"
#include "SFTime.h"
#include "MFNode.h"
#include "Proto.h"
#include "parser.h"
#include "Path.h"
#include "Field.h"
#include "EventIn.h"
#include "EventOut.h"
#include "ExposedField.h"
#include "URL.h"
#include "FontInfo.h"
#include "DuneApp.h"

#include "NodeAnchor.h"
#include "NodeAppearance.h"
#include "NodeAudioClip.h"
#include "NodeBackground.h"
#include "NodeBillboard.h"
#include "NodeBox.h"
#include "NodeCollision.h"
#include "NodeColor.h"
#include "NodeColorInterpolator.h"
#include "NodeContour2D.h"
#include "NodeCone.h"
#include "NodeCoordinate.h"
#include "NodeCoordinateDeformer.h"
#include "NodeCoordinateInterpolator.h"
#include "NodeCylinder.h"
#include "NodeCylinderSensor.h"
#include "NodeDirectionalLight.h"
#include "NodeElevationGrid.h"
#include "NodeExtrusion.h"
#include "NodeFog.h"
#include "NodeFontStyle.h"
#include "NodeGroup.h"
#include "NodeImageTexture.h"
#include "NodeIndexedFaceSet.h"
#include "NodeIndexedLineSet.h"
#include "NodeInline.h"
#include "NodeInlineLoadControl.h"
#include "NodeLOD.h"
#include "NodeLoadSensor.h"
#include "NodeMaterial.h"
#include "NodeMovieTexture.h"
#include "NodeNavigationInfo.h"
#include "NodeNormal.h"
#include "NodeNormalInterpolator.h"
#include "NodeNurbsCurve.h"
#include "NodeNurbsCurve2D.h"
#include "NodeNurbsGroup.h"
#include "NodeNurbsSurface.h"
#include "NodeNurbsTextureSurface.h"
#include "NodeNurbsPositionInterpolator.h"
#include "NodeOrientationInterpolator.h"
#include "NodePixelTexture.h"
#include "NodePlaneSensor.h"
#include "NodePointLight.h"
#include "NodePointSet.h"
#include "NodePolyline2D.h"
#include "NodePositionInterpolator.h"
#include "NodeProximitySensor.h"
#include "NodeScalarInterpolator.h"
#include "NodeShape.h"
#include "NodeSphere.h"
#include "NodeSphereSensor.h"
#include "NodeSpotLight.h"
#include "NodeSound.h"
#include "NodeSuperEllipsoid.h"
#include "NodeSuperExtrusion.h"
#include "NodeSuperShape.h"
#include "NodeSwitch.h"
#include "NodeText.h"
#include "NodeTextureCoordinate.h"
#include "NodeTextureTransform.h"
#include "NodeTimeSensor.h"
#include "NodeTouchSensor.h"
#include "NodeTrimmedSurface.h"
#include "NodeTransform.h"
#include "NodeViewpoint.h"
#include "NodeVisibilitySensor.h"
#include "NodeWorldInfo.h"
#include "NodeComment.h"

#define ARRAY_SIZE(v)  ((int) (sizeof(v) / sizeof(v[0])))

#define PICK_BUFFER_SIZE      1024
#define PICK_REGION_SIZE      2.5

enum {
    PICKED_NODE,
    PICKED_HANDLE,
    PICKED_3DCURSOR
};

Scene::Scene()
{
    _numSymbols = 0;

    _headlight = true;
    _numLights = 0;
    _selection = NULL;
    _selectedHandle = -1;
    _oldSelectedHandle = _selectedHandle;
    _transformMode = new TransformMode(
          (TMode) TheApp->GetIntPreference("TransformMode",TM_HOVER),
          (TDimension) TheApp->GetIntPreference("TransformDimension",TM_3D),
          (T2axes) TheApp->GetIntPreference("Transform2Axes",TM_NEAR_FAR));
    if (TheApp->getMaxNumberAxesInputDevices()<=4)
       if ((_transformMode->tmode == TM_6D) || (_transformMode->tmode == TM_6D))
          _transformMode->tmode = TM_HOVER;
    if (TheApp->getMaxNumberAxesInputDevices()<3)
       if (_transformMode->tmode == TM_ROCKET)
          _transformMode->tmode = TM_HOVER;
    if (TheApp->getMaxNumberAxesInputDevices()<2) 
       if (_transformMode->tmode >= TM_6D) 
          _transformMode->tmode = TM_TRANSLATE;

    _unmodified = NULL;
    _pureVRML97 = false;
    _extraModifiedFlag = false;

    _protos["Anchor"] = new ProtoAnchor(this);
    _protos["Appearance"] = new ProtoAppearance(this);
    _protos["AudioClip"] = new ProtoAudioClip(this);
    _protos["Background"] = new ProtoBackground(this);
    _protos["Billboard"] = new ProtoBillboard(this);
    _protos["Box"] = new ProtoBox(this);
    _protos["Collision"] = new ProtoCollision(this);
    _protos["Color"] = new ProtoColor(this);
    _protos["ColorInterpolator"] = new ProtoColorInterpolator(this);
    _protos["Contour2D"] = new ProtoContour2D(this);
    _protos["Cone"] = new ProtoCone(this);
    _protos["Coordinate"] = new ProtoCoordinate(this);
    _protos["CoordinateDeformer"] = new ProtoCoordinateDeformer(this);
    _protos["CoordinateInterpolator"] = new ProtoCoordinateInterpolator(this);
    _protos["Cylinder"] = new ProtoCylinder(this);
    _protos["CylinderSensor"] = new ProtoCylinderSensor(this);
    _protos["DirectionalLight"] = new ProtoDirectionalLight(this);
    _protos["ElevationGrid"] = new ProtoElevationGrid(this);
    _protos["Extrusion"] = new ProtoExtrusion(this);
    _protos["Fog"] = new ProtoFog(this);
    _protos["FontStyle"] = new ProtoFontStyle(this);
    _protos["Group"] = new ProtoGroup(this);
    _protos["ImageTexture"] = new ProtoImageTexture(this);
    _protos["IndexedFaceSet"] = new ProtoIndexedFaceSet(this);
    _protos["IndexedLineSet"] = new ProtoIndexedLineSet(this);
    _protos["Inline"] = new ProtoInline(this);
    _protos["InlineLoadControl"] = new ProtoInlineLoadControl(this);
    _protos["LoadSensor"] = new ProtoLoadSensor(this);
    _protos["LOD"] = new ProtoLOD(this);
    _protos["Material"] = new ProtoMaterial(this);
    _protos["MovieTexture"] = new ProtoMovieTexture(this);
    _protos["NavigationInfo"] = new ProtoNavigationInfo(this);
    _protos["Normal"] = new ProtoNormal(this);
    _protos["NormalInterpolator"] = new ProtoNormalInterpolator(this);
    _protos["NurbsCurve"] = new ProtoNurbsCurve(this);
    _protos["NurbsCurve2D"] = new ProtoNurbsCurve2D(this);
    _protos["NurbsGroup"] = new ProtoNurbsGroup(this);
    _protos["NurbsPositionInterpolator"] = new ProtoNurbsPositionInterpolator(this);
    _protos["NurbsSurface"] = new ProtoNurbsSurface(this);
    _protos["NurbsTextureSurface"] = new ProtoNurbsTextureSurface(this);
    _protos["OrientationInterpolator"] = new ProtoOrientationInterpolator(this);
    _protos["PixelTexture"] = new ProtoPixelTexture(this);
    _protos["PlaneSensor"] = new ProtoPlaneSensor(this);
    _protos["PointLight"] = new ProtoPointLight(this);
    _protos["PointSet"] = new ProtoPointSet(this);
    _protos["Polyline2D"] = new ProtoPolyline2D(this);
    _protos["PositionInterpolator"] = new ProtoPositionInterpolator(this);
    _protos["ProximitySensor"] = new ProtoProximitySensor(this);
    _protos["ScalarInterpolator"] = new ProtoScalarInterpolator(this);
    _protos["Shape"] = new ProtoShape(this);
    _protos["Sound"] = new ProtoSound(this);
    _protos["Sphere"] = new ProtoSphere(this);
    _protos["SphereSensor"] = new ProtoSphereSensor(this);
    _protos["SpotLight"] = new ProtoSpotLight(this);
    _protos["SuperEllipsoid"] = new ProtoSuperEllipsoid(this);
    _protos["SuperExtrusion"] = new ProtoSuperExtrusion(this);
    _protos["SuperShape"] = new ProtoSuperShape(this);
    _protos["Switch"] = new ProtoSwitch(this);
    _protos["Text"] = new ProtoText(this);
    _protos["TextureCoordinate"] = new ProtoTextureCoordinate(this);
    _protos["TextureTransform"] = new ProtoTextureTransform(this);
    _protos["TimeSensor"] = new ProtoTimeSensor(this);
    _protos["TouchSensor"] = new ProtoTouchSensor(this);
    _protos["Transform"] = new ProtoTransform(this);
    _protos["TrimmedSurface"] = new ProtoTrimmedSurface(this);
    _protos["Viewpoint"] = new ProtoViewpoint(this);
    _protos["VisibilitySensor"] = new ProtoVisibilitySensor(this);
    _protos["WorldInfo"] = new ProtoWorldInfo(this);
    _protos["#"] = new ProtoComment(this);

    _numberBuildinProtos = getAllNodeNames()->size();

    _root = createNode("Group");
    _root->ref();
    _rootIndex = ((NodeGroup *) _root)->children_Index();
    _running = false;
    _recording = false;
    _tempSave = false;

    _defaultViewpoint = (NodeViewpoint *) createNode("Viewpoint");
    _defaultViewpoint->ref();
    _currentViewpoint = _defaultViewpoint;
    _currentViewpoint->ref();
    _numProtoNames = 0; 
    _numProtoDefinitions = 0; 

    _navigationMode = true;

    _routeList.Init();

    _selectlevel=1;
    _hasFocus=false;

    _viewOfLastSelection=NULL;
    _selection_is_in_scene=false;
    _URL="";
    _errorLineNumber = -1;

    _obj3dCursor = gluNewQuadric();

    _multipleUndoTop = -1;
    _backupCommandList = NULL;

    _use3dCursor = false;

    _nodesWithExternProto.append("Contour2D");
    _nodesWithExternProto.append("CoordinateDeformer");
    _nodesWithExternProto.append("InlineLoadControl");
    _nodesWithExternProto.append("LoadSensor");
    _nodesWithExternProto.append("NurbsCurve");
    _nodesWithExternProto.append("NurbsCurve2D");
    _nodesWithExternProto.append("NurbsGroup");
    _nodesWithExternProto.append("NurbsPositionInterpolator");
    _nodesWithExternProto.append("NurbsSurface");
    _nodesWithExternProto.append("NurbsTextureSurface");
    _nodesWithExternProto.append("Polyline2D");
    _nodesWithExternProto.append("TrimmedSurface");
    _nodesWithExternProto.append("SuperEllipsoid");
    _nodesWithExternProto.append("SuperExtrusion");
    _nodesWithExternProto.append("SuperShape");
    _protoPrefix = NULL; 
    _externProtoWarning = true;
    TheApp->readProtoLibrary(this);
    _isParsing = false;
}

Scene::~Scene()
{
    int           i;

    TheApp->SetIntPreference("TransformMode",_transformMode->tmode);
    TheApp->SetIntPreference("TransformDimension",_transformMode->tdimension);
    TheApp->SetIntPreference("Transform2Axes",_transformMode->t2axes);

    while (!_undoStack.empty()) delete _undoStack.pop();

    while (!_redoStack.empty()) delete _redoStack.pop();

    _defaultViewpoint->unref();
    _currentViewpoint->unref();

    delete _selection;
    _selection = NULL;    

    _root->unref();

    ProtoMap::Chain::Iterator        *j;

    for ( i = 0; i < _protos.width(); i++) {
        for ( j = _protos.chain(i).first(); j != NULL; j = j->next()) {
          delete j->item()->getData();
      }
    }
    for (i = 0; i < _fonts.size(); i++) {
      delete _fonts[i];
    }
    gluDeleteQuadric(_obj3dCursor);
}

void Scene::def(const char *nodeName, Node *value)
{
    if (value) {
      _nodeMap[nodeName] = value;
      value->setName(nodeName);
    }
}

void Scene::undef(MyString nodeName)
{
    if (nodeName!="")
       _nodeMap[nodeName] = NULL;
}

Node *Scene::use(const char *nodeName)
{
    return _nodeMap[nodeName];
}

int
Scene::addSymbol(MyString s)
{
    int           &id = _symbols[s];

    if (id == 0) {
      id = _numSymbols++;
      _symbolList[id] = s;
    }
    return id;
}

// store begin of multiple undo commands 
// this can be used for later optimization

void 
Scene::startMultipleUndo()
{
    _multipleUndoTop = _undoStack.getTop();
}

// optimize multiple undo commands
// read stack and throw out all unneeded commands
// remember: backup commands contain setField(node, field) information
// a setField(node, field) is erased by a later setField(node, field) 
// WARNING ! 

void               
Scene::optimizeMultipleUndo()
{
   int i;
   // Take care, that the optimization mechanism for the commandStack 
   // do not try to optimize to handle already deleted nodes
   // prove if all commands on the undoStack till _multipleUndoTop
   // are not MOVE_COMMANDs (but FIELD_COMMANDs)
   if (_multipleUndoTop == -1)
       return;
   for (i = _undoStack.getTop(); i > _multipleUndoTop; i--)
       if (_undoStack.peek(i)->getType() != FIELD_COMMAND) {
           _multipleUndoTop = -1;
           return;
   }
   CommandStack tmpStack;
   while (_undoStack.getTop() > _multipleUndoTop) {
       if (_undoStack.empty()) {
           _multipleUndoTop = -1;
           return;
       }
       Command *stackCommand = _undoStack.pop();
       bool storeStackCommandToTmpStack = true;
       for (i = tmpStack.getTop(); i > 0; i--) {
           Command *tmpCommand = tmpStack.peek(i);
           if (   (stackCommand->getType() == FIELD_COMMAND)
               && (tmpCommand->getType() == FIELD_COMMAND)) {
               FieldCommand *fieldCommand = (FieldCommand *)tmpCommand;
               FieldCommand *fstackCommand = (FieldCommand *)stackCommand;
               if (   (fstackCommand->getNode() == fieldCommand->getNode())
                   && (fstackCommand->getField() == fieldCommand->getField()) ){
                   // there is already a command for this node and field
                   // in the tmpstack, ignore this command
                   storeStackCommandToTmpStack = false;
                   break;
               }
           }
       }
       if (storeStackCommandToTmpStack)
           tmpStack.push(stackCommand);
   }
   // store content of tmpStack back to _undoStack
   while (!tmpStack.empty())
       _undoStack.push(tmpStack.pop());
   _multipleUndoTop = -1;
}


const MyString &
Scene::getSymbol(int id) const
{
    return _symbolList[id];
}

void
Scene::setNodes(NodeList *nodes)
{
    ((NodeGroup *)_root)->children(new MFNode(nodes));
}

void
Scene::addNodes(Node *target, NodeList *nodes)
{
    if (target == NULL) {
        _root->addFieldNodeList(_rootIndex, nodes);
    } else
        target->addFieldNodeList(-1, nodes);
    scanForInlines(nodes);
}

void 
Scene::scanForInlines(NodeList *nodes)
{
    if (nodes == NULL) 
        return;
    for (int i=0; i < nodes->size(); i++) {
        Node    *node = nodes->get(i);
        if ((node->getType() == NODE_INLINE) ||
            (node->getType() == NODE_INLINE_LOAD_CONTROL)) {
            if (TheApp->loadNewInline())
                readInline((NodeInline *)node);
        } else {
            for (int j = 0; j < node->getProto()->getNumFields(); j++) {
                FieldValue *field = node->getField(j);
                if (field->getType() == MFNODE) {
                  NodeList *childList = ((MFNode *) field)->getValues();
                    scanForInlines(childList);
                }
            }
        }
    }
}

void 
Scene::readInline(NodeInline *node)
{
    MFString *urls = node->url();
    for (int j = 0; j < urls->getSize(); j++) {
        if (urls->getValue(j).length() == 0) 
            continue;
        URL url(getURL(), urls->getValue(j));
        MyString path;
        if (Download(url, &path)) {
            struct stat fileStat;
            const char *filename = path;
            if (stat(filename, &fileStat) == 0) {
                if (S_ISREG(fileStat.st_mode)) {
                    URL       importURL;
                    importURL.FromPath(filename);
                    TheApp->setImportURL(importURL);
                    FILE *file = fopen(filename, "r");
                    parse(file, false, node);
                    fclose(file);                             
                    break;
                }
            }
        }
    }    
}

int
Scene::writeRouteStrings(int f)
{
    if (_routeList.size()!=0) {
       for (List<MyString>::Iterator* routepointer = _routeList.first();
            routepointer != NULL; routepointer = routepointer->next() ) 
          {
          RET_ONERROR( mywritestr(f ,(const char*) routepointer->item()) )
          RET_ONERROR( mywritestr(f ,"\n") )
          TheApp->incSelectionLinenumber();
          }
       RET_ONERROR( mywritestr(f ,"\n") )
       TheApp->incSelectionLinenumber();
       _routeList.removeAll();
    }
    return 0;
}

int Scene::writeExternProto(int f, char* protoName)
{
    // search if EXTERNPROTO already exist
    int i;
    bool foundProto = false;
    for (i = 0;i < _numProtoNames;i++)
        if (strcmp((const char*)_protoNames[i], protoName)==0)
            foundProto = true;   

    if (!foundProto) {
        // write EXTERNPROTO
        const NodeList *nodes = getNodes();
        for (i = 0; i < nodes->size(); i++) {
            Node    *node = nodes->get(i);
            if (node->isInScene(this)) {
                const char *nodeName = node->getProto()->getName();
                if (strcmp(nodeName, protoName)==0) {
                    RET_ONERROR( node->writeEXTERNPROTO(f) )
                    RET_ONERROR( mywritestr(f ,"\n\n") )
                    TheApp->incSelectionLinenumber(2);
                    break;
                }
            }
        }
    }
    return 0;
}

bool avoidProto(MyString name)
{
    if (stringncmp(name, "Nurbs") == 0)
        return true;
    if (stringncmp(name, "Super") == 0)
        return true;
    if (strcmp(name, "Contour2D") == 0)
        return true;
    if (strcmp(name, "CoordinateDeformer") == 0)
        return true;
    if (strcmp(name, "Polyline2D") == 0)
        return true;
    if (strcmp(name, "TrimmedSurface") == 0)
        return true;
    return false;
}

int Scene::write(int f, const char *url, bool tempSave, bool pureVRML97)
{
    _newURL = url;
    _tempSave = tempSave;

    int i;

    _pureVRML97 = pureVRML97;

    getNodes()->clearFlag(NODE_FLAG_DEFED);
    getNodes()->clearFlag(NODE_FLAG_TOUCHED);

    // remove multiple identical ProtoDefinitions smuggled in by Inline nodes
    for (i = 0;i < _numProtoDefinitions;i++) {
        for (int j = 0;j < _numProtoDefinitions;j++) 
           if (i != j)
               if (strcmp((const char*)_protoDefinitions[i],
                         (const char*)_protoDefinitions[j]) == 0)
                   _protoDefinitions[j] = "";
    }

    RET_ONERROR( mywritestr(f ,"#VRML V2.0 utf8\n\n") )
    TheApp->incSelectionLinenumber(2);
    for (i = 0;i < _numProtoDefinitions;i++) {
        if (pureVRML97 && avoidProto(_protoNames[i])) {
            bool found = false;
            for (int j = 0; j < _nodesWithExternProto.size(); j++)
                if (strcmp((const char *)_protoNames[i], 
                           _nodesWithExternProto[j]) == 0)
                    found = true;
            if (found == true)
                continue;
        }
        if (getProtoPrefix() != NULL) {
            MyString prefix = getProtoPrefix();
            bool isPrefixProto = false;
            for (int j = 0; j < _numProtoDefinitions; j++) {
                if (strncmp(_protoNames[j], prefix, prefix.length()) == 0)
                    if (strcmp(_protoNames[j], _protoNames[i]) == 0)
                        isPrefixProto = true;
            }
            if (!isPrefixProto) {
                for (int j = 0; j < _numProtoDefinitions; j++) {
                    if (strncmp(_protoNames[j], prefix, prefix.length()) == 0)
                        _protoDefinitions[i].gsubOnce(
                              _protoNames[j] + prefix.length(), 
                              _protoNames[j]);
                }
            }
        }   
        RET_ONERROR( _protoDefinitions[i].write(f) )
        // count end of line characters in protodefinitions
        char* string=(char*) ((const char*) _protoDefinitions[i]);
        while ((string=strchr(string, '\n')) !=NULL) {
            TheApp->incSelectionLinenumber();
            string++;
            }
        RET_ONERROR( mywritestr(f ,"\n\n") )
        TheApp->incSelectionLinenumber(2);
    }

    for (int j = 0; j < _nodesWithExternProto.size(); j++) {
        // do not write EXTERN PROTOs for Nurbs Nodes when using pureVRML97
        if ((!pureVRML97) || (!avoidProto(_nodesWithExternProto[j]))) 
            RET_ONERROR( writeExternProto(f, _nodesWithExternProto[j]) )
    }

    _nodes.clearFlag(NODE_FLAG_TOUCHED);

    NodeList   *childList = ((NodeGroup *)getRoot())->children()->getValues();
    for (i = 0; i < childList->size(); i++) {
      RET_ONERROR( childList->get(i)->write(f, 0) )
    }
    if ((!tempSave) && (!pureVRML97)) {
      _unmodified = _undoStack.empty() ? (Command *) NULL : _undoStack.peek();
        _extraModifiedFlag = false;
      _URL = url;
    }
    RET_ONERROR( writeRouteStrings(f) )
    return(0);
}

void
Scene::addProto(MyString name, Proto *value)
{
    if (_protos[name] == NULL) {
      _protos[name] = value;
    } else {
      delete value;
    }
}

Proto *
Scene::getProto(MyString name)
{
    return _protos[name];
}

// static
bool
Scene::validRoute(Node *src, int eventOut, Node *dst, int eventIn)
{
    bool onlyOneConnectAnything = false;
    // "connect anything" route of ScriptNode (eventOut will be created)
    if (src->getType() == NODE_SCRIPT)
        if (eventOut == src->getProto()->getNumEventOuts())
            onlyOneConnectAnything = true;

    // "connect anything" route of ScriptNode (eventIn will be created)
    if (dst->getType() == NODE_SCRIPT)
        if (eventIn == dst->getProto()->getNumEventIns())
            if (onlyOneConnectAnything)
                onlyOneConnectAnything = false;
            else
                onlyOneConnectAnything = true;
     
    if (onlyOneConnectAnything)
        return true;

    if (eventOut < 0 || eventOut >= src->getProto()->getNumEventOuts())
      return false;

    if (eventIn < 0 || eventIn >= dst->getProto()->getNumEventIns())
      return false;

    if (src->getProto()->getEventOut(eventOut)->getType() != 
      dst->getProto()->getEventIn(eventIn)->getType())
      return false;

    return true;
}

bool
Scene::addRoute(Node *src, int eventOut, Node *dst, int eventIn)
{
    if (!validRoute(src, eventOut, dst, eventIn)) return false;
    RouteUpdate         hint(src, eventOut, dst, eventIn);

    src->addOutput(eventOut, dst, eventIn);
    dst->addInput(eventIn, src, eventOut);

    // try to copy a exposedField value or field of a eventIn
    // to the first value of a Interpolator

    int field = -1;

    // is this dst field an ExposedField?
    ExposedField *e = dst->getProto()->getEventIn(eventIn)->getExposedField();
    if (e)
        field = e->getField();
    else {
         // is this dst field an EventIn connected to a Field ?
         field = dst->getProto()->getEventIn(eventIn)->getField();
    }
    if (field != -1) {
        Interpolator *interp = findUpstreamInterpolator(dst, field);
        if (interp) {
            if (interp->getNumKeys() == 0) {
                FieldValue *value = dst->getField(field);
                if (value)
                  interp->recordKey(value, true);
            }
        }
    }

    UpdateViews(NULL, UPDATE_ADD_ROUTE, (Hint *) &hint);
    return true;
}

void
Scene::deleteRoute(Node *src, int eventOut, Node *dst, int eventIn)
{
    if (eventOut < 0 || eventOut >= src->getProto()->getNumEventOuts())
      return;

    if (eventIn < 0 || eventIn >= dst->getProto()->getNumEventIns())
      return;

    RouteUpdate         hint(src, eventOut, dst, eventIn);

    src->removeOutput(eventOut, dst, eventIn);
    dst->removeInput(eventIn, src, eventOut);
    UpdateViews(NULL, UPDATE_DELETE_ROUTE, (Hint *) &hint);
}

void
Scene::errorf(const char *fmt, ...)
{
    va_list ap;
    char buf[1024], buf2[1024];
    const char *url = "";  

    va_start(ap, fmt);
    myvsnprintf(buf, 1024, fmt, ap);
    if (TheApp->getImportURL() != NULL)
        url = TheApp->getImportURL();
    mysnprintf(buf2, 1024, "%s %d: %s", url, lineno, buf);
    _compileErrors += buf2;
}

void
Scene::invalidNode(const char *name)
{
    errorf("invalid DEF name \"%s\"\n", name);
}

void 
Scene::invalidField(const char *node, const char *field)
{
    errorf("node \"%s\" has no field \"%s\", ignored", node, field);
}

const char *
Scene::parse(FILE *f, bool protoLibrary, Node* target)
{
#ifdef HAVE_LIBZ
    inputFile = gzdopen(fileno(f),"rb");
#else
    inputFile = f;   
#endif
    isInProtoLibrary = protoLibrary ? 1 : 0;
    scene = this;
    targetNode = target;
    lineno = 1;
    _compileErrors = "";
    _isParsing = true;
    yyparse();
    _isParsing = false;
    return _compileErrors;
}

void
Scene::add(Command *cmd)
{
    _undoStack.push(cmd);

    while (!_redoStack.empty()) {
      delete _redoStack.pop();
    }
}

void
Scene::execute(Command *cmd)
{
    cmd->execute();
    add(cmd);
}

void
Scene::backupFieldsStart(void)
{
    // check for forbidden recurive usage or not missing backupFieldsDone()
    assert(_backupCommandList == NULL);
    _backupCommandList = new CommandList();
}

void
Scene::backupFieldsAppend(Node *node, int field)
{
    if (isRecording()) {
      Interpolator          *interp = findUpstreamInterpolator(node, field);

      if (interp) {
          interp->backup(_backupCommandList);
      }
    }
    _backupCommandList->append(new FieldCommand(node, field));
}

void 
Scene::backupFieldsDone(void)
{
    add(_backupCommandList);
    _backupCommandList = NULL;
}

void
Scene::backupField(Node *node, int field)
{
    backupFieldsStart();
    backupFieldsAppend(node, field);
    backupFieldsDone();
}

Interpolator *
Scene::findUpstreamInterpolator(Node *node, int field) const
{
    // is this field an ExposedField?
    int eventIn = -1;
    Field *f = node->getProto()->getField(field);
    ExposedField *e = f->getExposedField();
    if (e)
        eventIn = e->getEventIn();
    else if (f->getEventIn() != -1)
        eventIn = f->getEventIn();
    if (eventIn != -1) {
      const SocketList::Iterator    *i;

      // check for interpolator routed to the corresponding EventIn;

      for (i = node->getInput(eventIn).first(); i != NULL; i = i->next()) {
          if (i->item()._node->getNodeClass() & INTERPOLATOR_NODE)
            return ((Interpolator *) i->item()._node);
      }
    }

    return NULL;
}

void
Scene::setField(Node *node, int field, FieldValue *value)
{
    if (isRecording()) {
      Interpolator          *interp = findUpstreamInterpolator(node, field);

      if (interp) {
          interp->recordKey(value,isRunning());
      }
    }
    node->setField(field, value);
    if (node->getProto() == _protos["TimeSensor"]) {
       NodeTimeSensor *nodeTimeSensor = (NodeTimeSensor *) node;
       nodeTimeSensor->updateStart(field, value, swGetCurrentTime());
    }
    OnFieldChange(node, field);
}

void
Scene::undo()
{
    if (_undoStack.empty()) {
      assert(false);
      return;
    }

    Command *change = _undoStack.pop();
    change->undo();
    _redoStack.push(change);
}

void
Scene::redo()
{
    if (_redoStack.empty()) {
      assert(false);
      return;
    }

    Command *change = _redoStack.pop();
    change->execute();
    _undoStack.push(change);
}

void
Scene::drawScene(bool pick, int x, int y)
{
    GLint   v[4];
    float   aspect;
    int           i;

    glGetIntegerv(GL_VIEWPORT, v);

    if (v[3])
      aspect = (GLfloat)v[2]/v[3];
    else    // don't divide by zero, not that we should ever run into that...
      aspect = 1.0f;

    _numLights = 0;
    _headlight = true;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    float pickRegionSize = PICK_REGION_SIZE * TheApp->GetHandleSize();
    if (pick) gluPickMatrix(x, y, pickRegionSize, pickRegionSize, v);
    float fieldOfView = RAD2DEG(getCamera()->fieldOfView()->getValue());
    if (TheApp->hasFixFieldOfView())
        fieldOfView = TheApp->getFixFieldOfView();
    gluPerspective(fieldOfView, aspect, TheApp->GetNearClippingPlaneDist(),
                                        TheApp->GetFarClippingPlaneDist());
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glClearColor(0.0F, 0.0F, 0.0F, 1.0F);
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glEnable(GL_LIGHTING);
    glEnable(GL_NORMALIZE);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    if (TheApp->GetRenderFasterWorse()) {
        glHint(GL_LINE_SMOOTH_HINT, GL_FASTEST);
        glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST);
        glHint(GL_POLYGON_SMOOTH_HINT, GL_FASTEST);
    }


// use later 
//    glDepthFunc(GL_ALWAYS);
// for a extra pass, which renders only transparent shapes

    // no scene ambient light, please
    GLint    zero[4] = { 0, 0, 0, 0 };
    glLightModeliv(GL_LIGHT_MODEL_AMBIENT, zero);

    // first pass:  pre-draw traversal
    // enable PointLights and SpotLights; pick up ViewPoints, Fogs.
    // Backgrounds and TimeSensors

    glPushMatrix();

    applyCamera();
    _viewpoints.resize(0);
    _fogs.resize(0);
    _backgrounds.resize(0);
    _timeSensors.resize(0);

    _root->preDraw();

    if (_fogs.size() > 0) ((NodeFog *) _fogs[0])->apply();
    glPopMatrix();

    // second pass:  main drawing traversal

    glPushMatrix();
    if (_headlight) enableHeadlight();
    glPushName(PICKED_NODE);
    if (_backgrounds.size() > 0) {
      glPushName(0);              // FIXME:  can't pick backgrounds, yet
      ((NodeBackground *) _backgrounds[0])->apply();
      glPopName();
    }
    applyCamera();
    _root->draw();

    for (i = 0; i < _numLights; i++) {
      glDisable((GLenum) (GL_LIGHT0 + i));
    }

    _numLights = 0;
    glDisable(GL_FOG);
    glPopMatrix();

    // post-draw phase
    // draw handles for all nodes in the current selection path

    glPushMatrix();

    enableHeadlight();
    applyCamera();

    glLoadName(PICKED_HANDLE);
    drawHandles();
    glPopName();

    glDisable(GL_LIGHT0);
    glPopMatrix();
}

void
Scene::draw3dCursor(int x, int y)
{
    if (!use3dCursor())
         return;

    float objX;
    float objY;
    float objZ;

    float eyeposition=0; 
    float eyeangle=0; 
    float nearPlane=TheApp->GetNearClippingPlaneDist();
    
    glPushMatrix();

    unProjectPoint(x, y, 0, &objX, &objY, &objZ);

    if (TheApp->useStereo())
       {
       // inexact "toe in" stereo method 
       if (TheApp->getEyeMode()==EM_RIGHT)
          {
          eyeposition= - TheApp->getEyeHalfDist();
          eyeangle= - TheApp->getEyeAngle();
          }
       else if (TheApp->getEyeMode()==EM_LEFT)
          {
          eyeposition= + TheApp->getEyeHalfDist();
          eyeangle= + TheApp->getEyeAngle();
          }
       }
    glTranslatef(-eyeposition, 0, 0);
    glRotatef(-eyeangle, 0,1,0);

    glPushAttrib(GL_ENABLE_BIT);
    glDisable(GL_LIGHTING);
    glDisable(GL_TEXTURE_2D);
    glEnable(GL_LINE_SMOOTH);
    glDisable(GL_BLEND);
    glLineWidth(TheApp->Get3dCursorWidth());

    glBegin(GL_LINE_STRIP);
    if (TheApp->isAnaglyphStereo())
        Util::myGlColor3f(1, 1, 1);
    else
        Util::myGlColor3f(0, 1, 0);
    glVertex3f(objX * 1.0/nearPlane, -objY * 1.0/nearPlane, objZ - 1);    
    glVertex3f(objX * 1.0/nearPlane, -objY * 1.0/nearPlane, objZ 
               - 1 - TheApp->Get3dCursorLength());
    glEnd();
    glEnable(GL_LIGHTING);
    glLineWidth(1);
    glPopAttrib();

    glPopMatrix();
}

Path *
Scene::pick(int x, int y)
{
    GLuint  pickBuffer[PICK_BUFFER_SIZE];

    glSelectBuffer(PICK_BUFFER_SIZE, pickBuffer);
    glRenderMode(GL_SELECT);
    glInitNames();

    TheApp->setEyeMode(EM_NONE);
    drawScene(true, x, y);

    int hits = glRenderMode(GL_RENDER);
    Path *path = NULL;
    if (hits)
        path = processHits(hits, pickBuffer);
    if (path != NULL) {
      return path;
    } else {
      return new Path(NULL, 0, this);
    }
}

Path *
Scene::processHits(unsigned hits, GLuint *pickBuffer)
{
    GLuint     *path = NULL;
    int           pathLen = 0;
    GLuint  depth = UINT_MAX;
    int         selectedLevel=0;

    _selectedHandle = -1;
//    _oldSelectedHandle = _selectedHandle;

    for (unsigned i = 0; i < hits; i++) {
      unsigned    numNames = *pickBuffer++;
      unsigned    minDepth = *pickBuffer++;
      /* unsigned    maxDepth = * */ pickBuffer++;
      if (*pickBuffer == PICKED_NODE) {
          if (_selectedHandle == -1 || minDepth < depth) {
            path = pickBuffer + 1;
            pathLen = numNames - 1;
            depth = minDepth;
// write here code to avoid the selection of the thing most away from user
// printf("selectedLevel %d _selectlevel %d\n",selectedLevel+1,_selectlevel);
//                if (selectedLevel++==_selectlevel)
//                   break;
          }
      } else if (*pickBuffer == PICKED_HANDLE) {
          path = pickBuffer + 1;
          pathLen = numNames - 2;
          _selectedHandle = pickBuffer[numNames-1];
            _oldSelectedHandle = _selectedHandle;
          depth = minDepth;
      } else if (*pickBuffer == PICKED_3DCURSOR) {
            return NULL;
        } else {
            return NULL;
          // assert(false);
      }
      pickBuffer += numNames;
    }
    assert( path != NULL );
    return new Path((int *) path, pathLen, this);
}

void
Scene::transform(const Path *path)
{
    assert(path != NULL);
    applyCamera();
    const NodeList    *nodes = path->getNodes();
    int           size = nodes->size() - 1;
    for (int i = 0; i < size; i++) {
      nodes->get(i)->transform();
    }
}

// search for a Transform node in a path
// return new path to Transform node or NULL if not found

Path* Scene::searchTransform(void)
{
    Path* transform=new Path(*_selection);
    if (transform != NULL) { 
        if (transform->getNode()->getType() == NODE_TRANSFORM)
            return transform;
        while (transform->getNode() != _root) {
            Path* old_transform=transform;
            transform=newPath(transform->getNode()->getParent());
            if (transform->getNode()->getType() == NODE_TRANSFORM)
                break;
            // delete old_transform; // bug: deleting a path cause a crash
        }
        if (transform->getNode()==_root)
            transform=NULL;
    }
    return transform;
}

void
Scene::drawHandles()
{
    glDisable(GL_DEPTH_TEST);
    switch (TheApp->GetHandleMode()) {
      case HM_NONE:
      break;
      case HM_TREE:
      if (_selection != NULL) {
          glPushMatrix();
          int     len = _selection->getPathLen();
          const int    *path = _selection->getPath();
          Node *node = _root;
          for (int i = 0; i < len;) {
            int field = path[i++];
            glPushName(field);
            FieldValue  *value = node->getField(field);
            if (i >= len) {
                break;
            }
            glPushName(path[i]);
            if (value->getType() == SFNODE) {
                node = ((SFNode *) value)->getValue(); i++;
            } else if (value->getType() == MFNODE) {
                node = ((MFNode *) value)->getValue(path[i++]);
            } else {
                assert(false);
            }
            node->drawHandles();
            node->transform();
          }
          glPopMatrix();
      }
      break;
      case HM_SELECTED:
      if (_selection != NULL) {
          glPushMatrix();
          int     len = _selection->getPathLen();
          const int    *path = _selection->getPath();
          Node *node = _root;
          Node *handlenode = _root;
            Node *lastnode = _root;
            {
          for (int i = 0; i < len;) {
            int field = path[i++];
            FieldValue  *value = node->getField(field);
            if (i >= len) {
                break;
            }
            if (value->getType() == SFNODE) {
                node = ((SFNode *) value)->getValue(); i++;
            } else if (value->getType() == MFNODE) {
                node = ((MFNode *) value)->getValue(path[i++]);
            } else {
                assert(false);
            }
                /* search last transform node in path */
                if (node->getType() == NODE_TRANSFORM)
                   handlenode=node;
                }
            }
            lastnode=node;
          node = _root;
            {
          for (int i = 0; i < len;) {
            int field = path[i++];
            glPushName(field);
            FieldValue  *value = node->getField(field);
            if (i >= len) {
                break;
            }
            glPushName(path[i]);
            if (value->getType() == SFNODE) {
                node = ((SFNode *) value)->getValue(); i++;
            } else if (value->getType() == MFNODE) {
                node = ((MFNode *) value)->getValue(path[i++]);
            } else {
                assert(false);
            }
                /* display last transform node in path */
                if ((node == handlenode) || (node==lastnode))
                   node->drawHandles();
                node->transform();
          }
            }
          glPopMatrix();
      }
      break;
      case HM_ALL:
      drawHandlesRec(_root);
      break;
    }
}

void
Scene::drawHandlesRec(Node *node) const
{
    int         numFields = node->getProto()->getNumFields();

    glPushMatrix();
    node->drawHandles();
    node->transform();
    for (int i = 0; i < numFields; i++) {
      FieldValue  *value = node->getField(i);
      if (value->getType() == SFNODE) {
          Node    *child = ((SFNode *) value)->getValue();
          if (child) {
            glPushName(i);
            glPushName(0);
            drawHandlesRec(child);
            glPopName();
            glPopName();
          }
      } else if (value->getType() == MFNODE) {
          glPushName(i);
          glPushName(0);
          MFNode  *v = (MFNode *) value;
          int          n = v->getSize();
          for (int j = 0; j < n; j++) {
            glLoadName(j);
            drawHandlesRec(v->getValue(j));
          }
          glPopName();
          glPopName();
      }
    }
    glPopMatrix();
}

void
Scene::enableHeadlight()
{
    GLenum light = (GLenum) allocateLight();
    static float  pos[4] = {0.0f, 0.0f, 1.0f, 0.0f};
    static float  ambientColor[4] = {0.0f, 0.0f, 0.0f, 1.0f};
    static float  diffuseColor[4] = {1.0f, 1.0f, 1.0f, 1.0f};

    glLightfv(light, GL_AMBIENT, ambientColor);
    glLightfv(light, GL_DIFFUSE, diffuseColor);
    glLightfv(light, GL_POSITION, pos);
    glLightfv(light, GL_SPECULAR, diffuseColor);
    glLightf(light, GL_SPOT_CUTOFF, 180.0f);
    glLightf(light, GL_SPOT_EXPONENT, 0.0f);
    glLightf(light, GL_CONSTANT_ATTENUATION, 1.0f);
    glLightf(light, GL_LINEAR_ATTENUATION, 0.0f);
    glLightf(light, GL_QUADRATIC_ATTENUATION, 0.0f);
    glEnable(light);
}

// allocateLight()
//
// reserve an openGL light

int
Scene::allocateLight()
{
    GLint         maxLights;
    glGetIntegerv(GL_MAX_LIGHTS, &maxLights);

    if (_numLights >= maxLights) {
      errorf("too many lights!");
      return GL_LIGHT0;
    }
    
    return (GL_LIGHT0 + _numLights++);
}

int
Scene::freeLight()
{
    return GL_LIGHT0 + --_numLights;
}

void
Scene::projectPoint(float x, float y, float z, float *wx, float *wy, float *wz)
{
    GLdouble      mmat[16], pmat[16];
    GLdouble      winx, winy, winz;
    GLint   viewport[4];

    glGetDoublev(GL_MODELVIEW_MATRIX, mmat);
    glGetDoublev(GL_PROJECTION_MATRIX, pmat);
    glGetIntegerv(GL_VIEWPORT, viewport);

    gluProject(x, y, z, mmat, pmat, viewport, &winx, &winy, &winz);

    *wx = (float) winx;
    *wy = (float) winy;
    *wz = (float) winz;
}

void
Scene::unProjectPoint(float wx, float wy, float wz, float *x, float *y, float *z)
{
    GLdouble      mmat[16], pmat[16];
    GLdouble      objx, objy, objz;
    GLint   viewport[4];

    glGetDoublev(GL_MODELVIEW_MATRIX, mmat);
    glGetDoublev(GL_PROJECTION_MATRIX, pmat);
    glGetIntegerv(GL_VIEWPORT, viewport);

    gluUnProject(wx, wy, wz, mmat, pmat, viewport, &objx, &objy, &objz);

    *x = (float) objx;
    *y = (float) objy;
    *z = (float) objz;
}

void
Scene::addViewpoint(Node *viewpoint)
{
    _viewpoints.append(viewpoint);
}

void
Scene::addFog(Node *fog)
{
    _fogs.append(fog);
}

void
Scene::addBackground(Node *background)
{
    _backgrounds.append(background);
}

void
Scene::addTimeSensor(Node *timeSensor)
{
    _timeSensors.append(timeSensor);
}

void
Scene::moveCamera(float dx, float dy, float dz)
{
    Vec3f   pos = _currentViewpoint->getPosition();
    Quaternion    rot = _currentViewpoint->getOrientation();

/*    if (!isRecording() && _currentViewpoint != _defaultViewpoint) {
      _currentViewpoint = _defaultViewpoint;
      _currentViewpoint->setOrientation(rot);
    }
*/
    _currentViewpoint->setPosition(pos + rot * Vec3f(dx, dy, dz));
}

void
Scene::turnCamera(float x, float y, float z, float ang)
{
    Quaternion        rot = _currentViewpoint->getOrientation();
    Quaternion        r(Vec3f(x, y, z), ang);

    _currentViewpoint->setOrientation(r * rot);
}

void
Scene::orbitCamera(float dtheta, float dphi)
{    
/*
    if (!isRecording() && _currentViewpoint != _defaultViewpoint) {
      _currentViewpoint = _defaultViewpoint;
    }
*/
    Vec3f   pos(_currentViewpoint->getPosition());
    Quaternion    rot(_currentViewpoint->getOrientation());
    Quaternion    up(Vec3f(0.0f, 1.0f, 0.0f), dtheta);
    Quaternion    around(Vec3f(1.0f, 0.0f, 0.0f), dphi);
    Quaternion    newRot(up * around * rot);
    newRot.normalize();

    Vec3f   newPos(rot.conj() * newRot * pos);

    if (TheApp->GetMouseMode() == MOUSE_EXAMINE) {
        //  fixme: with this line: a buggy examine mode, without a fly mode
        _currentViewpoint->setPosition(newPos);
    }
    _currentViewpoint->setOrientation(newRot);

}

Node *
Scene::createNode(const char *nodeType)
{
    Proto *def = _protos[nodeType];

    return def ? def->create(this) : (Node *) NULL;
}

void
Scene::addNode(Node *node)
{
    _nodes.append(node);
}

MyString
Scene::getUniqueNodeName(Node *node)
{
    static char buf[512];
    const char *name = node->getProto()->getName();

    for (int i = 1; ; i++) {
      mysnprintf(buf, 512, "%s%d", name, i);
      if (use(buf) == NULL) break;
    }
    MyString ret = "";
    ret += buf;
    return ret;
}

MyString
Scene::generateUniqueNodeName(Node *node)
{
    MyString name = strdup(getUniqueNodeName(node));
    def(name, node);
    return name;
}

void
Scene::removeNode(Node *node)
{
    int index = _nodes.find(node);
    _nodes.remove(index);
}

void
Scene::setSelection(const Path *path)
{
    _selection_is_in_scene = true;
    _viewOfLastSelection = NULL;
    if (_selection != path) {
      delete _selection;
      _selection = path;
      _nodes.clearFlag(NODE_FLAG_SELECTED);
      _selection->getNodes()->setFlag(NODE_FLAG_SELECTED);
        Node *node = _selection->getNode();
        if (node == NULL) {
            setSelection(getRoot());
            return;
        }   
        if (node->getType() == NODE_VIEWPOINT) {
            _currentViewpoint = (NodeViewpoint *)node;
            applyCamera();
        }
    }
}

Path*
Scene::newPath(Node *node)
{
    Path* ret;
    int           len = 0;
    Node       *n;

    for (n = node; n->hasParent() ; n = n->getParent()) {
      len += 2;
    }

    if (len > 0) {
      int      *list = new int[len];

      int i = len-1;
    
      for (n = node; n->hasParent(); n = n->getParent()) {
          Node       *parent = n->getParent();
          int         field = n->getParentField();
          list[i--] = parent->findChild(n, field);
          list[i--] = field;
      }

      ret=new Path(list, len, this);
      delete [] list;
    } else {
      // select root node
      ret=new Path(NULL, 0, this);
    }
    return ret;
}

void
Scene::setSelection(Node *node)
{
    setSelection(newPath(node));
}

bool
Scene::isModified() const
{
    if (_undoStack.empty()) {
        if (_unmodified != NULL)
            return TRUE;
        else
            return _extraModifiedFlag;
    } else {
      return _unmodified != _undoStack.peek();
    }
}

void
Scene::applyCamera()
{
    _currentViewpoint->apply();
}

void
Scene::start()
{
    _running = true;
    if (_viewpoints.size() > 0) {
      _currentViewpoint = (NodeViewpoint *) _viewpoints[0];
    } else {
      _currentViewpoint = _defaultViewpoint;
    }
    double  t = swGetCurrentTime();

    for (int i = 0; i < _timeSensors.size(); i++) {
      ((NodeTimeSensor *) _timeSensors[i])->start(t);
    }
}

void
Scene::stop()
{
    _running = false;
}

void
Scene::updateTime()
{
    double  t = swGetCurrentTime();

    for (int i = 0; i < _timeSensors.size(); i++) {
      ((NodeTimeSensor *) _timeSensors[i])->setTime(t);
    }
    UpdateViews(NULL, UPDATE_TIME);
}

NodeViewpoint *
Scene::getCamera() const
{
    return _currentViewpoint;
}

void
Scene::AddView(SceneView *view)
{
    _views.append(view);
}

void
Scene::RemoveView(SceneView *view)
{
    _views.remove(_views.find(view));
}

void
Scene::OnFieldChange(Node *node, int field, int index)
{
    FieldUpdate         hint(node, field, index);

    UpdateViews(NULL, UPDATE_FIELD, (Hint *) &hint);
}

void
Scene::OnAddNode(Node *node, Node *dest, int field)
{
    NodeUpdate          hint(node, dest, field);

    UpdateViews(NULL, UPDATE_ADD_NODE, (Hint *) &hint);
}

void
Scene::OnRemoveNode(Node *node, Node *src, int field)
{
    NodeUpdate          hint(node, src, field);

    UpdateViews(NULL, UPDATE_REMOVE_NODE, (Hint *) &hint);
}

void
Scene::UpdateViews(SceneView *sender, int type, Hint *hint)
{
    for (List<SceneView *>::Iterator *i = _views.first(); i != NULL; i = i->next())
    {
      SceneView   *view = i->item();
      if (view != sender)
      {
          view->OnUpdate(sender, type, hint);
      }
    }
}

void
BackupRoutesRec(Node *node, CommandList *list)
{
    int                         i;
    SocketList::Iterator       *j;

    if (!node) return;

    if (node->getNumParents() > 1) return;

    for (i = 0; i < node->getProto()->getNumEventIns(); i++) {
      for (j = node->getInput(i).first(); j != NULL; j = j->next()) {
          const Socket            &s = j->item();
          list->append(new UnRouteCommand(s._node, s._index,
                                  node, i));
      }
    }
    for (i = 0; i < node->getProto()->getNumEventOuts(); i++) {
      for (j = node->getOutput(i).first(); j != NULL; j = j->next()) {
          const Socket      &s = j->item();
          list->append(new UnRouteCommand(node, i, 
                                  s._node, s._index));
      }
    }

    for (i = 0; i < node->getProto()->getNumFields(); i++) {
      FieldValue  *v = node->getField(i);
      if (v->getType() == SFNODE) {
          BackupRoutesRec(((SFNode *) v)->getValue(), list);
      } else if (v->getType() == MFNODE) {
          int         size = ((MFNode *) v)->getSize();
          for (int k = 0; k < size; k++) {
            BackupRoutesRec(((MFNode *) v)->getValue(k), list);
          }
      }
    }
}

void
Scene::DeleteSelected()
{
//    Node *parent = _selection->getParent();
    if ((_selection->getField() == -1 && _selection->getParentField() != -1)) {
        CommandList *list = new CommandList();
        DeleteSelectedAppend(list);
        execute(list);
//        setSelection(parent);
    }    
}

void
Scene::DeleteSelectedAppend(CommandList* list)
{
    if (_selection && (_selection->getNode() != _root)) {
      Node   *node = _selection->getNode();
      int     field = _selection->getField();
      Node   *parent = _selection->getParent();
      int     parentField = _selection->getParentField();

      if (field == -1 && parentField != -1) {
          if (node->getNumParents() == 1) {
            BackupRoutesRec(node, list);
          }
          list->append(new MoveCommand(node, parent, parentField, NULL, -1));
      }
    }
}

int
Scene::OnDragOver(Node *src, Node *srcParent, int srcField, 
              Node *dest, int destField, int modifiers)
{
    int     rc = 0;

    if (src && dest) {
      if (destField == -1) destField = dest->findValidField(src);
      if (dest->validChildType(destField, src->getNodeClass()) ||
          dest->validChildType(destField, src->getType())) {
          if ((modifiers & SW_CONTROL) && (modifiers & SW_SHIFT)
            && dest != src && !dest->hasAncestor(src)) {
            rc = SW_DRAG_LINK;
          } else if (modifiers & SW_CONTROL) {
            rc = SW_DRAG_COPY;
          } else if (dest != src
               && !dest->hasAncestor(src)
               && dest->findChild(src, destField) == -1) {
            rc = SW_DRAG_MOVE;
          }
      }
    }
    return rc;
}

int
Scene::OnDrop(Node *src, Node *srcParent, int srcField, 
            Node *dest, int destField, int modifiers)
{
    int effect = OnDragOver(src, srcParent, srcField, dest, destField, modifiers);
    if (src && dest) {
      if (destField == -1) destField = dest->findValidField(src);
      switch(effect) {
        case SW_DRAG_COPY:
          execute(new MoveCommand(src->copy(), NULL, -1, dest, destField));
            src->reInit();
          break;
        case SW_DRAG_MOVE:
          execute(new MoveCommand(src, srcParent, srcField, dest, destField));
          break;
        case SW_DRAG_LINK:
          execute(new MoveCommand(src, NULL, -1, dest, destField));
          break;
      }
      return 1;
    } else {
      return 0;
    }
}

bool
Scene::Download(const URL &url, MyString *path)
{
    *path = url.ToPath();
    return true;
}

FontInfo *
Scene::LoadGLFont(const char *fontName, const char *style)
{
    int           i;
    int           styleId;

    // handle "special" font names
    if (!strcmp(fontName, "SERIF")) {
      fontName = "Times New Roman";
    } else if (!strcmp(fontName, "SANS")) {
      fontName = "Arial";
    } else if (!strcmp(fontName, "TYPEWRITER")) {
      fontName = "Courier New";
    }
    
    if (!strcmp(style, "BOLD")) {
      styleId = SW_BOLD;
    } else if (!strcmp(style, "ITALIC")) {
      styleId = SW_ITALIC;
    } else if (!strcmp(style, "BOLDITALIC")) {
      styleId = SW_BOLD | SW_ITALIC;
    } else {
      styleId = SW_PLAIN;
    }

    // look for font in cache

    for (i = 0; i < _fonts.size(); i++) {
      if (!strcmp(_fonts[i]->name, fontName) && _fonts[i]->style == styleId) {
          return _fonts[i];
      }
    }

    // create some font outlines

    FontInfo        *info = new FontInfo();

    info->displayListBase =
      swLoadGLFont(fontName, styleId, info->kernX, info->kernY);

    if (info->displayListBase == 0) {
      delete info;
      return NULL;
    } else {
      info->name = fontName;
      info->style = styleId;
      _fonts.append(info);
      return info;
    }
}

bool                
Scene::addProtoName(MyString name)
{
   for (int i=0;i<_numProtoNames;i++)
       if (name == _protoNames[i]) {
           for (int j = 0; j < _nodesWithExternProto.size(); j++)
              if (strcmp(name, _nodesWithExternProto[j]) == 0)
                  return true;
           return false;
       }
   _protoNames[_numProtoNames++] = name;
   return true;
}

void
Scene::addProtoDefinition(void)
{
   _protoDefinitions[_numProtoDefinitions++] = "";
}

void 
Scene::addToProtoDefinition(char* string)
{
   _protoDefinitions[_numProtoDefinitions-1] += string;
}

void 
Scene::addRouteString(MyString string)
{
   _routeList.append(string);
}

void                
Scene::setViewOfLastSelection(SceneView* view)
{ 
   _viewOfLastSelection=view; 
   _selection_is_in_scene=false;
}

SceneView* Scene::getViewOfLastSelection(void)
{ 
   return _viewOfLastSelection; 
}

void Scene::DeleteLastSelection(void)
{
   if (_selection_is_in_scene) {
      // do delete in scene 
      DeleteSelected();      
   } else {
      // do delete in View
      SceneView* view = getViewOfLastSelection();
      if (view != NULL)
          view->DeleteLastSelection();
   }
}

/* check if name of node is already used in scene */

bool Scene::hasAlreadyName(MyString name)
{
    const NodeList *nodes = getNodes();
    for (int i = 0; i < nodes->size(); i++) {
      if (name == nodes->get(i)->getName())
            return true;
    }
    return false;
}

/* update URLs in Nodes of scene to new url */

void Scene::updateURLs(Node* node)
{
    if (node==NULL) return;
    for (int i=0;i<node->getProto()->getNumFields();i++) {
        Field      *field = node->getProto()->getField(i);
        FieldValue *value = node->getField(i);
        if (value && !value->equals(field->getDefault())) {
            if ((field->getFlags() & FF_URL) && (!TheApp->GetKeepURLs())) {
                node->setField(i,node->rewriteField(value, 
                                                    TheApp->getImportURL(),
                                                    getURL()));
            }
        } 
    }
}

void Scene::saveProtoStatus(void)
{
   _statusNumProtoNames=_numProtoNames;
   _statusNumProtoDefinitions=_numProtoDefinitions;
}

void Scene::restoreProtoStatus(void)
{
   int i;
   for (i=_statusNumProtoNames+1;i<_numProtoNames;i++)
       _protoNames.remove(i);
   _numProtoNames=_statusNumProtoNames;
   for (i=_statusNumProtoDefinitions+1;i<_numProtoDefinitions;i++)
       _protoDefinitions.remove(i);
   _numProtoDefinitions=_statusNumProtoDefinitions;
}

StringArray *
Scene::getAllNodeNames(void)
{
    StringArray *ret = new StringArray();
    ProtoMap::Chain::Iterator *j;
    for (int i = 0; i < _protos.width(); i++) {
        for ( j = _protos.chain(i).first(); j != NULL; j = j->next()) {
          ret->append(j->item()->getKey());
      }
    }
    return ret;
}

bool
Scene::use3dCursor(void) 
{ 
    switch (TheApp->Get3dCursorMode()) {
      case CM_3DCURSOR_ALLWAYS:
         return _use3dCursor;
      case CM_3DCURSOR_RECORDING:
        if (isRecording())
            return _use3dCursor; 
        break;
      case CM_3DCURSOR_NOT_RUN:
        if (!isRunning())
            return _use3dCursor; 
        break;
    }
    return false;            
}

// the proto PREFIX is only needed for the illegal2vrml program
// undefined nodes named "something" are renamed to "PREFIXsomething"
// when the proto "PREFIXsomething" is defined

bool
Scene::setProtoPrefix(char* protoPrefix) 
{ 
    if (protoPrefix != NULL)
       _protoPrefix = protoPrefix; 
    else {
       // compare all protonames to find out a common prefix
       bool prefixFound = false;
       if (getNumProtoNames() > 1) {
           MyString prefix = "";
           for (int numChar = 0; numChar < _protoNames[0].length(); 
               numChar++) {
               char character = _protoNames[0][numChar];
               bool sameCharacter = true; 
               for (int i = 1; i < getNumProtoNames(); i++) {
                   if ((numChar >= _protoNames[i].length()) ||
                       (_protoNames[i][numChar] != character)) {
                       sameCharacter = false;
                       break;
                   }
               }
               if (sameCharacter) {
                   prefixFound = true;
                   prefix += character;
               } else 
                   break;
           }
           if (prefixFound) 
               _protoPrefix = strdup(prefix); 
       } 
       if (!prefixFound) { 
           errorf("can not find out prefix from only one node\n");
           errorf("prefix missing, use \"-prefix\" in commandline\n");
           return false;
       }
    }
    return true;
}

MyString
Scene::getNodeWithPrefix(const MyString &nodeType)
{
    MyString newNodeType = "";
    newNodeType += scene->getProtoPrefix();
    newNodeType += nodeType;
    return newNodeType;
}

void
Scene::setPathAllURL(const char *path)
{
    const NodeList *nodes = getNodes();
    for (int i = 0; i < nodes->size(); i++) {
        Node    *node = nodes->get(i);
        if (node->isInScene(this))
            for (int j = 0; j < node->getProto()->getNumFields(); j++) {
                Field *field = node->getProto()->getField(j);
                if ((field->getType() == MFSTRING) &&
                    ((field->getFlags() & FF_URL) != 0)) {
                    MFString* urls = (MFString *)node->getField(j);
                    for (int k = 0; k < urls->getSize(); k++) {
                        const char *urlk =  urls->getValue(k);
                        if (notJavascript(urlk) && notURN(urlk)) {
                            URL url(getURL(), urlk);
                            MyString *newURL = new MyString("");
                            if (strlen(path) != 0) {
                                *newURL += path;
                                *newURL += "/";
                            }
                            *newURL += url.GetFileName();
                            urls->setValue(k, *newURL);
                        }
                    }
                } 
            }
    }
}

Node *
Scene::convertConeToNurbs(Node* cone)
{
    Node *parent = cone->getParent();
    int field = cone->getParentField();
    if ((parent != NULL) && (field != -1)) {
        NodeNurbsSurface *nurbsNode = (NodeNurbsSurface *)
                                      cone->toNurbs(2, 2, 3, 2, 1);
        SFRotation rot(0,1,0,M_PI/2.0);
        nurbsNode->rotate(rot);
        execute(new MoveCommand(cone, parent, field, NULL, -1));
        execute(new MoveCommand(nurbsNode, NULL, -1, parent, field));
        return nurbsNode;
    }
    return NULL;
}


Generated by  Doxygen 1.6.0   Back to index