Logo Search packages:      
Sourcecode: 3depict version File versions  Download package

viscontrol.cpp

/*
 *    viscontrol.cpp - visualation-user interface glue code
 *    Copyright (C) 2010, D Haley 

 *    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 3 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.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "viscontrol.h"
#include "XMLHelper.h"

#include <list>
#include <stack>

#include "common.h"
#include "scene.h"
#include "drawables.h"

using std::list;
using std::stack;
//DEBUGGING
#include <iostream>
using std::cerr;
using std::endl;

//oh no! global. window to safeYield/ needs to be set
wxWindow *yieldWindow=0;
//Another global!
bool *abortVisCtlOp=0;

const float DEFAULT_MAX_CACHE_PERCENT=50;

bool wxYieldCallback()
{
      //Rate limit the updates
      //FIXME: deprecated. use wxStopwatch instead
      if(wxGetElapsedTime(false) > 75)
      {
            wxSafeYield(yieldWindow);
            wxStartTimer();
      }

      ASSERT(abortVisCtlOp);
      return !(*abortVisCtlOp);
}

VisController::VisController()
{
      targetScene=0;
      nextID=0; 
      targetPlots=0;
      targetRawGrid=0;
      doProgressAbort=false;
      maxCachePercent=DEFAULT_MAX_CACHE_PERCENT;
      cacheStrategy=CACHE_DEPTH_FIRST;
      ASSERT(!abortVisCtlOp);
      abortVisCtlOp=&doProgressAbort;
      amRefreshing=false;
      pendingUpdates=false;
}

VisController::~VisController()
{
      //clean up
      for(tree<Filter * >::iterator it=filters.begin(); 
                                    it!=filters.end(); it++)
      {
            delete *it;
      }


      //clean up the stash trees
      for(unsigned int ui=0;ui<stashedFilters.size();ui++)
      {
            tree<Filter *> t;
            t=stashedFilters[ui].second;
            for(tree<Filter * >::iterator it=t.begin(); it!=t.end(); it++)
                  delete *it;
      }

      //Delete the undo stack trees
      while(undoFilterStack.size())
      {
            //clean up
            for(tree<Filter * >::iterator it=undoFilterStack.back().begin(); 
                                          it!=undoFilterStack.back().end(); it++)
            {
                  delete *it;
            }

            undoFilterStack.pop_back();   
      }


}
      
00107 void VisController::setScene(Scene *theScene)
{
      targetScene=theScene;
      //Set a default camera as needed. We don't need to track its unique ID, as this is
      //"invisible" to the UI
      if(!targetScene->getNumCams())
      {
            Camera *c=new CameraPerspLookAt();
            unsigned int id;
            id=targetScene->addCam(c);
            targetScene->setActiveCam(id);
      }

      //Inform scene about vis control.
      targetScene->setViscontrol(this);
}
      
00124 void VisController::setYieldWindow(wxWindow *newYield)
{
      yieldWindow=newYield;
}

00129 unsigned int VisController::LoadIonSet(const std::string &filename, unsigned int &totalProgress,
                        unsigned int &filterProgress, const Filter* &curFilter)
{

      //Save current filter state to undo stack
      pushUndoStack();


      PosLoadFilter *p = new PosLoadFilter();

      p->setFilename(filename);

      tree<Filter *>::iterator it;
      //From version 1.45 of ttree.h the top element should be added
      // using 'insert'.      
      if(!filters.size())
            it=filters.insert(filters.begin(),p);
      else
            it=filters.insert_after(filters.begin(),p);



      //Append a new filter to the filter tree
      filters.append_child(it,new IonDownsampleFilter);

      return 0;
}

00157 void VisController::updateWxTreeCtrl(wxTreeCtrl *t, const Filter *visibleFilt)
{
      stack<wxTreeItemId> treeIDs;
      wxTreeItemId visibleTreeId;
      //Warning: this generates an event, 
      //most of the time (some windows versions do not according to documentation)
      t->DeleteAllItems();

      //Clear the mapping
      filterTreeMapping.clear();
      nextID=0;
      
      int lastDepth=0;
      //Add dummy root node. This will be invisible to wxTR_HIDE_ROOT controls
      wxTreeItemId tid;
      tid=t->AddRoot(wxT("TreeBase"));
      t->SetItemData(tid,new wxTreeUint(nextID));

      // Push on stack to prevent underflow, but don't keep a copy, 
      // as we will never insert or delete this from the UI 
      treeIDs.push(tid);

      nextID++;   
      //Depth first  add
      for(tree<Filter * >::pre_order_iterator filtIt=filters.begin();
                              filtIt!=filters.end(); filtIt++)
      {     
            //Push or pop the stack to make it match the iterator position
            if( lastDepth > filters.depth(filtIt))
            {
                  while(filters.depth(filtIt) +1 < (int)treeIDs.size())
                        treeIDs.pop();
            }
            else if( lastDepth < filters.depth(filtIt))
            {
                  treeIDs.push(tid);
            }
            

            lastDepth=filters.depth(filtIt);
      
            //This will use the user label or the type string.    
            tid=t->AppendItem(treeIDs.top(),
                  wxStr((*filtIt)->getUserString()));
            
            //If we have been given a child ID, and it matches this item.
            //remember this ID, because we need to ensure that it is visible later
            if(visibleFilt && *filtIt == visibleFilt)
                  visibleTreeId=tid;
            
            t->SetItemData(tid,new wxTreeUint(nextID));
      

            //Record mapping to filter for later reference
            filterTreeMapping.push_back(std::make_pair(nextID,*filtIt));
            nextID++;
      }

      if(visibleTreeId.IsOk())
      {
            t->EnsureVisible(visibleTreeId);
            t->SelectItem(visibleTreeId);
      }

      t->GetParent()->Layout();

}

00225 void VisController::updateFilterPropertyGrid(wxPropertyGrid *g,unsigned long long filterId)
{

      Filter *targetFilter;
      targetFilter=getFilterByIdNonConst(filterId);

      FilterProperties p;
      targetFilter->getProperties(p);


      g->clearKeys();
      g->setNumSets(p.data.size());
      //Create the keys for the property grid to do its thing
      for(unsigned int ui=0;ui<p.data.size();ui++)
      {
            for(unsigned int uj=0;uj<p.data[ui].size();uj++)
            {
                  g->addKey(p.data[ui][uj].first, ui,p.keys[ui][uj],
                              p.types[ui][uj],p.data[ui][uj].second);
            }
      }

      //Let the property grid layout what it needs to
      g->propertyLayout();
      
}

00252 const Filter* VisController::getFilterById(unsigned long long filterId) const
{
      return getFilterByIdNonConst(filterId);
}     

00257 Filter* VisController::getFilterByIdNonConst(unsigned long long filterId) const
{

      Filter *targetFilter=0;

      //Look up tree mapping 
      for(std::list<std::pair<unsigned long long, Filter *> >::const_iterator it=filterTreeMapping.begin();
                  it!=filterTreeMapping.end(); it++)
      {
            if(it->first == filterId)
            {
                  targetFilter=it->second;
                  break;      
            }
      }

      ASSERT(targetFilter);
      return targetFilter;
}     


00278 bool VisController::setFilterProperties(unsigned long long filterId, unsigned int set, 
                                    unsigned int key, const std::string &value, bool &needUpdate)
{
      //Save current filter state to undo stack
      pushUndoStack();
      
      Filter *targetFilter=0;
      targetFilter=getFilterByIdNonConst(filterId);

      if(!targetFilter->setProperty(set,key,value,needUpdate))
            return false;

      //If we no longer have a cache, and the filter needs an update, then we must
      //modify the downstream objects
      if(needUpdate)
      {
            for(tree<Filter * >::pre_order_iterator filtIt=filters.begin();
                                    filtIt!=filters.end(); filtIt++)
            {
                  if(targetFilter == *filtIt)
                  {
                        //Kill all caches
                        for(tree<Filter *>::pre_order_iterator it(filtIt);it!= filters.end(); it++)
                        {
                              //Do not traverse siblings
                              if(filters.depth(filtIt) >= filters.depth(it) && it!=filtIt )
                                    break;

                              (*it)->clearCache();
                        }

                        targetFilter->clearCache();
                  }
            }

      }

      return true;

}

00319 bool VisController::setCamProperties(unsigned long long camUniqueID, unsigned int set, 
                        unsigned int key, const std::string &value)
{
      return targetScene->setCamProperty(camUniqueID,key,value);
}

//MUST re-force an update after calling this; as it is an error
//to attempt to re-use this ID.
00327 void VisController::removeTreeFilter(unsigned long long tId )
{
      //Save current filter state to undo stack
      pushUndoStack();

      Filter *removeFilt=getFilterByIdNonConst(tId);

      ASSERT(removeFilt);     

      //Remove element and all children
      for(tree<Filter * >::pre_order_iterator filtIt=filters.begin();
                              filtIt!=filters.end(); filtIt++)
      {
            if(removeFilt == *filtIt)
            {

                  for(tree<Filter *>::pre_order_iterator it(filtIt);it!= filters.end(); it++)
                  {
                        //Do not traverse siblings
                        if(filters.depth(filtIt) >= filters.depth(it) && it!=filtIt )
                              break;
                        
                        //Delete the children filters.
                        delete *it;
                  }

                  //Remove the children from the tree
                  filters.erase_children(filtIt);
                  filters.erase(filtIt);
                  break;
            }

      }
}

00362 bool VisController::addFilter(Filter *f,unsigned long long parentId)
{
      //Save current filter state to undo stack
      pushUndoStack();

      Filter *parentFilter=0;

      parentFilter=getFilterByIdNonConst(parentId);

      tree<Filter *>::iterator it= std::find(filters.begin(),filters.end(),parentFilter);

      if(it == filters.end())
            return false;

      //Add the child to the tree
      filters.append_child(it,f);

      filterTreeMapping.push_back(make_pair(nextID,f));
      nextID++;
      return true;      
}

00384 void VisController::popPointerStack(std::list<const FilterStreamData *> &pointerTrackList,
                        std::stack<vector<const FilterStreamData * > > &inDataStack, unsigned int depth)
{

      while(inDataStack.size() > depth)
      {
            //Look at each filter pointer on this stack level.
            for(unsigned int ui=0; ui<inDataStack.top().size(); ui++)
            {
                  const FilterStreamData *thisData;
                  thisData=inDataStack.top()[ui];
                  //Search through the pointer list. If we find it,
                  //it is an unhandled pointer and should be erased.
                  //If not, it has been handled.
                  list<const FilterStreamData *>::iterator it;
                  it=find(pointerTrackList.begin(),pointerTrackList.end(),thisData);
                  if(it!=pointerTrackList.end())
                  {
                        ASSERT(thisData->cached ==0);

                        delete thisData;
                        
                        //Remove from tracking list. This has been erased
                        pointerTrackList.erase(it);
                        //Pointer should be only in the track list once.
                        ASSERT(find(pointerTrackList.begin(),
                              pointerTrackList.end(),thisData) == pointerTrackList.end());
                  }
            }
            //We no longer need this level
            inDataStack.pop();
      }
}

unsigned int VisController::refreshFilterTree(unsigned int &overallProgress, 
            unsigned int &filterProgress, const Filter* &curFilter,
            list<std::pair<Filter *,vector<const FilterStreamData * > > > &outData)
      
{
      amRefreshing=true;
      doProgressAbort=false;
      wxStartTimer();
      
      unsigned int errCode=0;

      if(!filters.size())
      {
            targetScene->clearObjs();
            return 0;   
      }

      // -- Build data streams --   
      vector< const FilterStreamData *> curData;
      stack<vector<const FilterStreamData * > > inDataStack;

      list<const FilterStreamData *> pointerTrackList;

      overallProgress=filterProgress=0;
      
      //Push some dummy data onto the stack to prime first-pass (call to refresh(..) requires stack
      //size to be non-zero)
      inDataStack.push(curData);




      //Keep redoing the refresh until the user stops fiddling with the filter tree.
      do
      {
            if(pendingUpdates)
            {
                  getFilterUpdates();
                  overallProgress=filterProgress=0;
            }

            //Depth-first search from root node, refreshing filters as we proceed.
            for(tree<Filter * >::pre_order_iterator filtIt=filters.begin();
                                    filtIt!=filters.end(); filtIt++)
            {

                  //Step 0 : Pop the cache until we reach our current level, 
                  //    deleting any pointers that would otherwise be lost.
                  //---
                  popPointerStack(pointerTrackList,
                        inDataStack,filters.depth(filtIt)+1);
                  //---

                  //Step 1: Set up the progress system
                  //---
                  curFilter=*filtIt;      
                  overallProgress++;
                  filterProgress=0;
                  //---
                  
                  //Step 2: Check if we should cache this filter or not.
                  //Get the number of bytes that the filter expects to use
                  //---
                  unsigned long long cacheBytes;
                  if(inDataStack.size())
                        cacheBytes=(*filtIt)->numBytesForCache(numElements(inDataStack.top()));
                  else
                        cacheBytes=(*filtIt)->numBytesForCache(0);

                  if(cacheBytes != (unsigned long long)(-1))
                  {
                        //As long as we have caching enabled, let us cache according to the
                        //selected strategy
                        switch(cacheStrategy)
                        {
                              case CACHE_NEVER:
                                    (*filtIt)->setCaching(false);
                                    break;
                              case CACHE_DEPTH_FIRST:
                                    (*filtIt)->setCaching(cacheBytes/(1024*1024) < maxCachePercent*getAvailRAM());
                                    break;

                        }
                  }
                  else
                        (*filtIt)->setCaching(false);

                  //---

                  //Step 3: Take the stack top, and turn it into "curdata" using the filter
                  //    record the result on the stack
                  //    We also record any Selection devices that are generated by the filter.
                  //    This is the guts of the system.
                  //---
                  //
                  filterProgress=0;
                  (*wxYieldCallback)();
                  if((*filtIt)->isEnabled())
                  {
                        //Take the stack top, filter it and generate "curData"
                        errCode=(*filtIt)->refresh(inDataStack.top(),
                                          curData,filterProgress,wxYieldCallback);
                  }

                  //Ensure that (1) yield is called, regardless of what filter does
                  //(2) yield is called after 100% update   
                  filterProgress=100;     
                  (*wxYieldCallback)();


                  //Retrieve the user interaction "devices", and send them to the scene
                  vector<SelectionDevice *> curDevices;
                  (*filtIt)->getSelectionDevices(curDevices);
                  targetScene->addSelectionDevices(curDevices);
                  curDevices.clear();

                  //check for any error in filter update (including user abort)
                  if(errCode)
                  {
                        //clear any intermediary pointers
                        popPointerStack(pointerTrackList,inDataStack,0);
                        ASSERT(pointerTrackList.size() == 0);
                        ASSERT(inDataStack.size() ==0);
                        amRefreshing=false;
                        return errCode;
                  }


                  //Update the filter output information
                  (*filtIt)->updateOutputInfo(curData);
                  
                  
                  //is this node a leaf of the tree?
                  bool isLeaf;
                  isLeaf=false;
                  for(tree<Filter *>::leaf_iterator leafIt=filters.begin_leaf();
                              leafIt!=filters.end_leaf(); leafIt++)
                  {
                        if(*leafIt == *filtIt)
                        {
                              isLeaf=true;
                              break;
                        }
                  }
            
                  //If this is not a leaf, keep track of intermediary pointers
                  if(!isLeaf)
                  {
                        //The filter will generate a list of new pointers. If any out-going data 
                        //streams are un-cached, track them
                        for(unsigned int ui=0;ui<curData.size(); ui++)
                        {
                              //Keep an eye on this pointer. It is not cached (owned) by the filter
                              //and needs to be tracked (for possible later deletion)
                              ASSERT(curData[ui]);

                              list<const FilterStreamData *>::iterator it;
                              it = find(pointerTrackList.begin(),pointerTrackList.end(),curData[ui]);
                              //Check it is not cached, and that we are not already tracking it.
                              if(!curData[ui]->cached && it!=pointerTrackList.end()) 
                              {
                                    //track pointer.
                                    pointerTrackList.push_back(curData[ui]);
                              }
                        }     
                        
                        //Put this in the intermediary stack, 
                        //so it is available for any other children at this leve.
                        inDataStack.push(curData);
                  }
                  else
                  {
                        //The filter has created an ouput. Record it for passing to updateScene
                        outData.push_back(make_pair(*filtIt,curData));
                        for(unsigned int ui=0;ui<curData.size();ui++)
                        {
                              //Search through the pointer list. If we find it,
                              //then it is handled by VisController::updateScene, 
                              //and we do not need to delete it using the stack
                              //if we don't find it, then it is a new pointer, and we still don't need
                              //to delete it. At any rate, we need to make sure that it is not
                              //deleted by the popPointerStack function.
                              list<const FilterStreamData *>::iterator it;
                              it=find(pointerTrackList.begin(),pointerTrackList.end(),
                                                curData[ui]);

                              cerr << "Pointer " << curData[ui] << " from " <<  (*filtIt)->getUserString() << " is now a filter output -- stopping tracking" << endl;
                              if(it!=pointerTrackList.end())
                                    pointerTrackList.erase(it);

                              //Pointer should be only in the track list once.
                              ASSERT(find(pointerTrackList.begin(),pointerTrackList.end(),
                                                curData[ui]) == pointerTrackList.end());
                        
                        }
                  }     

                  //Cur data is recorded either in outDta or on the data stack
                  curData.clear();
                  //---
                  
                  //check for any pending updates to the filter data
                  //in case we need to start again, user has changed something
                  if(pendingUpdates)
                        break;
            }

            popPointerStack(pointerTrackList,inDataStack,0);
      }while(pendingUpdates);
      
      
      //Pointer tracking list should be empty.
      ASSERT(pointerTrackList.size() == 0);
      ASSERT(inDataStack.size() ==0);


      //====Output scrubbing ===

      //Should be no duplicate pointers in output data.
      //(this makes preventing double frees easier, and
      // minimises unneccesary output)
      //Construct a single list of all pointers in output,
      //checking for uniqueness. Delete duplicates
      list<const FilterStreamData *> flatList;
      
      for(list<std::pair<Filter *,vector<const FilterStreamData * > > >::iterator 
                              it=outData.begin();it!=outData.end(); it++)
      {
            vector<const FilterStreamData *>::iterator itJ;

            itJ=it->second.begin();
            while(itJ!=it->second.end()) 
            {
                  //Each stream data pointer should only occur once in the entire lot.
                  if(find(flatList.begin(),flatList.end(),*itJ) == flatList.end())
                  {
                        flatList.push_back(*itJ);
                        itJ++;
                  }
                  else
                        itJ=it->second.erase(itJ);
            }
      }

      //outData List is complete, and contains only unique entries. clear the checking list.
      flatList.clear();

      //======

      return 0;
}

00670 void VisController::getFilterUpdates()
{
      vector<pair<const Filter *,SelectionBinding> > bindings;
      targetScene->getModifiedBindings(bindings);

      for(unsigned int ui=0;ui<bindings.size();ui++)
      {
#ifdef DEBUG
            bool haveBind;
            haveBind=false;
#endif
            for(tree<Filter *>::iterator it=filters.begin(); it!=filters.end();it++)
            {
                  if(*it  == bindings[ui].first)
                  {
                        //We are modifying the contents of
                        //the filter, this could make a chankge that
                        //modifies output so we need to invalidate 
                        //all subtree caches to force reprocessing
                        invalidateCache(*it);

                        (*it)->setPropFromBinding(bindings[ui].second);
#ifdef DEBUG
                        haveBind=true;
#endif
                        break;
                  }
            }

            ASSERT(haveBind);

      }

      //we have retrieved the updates.
      pendingUpdates=false;
}

00707 unsigned int VisController::updateScene(unsigned int &overallProgress, 
            unsigned int &filterProgress, const Filter* &curFilter)
{
      typedef std::pair<Filter *,vector<const FilterStreamData * > > filterOutputData;
      list<filterOutputData > refreshData;

      //Apply any remaining updates if we have them
      if(pendingUpdates)
            getFilterUpdates();
      targetScene->clearBindings();

      //Run the tree refresh system.
      //we will delete the pointers ourselves, rather than
      //using safedelete so we ca free memory as we go.
      unsigned int errCode=0;
      errCode=refreshFilterTree(overallProgress,filterProgress,
                              curFilter, refreshData);
      if(errCode)
            return errCode;

      //strip off the source filter information for
      //convenience in this routine
      list<vector<const FilterStreamData *> > outData;

      for(list<filterOutputData>::iterator it=refreshData.begin(); it!=refreshData.end(); it++)
            outData.push_back(it->second);

      // -- Build target scene --- 
      targetScene->clearObjs();
      targetScene->clearRefObjs();
      
      //Construct a new drawing object for ions
      vector<DrawManyPoints *> drawIons;

      //erase the contents of each plot in turn
      ASSERT(targetPlots);
      targetPlots->clear(true); //Clear, but preserve visibity information.

      ASSERT(amRefreshing);
      plotSelList->Clear();


      //Loop through the outputs from the filters to construct the input
      //to the scene & plot windows
      for(list<vector<const FilterStreamData *> > ::iterator it=outData.begin(); 
                                          it!=outData.end(); it++)
      {
            for(unsigned int ui=0;ui<it->size(); ui++)
            {
                  //Filter must specify whether it is cached or not. other values
                  //are inadmissible, but useful to catch uninitialised values
                  ASSERT((*it)[ui]->cached == 0 || (*it)[ui]->cached == 1);
                  switch((*it)[ui]->getStreamType())
                  {
                        case STREAM_TYPE_IONS:
                        {
                              //Create a new group for this stream. We have to have individual groups because of colouring.
                              DrawManyPoints *curIonDraw;
                              curIonDraw=new DrawManyPoints;
                              //Iterator points to vector. Typecast elements in vector to IonStreamData s
                              const IonStreamData *ionData;
                              ionData=((const IonStreamData *)((*it)[ui]));

                              //Can't just do a swap, as we need to strip the m/c value.
                              curIonDraw->addPoints(ionData->data);

                              //Set the colour from the ionstream data
                              curIonDraw->setColour(ionData->r,
                                                ionData->g,
                                                ionData->b,
                                                ionData->a);
                              //set the size from the ionstream data
                              curIonDraw->setSize(ionData->ionSize);
                              drawIons.push_back(curIonDraw);

                              ASSERT(ionData->cached == 1 ||
                                    ionData->cached == 0);

                              if(!ionData->cached)
                              {
                                    cerr << " Deleting ion data pointer :" << ionData << " in " <<  __FUNCTION__ << endl;
                                    delete ionData;
                              }

                              //Randomly shuffle the ion data before we draw it
                              curIonDraw->shuffle();
                              break;
                        }
                        case STREAM_TYPE_PLOT:
                        {
                              
                              //Draw plot
                              const PlotStreamData *plotData;
                              plotData=((PlotStreamData *)((*it)[ui]));

                              //Construct a new plot
                              unsigned int plotID;
                              plotID=targetPlots->addPlot(plotData->xyData,
                                                plotData->logarithmic); 
                              targetPlots->setType(plotID,plotData->plotType);
                              targetPlots->setColours(plotID,plotData->r,
                                                plotData->g,plotData->b);
                              targetPlots->setStrings(plotID,plotData->xLabel,
                                                plotData->yLabel,plotData->dataLabel);
                              targetPlots->setParentData(plotID,plotData->parent,
                                                plotData->index);

                              //Construct any regions that the pot may have
                              for(unsigned int ui=0;ui<plotData->regions.size();ui++)
                              {
                                    //add a region to the plot,
                                    //using the region data stored
                                    //in the plot stream
                                    targetPlots->addRegion(plotID,
                                          plotData->regions[ui].first,
                                          plotData->regions[ui].second,
                                          plotData->regionR[ui],
                                          plotData->regionG[ui],
                                          plotData->regionB[ui]);
                              }

                              //Append the plot to the list in the user interface
                              wxListUint *l = new wxListUint(plotID);
                              plotSelList->Append(wxStr(plotData->dataLabel),l);

                              ASSERT(plotData->cached == 1 ||
                                    plotData->cached == 0);
                              if(!plotData->cached)
                                    delete plotData;
                              
                              break;
                        }
                        case STREAM_TYPE_DRAW:
                        {
                              DrawStreamData *drawData;
                              drawData=((DrawStreamData *)((*it)[ui]));
                              
                              //Retrieve vector
                              const std::vector<DrawableObj *> *drawObjs;
                              drawObjs = &(drawData->drawables);
                              //Loop through vector, Adding each object to the scene
                              for(unsigned int ui=0;ui<drawObjs->size();ui++)
                                    targetScene->addDrawable((*drawObjs)[ui]);

                              //Although we do not delete the drawable objects
                              //themselves, we do delete the container
                              ASSERT(!drawData->cached);
                              //Zero-size the internal vector to 
                              //prevent destructor from deleting pointers
                              //we have transferrred ownership of to scene
                              drawData->drawables.clear();
                              cerr << "Deleting draw pointer :" << drawData << " in " << __FUNCTION__ << endl;
                              delete drawData;
                              break;
                        }
                        case STREAM_TYPE_RANGE:
                              break;

                        
                        default:
                              ASSERT(false);
                  }

            }
      
      }


      outData.clear();  
      //---

      //If there is only one spectrum, select it
      if(plotSelList->GetCount() == 1 )
            plotSelList->SetSelection(0);
      else if( plotSelList->GetCount() > 1)
      {
            //Otherwise try to use the last visibility information
            //to set the selection
            targetPlots->bestEffortRestoreVisibility();

            plotSelList->SetSelection(wxNOT_FOUND); //Clear selection
            for(unsigned int ui=0; ui<plotSelList->GetCount();ui++)
            {
                  wxListUint *l;
                  unsigned int plotID;

                  //Retreive the uniqueID
                  l=(wxListUint*)plotSelList->GetClientData(ui);
                  plotID = l->value;
                  if(targetPlots->isPlotVisible(plotID))
                        plotSelList->SetSelection(ui);
            }
      }


      updateRawGrid();
      //Construct an OpenGL display list from the dataset

      //Check how many points we have. Too many can cause the display list to crash
      size_t totalIonCount=0;
      for(unsigned int ui=0;ui<drawIons.size();ui++)
            totalIonCount+=drawIons[ui]->getNumPts();

      if(totalIonCount < MAX_NUM_DRAWABLE_POINTS)
      {
            DrawDispList *displayList;
            displayList = new DrawDispList();

            displayList->startList(false);
            for(unsigned int ui=0;ui<drawIons.size(); ui++)
                  displayList->addDrawable(drawIons[ui]);

            displayList->endList();
            
            targetScene->addDrawable(displayList);
            
            for(unsigned int ui=0;ui<drawIons.size(); ui++)
                  delete drawIons[ui];
      }
      else
      {
            for(unsigned int ui=0;ui<drawIons.size(); ui++)
                  targetScene->addDrawable(drawIons[ui]);
      }
      //clean up uncached data
      for(list<vector<const FilterStreamData *> > ::iterator it=outData.begin(); 
                                          it!=outData.end(); it++)
      {
            //iterator points to a vector of pointers, which are the filter stream 
            //data. We need to delete any uncached items before we quit
            for(unsigned int ui=0;ui<it->size(); ui++)
            {
                  if(!((*it)[ui])->cached)
                        delete ((*it)[ui]);
            }
      }

      amRefreshing=false;
      return 0;
}
            
00948 void VisController::safeDeleteFilterList(
            std::list<std::pair<Filter *, std::vector<const FilterStreamData * > > > &outData, 
                                    unsigned long long typeMask, bool maskPrevents) const
{
      //Loop through the list of vectors of filterstreamdata, then drop any elements that are deleted
      for(list<pair<Filter *,vector<const FilterStreamData *> > > ::iterator it=outData.begin(); 
                                          it!=outData.end(); ) 
      {
            //Note the No-op at the loop iterator. this is needed so we can safely .erase()
            for(vector<const FilterStreamData *>::iterator itJ=(it->second).begin(); itJ!=(it->second).end(); )  
            {
                  //Don't operate on streams if we have a nonzero mask, and the (mask is active XOR mask mode)
                  //NOTE: the XOR flips the action of the mask. if maskprevents is true, then this logical switch
                  //prevents the mask from being deleted. if not, ONLY the maked types are deleted.
                  //In any case, a zero mask makes this whole thing not do anything, and everything gets deleted.
                  if(typeMask && ( ((bool)((*itJ)->getStreamType() & typeMask)) ^ !maskPrevents)) 
                  {
                        itJ++; //increment.
                        continue;
                  }
            
                  switch((*itJ)->getStreamType())
                  {
                        case STREAM_TYPE_IONS:
                        {
                              //Iterator points to vector. Typecast elements in vector to IonStreamData 
                              const IonStreamData *ionData;
                              ionData=((const IonStreamData *)(*itJ));
                              
                              ASSERT(ionData->cached == 1 ||
                                    ionData->cached == 0);

                              if(!ionData->cached)
                                    delete ionData;
                              break;
                        }
                        case STREAM_TYPE_PLOT:
                        {
                              
                              //Draw plot
                              const PlotStreamData *plotData;
                              plotData=((PlotStreamData *)(*itJ));

                              ASSERT(plotData->cached == 1 ||
                                    plotData->cached == 0);
                              if(!plotData->cached)
                                    delete plotData;
                              
                              break;
                        }
                        case STREAM_TYPE_DRAW:
                        {
                              DrawStreamData *drawData;
                              drawData=((DrawStreamData *)(*itJ));
                              
                              ASSERT(drawData->cached == 1 ||
                                    drawData->cached == 0);
                              if(drawData->cached)
                                    delete drawData;
                              break;
                        }
                        case STREAM_TYPE_RANGE:
                              //Range data has no allocated pointer
                              break;

                        
                        default:
                              ASSERT(false);
                  }

                  //deleting increments.
                  itJ=it->second.erase(itJ);
            }

            //Check to see if this element still has any items in its vector. if not,
            //then discard the element
            if(!(it->second.size()))
                  it=outData.erase(it);
            else
                  it++;

      }



}

01035 unsigned int VisController::addCam(const std::string &camName)
{
      Camera *c=targetScene->cloneActiveCam();
      c->setUserString(camName);
      //Store the camera
      unsigned int id = targetScene->addCam(c);
      targetScene->setActiveCam(0);
      return id;
}

01045 bool VisController::removeCam(unsigned int uniqueID)
{
      targetScene->removeCam(uniqueID);
      targetScene->setDefaultCam();
      return true;
}

01052 bool VisController::setCam(unsigned int uniqueID)
{
      targetScene->setActiveCam(uniqueID);
      return true;
}

01058 void VisController::updateCamPropertyGrid(wxPropertyGrid *g,unsigned int camID)
{

      g->clearKeys();
      if(targetScene->isDefaultCam())
            return;

      CameraProperties p;
      
      targetScene->getCamProperties(camID,p);

      g->setNumSets(p.data.size());
      //Create the keys for the property grid to do its thing
      for(unsigned int ui=0;ui<p.data.size();ui++)
      {
            for(unsigned int uj=0;uj<p.data[ui].size();uj++)
            {
                  g->addKey(p.data[ui][uj].first, ui,p.keys[ui][uj],
                        p.types[ui][uj],p.data[ui][uj].second);
            }
      }

      //Let the property grid layout what it needs to
      g->propertyLayout();
}

01084 bool VisController::reparentFilter(unsigned long long toMove, 
                              unsigned long long newParent)
{
      //Save current filter state to undo stack
      pushUndoStack();
      

      const Filter *toMoveFilt=getFilterById(toMove);
      const Filter *newParentFilt=getFilterById(newParent);


      ASSERT(toMoveFilt && newParentFilt && 
            !(toMoveFilt==newParentFilt));

      //Look for both newparent and sibling iterators 
      bool found[2] = {false,false};
      tree<Filter *>::iterator moveFilterIt,parentFilterIt;
      for(tree<Filter * >::iterator it=filters.begin(); 
                                    it!=filters.end(); it++)
      {
            if(!found[0])
            {
                  if(*it == toMoveFilt)
                  {
                        moveFilterIt=it;
                        found[0]=true;
                  }
            }
            if(!found[1])
            {
                  if(*it == newParentFilt)
                  {
                        parentFilterIt=it;
                        found[1]=true;
                  }
            }

            if(found[0] && found[1] )
                  break;
      }
      
      ASSERT(found[0] && found[1] );

      //ensure that this is actually a parent-child relationship, and not the other way around!
      for(tree<Filter *>::pre_order_iterator it(moveFilterIt);it!= filters.end(); it++)
      {
            //Do not traverse siblings
            if(filters.depth(moveFilterIt) >= filters.depth(it) && it!=moveFilterIt )
                  break;
            
            if(it == parentFilterIt)
                  return false;
      }
      
      //Move the "tomove" filter, and its children to be a child of the
      //newly nominated parent (DoCs "adoption" you might say.)
      if(parentFilterIt != moveFilterIt)
      {
            for(tree<Filter *>::pre_order_iterator it(moveFilterIt);it!= filters.end(); it++)
            {
                  //Do not traverse siblings
                  if(filters.depth(moveFilterIt) >= filters.depth(it) && it!=moveFilterIt )
                        break;
            
                  (*it)->clearCache();
            }
            //Erase the cache of moveFilterIt, and then move it to a new location
            (*moveFilterIt)->clearCache();
            //Create a dummy node after this parent
            tree<Filter*>::iterator node = filters.append_child(parentFilterIt,0);
            //This doesn't actually nuke the original subtree, but rather copies it, replacing the dummy node.
            filters.replace(node,moveFilterIt); 
            //Nuke the original subtree
            filters.erase(moveFilterIt);
            return true;
      }
      else 
            return false;
}

01164 bool VisController::copyFilter(unsigned long long toCopy, 
                              unsigned long long newParent, bool copyToRoot)
{
      //Save current filter state to undo stack
      pushUndoStack();
      

      const Filter *toCopyFilt=getFilterById(toCopy);
      //Copy a filter child to a filter child
      if(!copyToRoot)
      {
            const Filter *newParentFilt=getFilterById(newParent);


            ASSERT(toCopyFilt && newParentFilt && 
                  !(toCopyFilt==newParentFilt));

            //Look for both newparent and sibling iterators 
            bool found[2] = {false,false};
            tree<Filter *>::iterator moveFilterIt,parentFilterIt;
            for(tree<Filter * >::iterator it=filters.begin(); 
                                          it!=filters.end(); it++)
            {
                  if(!found[0])
                  {
                        if(*it == toCopyFilt)
                        {
                              moveFilterIt=it;
                              found[0]=true;
                        }
                  }
                  if(!found[1])
                  {
                        if(*it == newParentFilt)
                        {
                              parentFilterIt=it;
                              found[1]=true;
                        }
                  }

                  if(found[0] && found[1] )
                        break;
            }
            
            ASSERT(found[0] && found[1] );

            //ensure that this is actually a parent-child relationship
            for(tree<Filter *>::pre_order_iterator it(moveFilterIt);it!= filters.end(); it++)
            {
                  //Do not traverse siblings
                  if(filters.depth(moveFilterIt) >= filters.depth(it) && it!=moveFilterIt )
                        break;
                  
                  if(it == parentFilterIt)
                        return false;
            }
            
            //Move the "tomove" filter, and its children to be a child of the
            //newly nominated parent (DoCS* "adoption" you might say.) 
            //*DoCs : Department of Child Services (bad taste .au joke)
            if(parentFilterIt != moveFilterIt)
            {
                  //Create a temporary tree and copy the contents into here
                  tree<Filter *> tmpTree;
                  tree<Filter *>::iterator node= tmpTree.insert(tmpTree.begin(),0);
                  tmpTree.replace(node,moveFilterIt); //Note this doesn't kill the original
                  
                  //Replace each of the filters in the temporary_tree with a clone of the original
                  for(tree<Filter*>::iterator it=tmpTree.begin();it!=tmpTree.end(); it++)
                        *it= (*it)->cloneUncached();

                  //In the original tree, create a new null node
                  node = filters.append_child(parentFilterIt,0);
                  //Replace the node with the tmpTree's contents
                  filters.replace(node,tmpTree.begin()); 
                  return true;
            }
            else 
                  return false;
      }
      else
      {
            //copy a selected base of the tree to a new base component

            //Look for both newparent and sibling iterators 
            bool found = false;
            tree<Filter *>::iterator moveFilterIt;
            for(tree<Filter * >::iterator it=filters.begin(); 
                                          it!=filters.end(); it++)
            {
                  if(*it == toCopyFilt)
                  {
                        moveFilterIt=it;
                        found=true;
                        break;
                  }
            }

            if(!found)
                  return false;

            //Create a temporary tree and copy the contents into here
            tree<Filter *> tmpTree;
            tree<Filter *>::iterator node= tmpTree.insert(tmpTree.begin(),0);
            tmpTree.replace(node,moveFilterIt); //Note this doesn't kill the original
            
            //Replace each of the filters in the temporary_tree with a clone of the original
            for(tree<Filter*>::iterator it=tmpTree.begin();it!=tmpTree.end(); it++)
                  *it= (*it)->cloneUncached();

            //In the original tree, create a new null node
            node = filters.insert_after(filters.begin(),0);
            //Replace the node with the tmpTree's contents
            filters.replace(node,tmpTree.begin()); 
            return true;
      }
}


01283 bool VisController::setFilterString(unsigned long long id,const std::string &s)
{
      //Save current filter state to undo stack
      pushUndoStack();
      
      Filter *p=(Filter *)getFilterById(id);

      if(s != p->getUserString())
      {
            p->setUserString(s);
            return true;
      }

      return false;
}

01299 void VisController::invalidateCache(const Filter *filtPtr)
{

      if(!filtPtr)
      {
            //Invalidate everything
            for(tree<Filter * >::iterator it=filters.begin(); 
                                    it!=filters.end(); it++)
                  (*it)->clearCache();
      }
      else
      {
            //Find the filter in the tree
            tree<Filter *>::iterator filterIt;  
            for(tree<Filter * >::iterator it=filters.begin(); 
                                          it!=filters.end(); it++)
            {
                  if(*it == filtPtr)
                  {
                        filterIt=it;
                        break;
                  }
            }

            for(tree<Filter *>::pre_order_iterator it(filterIt);it!= filters.end(); it++)
            {
                  //Do not traverse siblings
                  if(filters.depth(filterIt) >= filters.depth(it) && it!=filterIt )
                        break;
            
                  (*it)->clearCache();
            }
      }

      


}

01338 unsigned int VisController::numCams() const 
{
      return targetScene->getNumCams();
}
            
01343 void VisController::ensureSceneVisible(unsigned int direction)
{
      targetScene->ensureVisible(direction);
}

01348 bool VisController::saveState(const char *cpFilename) const
{
      //Open file for output
      std::ofstream f(cpFilename);

      if(!f)
            return false;

      //Write state open tag 
      f<< "<threeDepictstate>" << endl;
      f<<tabs(1)<< "<writer version=\"" << PROGRAM_VERSION << "\"/>" << endl;

      //Write filter tree
      f << tabs(1) << "<filtertree>" << endl;
      //Depth-first search, enumerate all filters in depth-first fashion
      unsigned int depthLast=0;
      unsigned int child=0;
      for(tree<Filter * >::pre_order_iterator filtIt=filters.begin();
                              filtIt!=filters.end(); filtIt++)
      {
            unsigned int depth;
            depth = filters.depth(filtIt);
            if(depth >depthLast)
            {
                  while(depthLast++ < depth)
                  {
                        f << tabs(depthLast+1);
                        f  << "<children>" << endl; 
                        child++;
                  }
            }
            else if (depth < depthLast)
            {
                  while(depthLast-- > depth)
                  {
                        f << tabs(depthLast+2);
                        f  << "</children>" << endl; 
                        child--;
                  }
            }

            if(!(*filtIt)->writeState(f,STATE_FORMAT_XML,depth+2))
                  return false;
            depthLast=depth;
      }

      //Close out filter tree.
      while(child--)
      {
            f << tabs(child+2) << "</children>" << endl;
      }
      f << tabs(1) << "</filtertree>" << endl;

      vector<Camera *> camVec;

      unsigned int active=targetScene->duplicateCameras(camVec);

      //First camera is hidden "working" camera. do not record 
      if(camVec.size() > 1)
      {
            f <<tabs(1) <<  "<cameras>" << endl;
            //subtract 1 to eliminate "hidden" camera offset 
            f << tabs(2) << "<active value=\"" << active-1 << "\"/>" << endl;
            
            for(unsigned int ui=1;ui<camVec.size();ui++)
            {
                  //ask each camera to write its own state, tab indent 2
                  camVec[ui]->writeState(f,STATE_FORMAT_XML,2);
                  delete camVec[ui];
            }
            f <<tabs(1) <<  "</cameras>" << endl;
      }


      if(stashedFilters.size())
      {
            f << tabs(1) << "<stashedfilters>" << endl;

            for(unsigned int ui=0;ui<stashedFilters.size();ui++)
            {
                  f << tabs(2) << "<stash name=\"" <<  stashedFilters[ui].first <<"\">" << endl;

                  //Depth-first search, enumerate all filters in depth-first fashion
                  unsigned int depthLast;
                  depthLast=0;
                  unsigned int child;
                  child=0;

                  const unsigned int tabOffset=2;
                  for(tree<Filter * >::pre_order_iterator filtIt=stashedFilters[ui].second.begin();
                                          filtIt!=stashedFilters[ui].second.end(); filtIt++)
                  {
                        unsigned int depth;
                        depth = filters.depth(filtIt);
                        if(depth >depthLast)
                        {
                              while(depthLast++ < depth)
                              {
                                    f << tabs(depthLast+1+tabOffset);
                                    f  << "<children>" << endl; 
                                    child++;
                              }
                        }
                        else if (depth < depthLast)
                        {
                              while(depthLast-- > depth)
                              {
                                    f << tabs(depthLast+2+tabOffset);
                                    f  << "</children>" << endl; 
                                    child--;
                              }
                        }

                        if(!(*filtIt)->writeState(f,STATE_FORMAT_XML,depth+2))
                              return false;
                        depthLast=depth;
                  }
                  
                  //Close out stash's filter tree
                  while(child--)
                        f << tabs(child+1+tabOffset) << "</children>" << endl;

                  //Finish stash
                  f << tabs(2) << "</stash>" << endl;

            }




            f << tabs(1) << "</stashedfilters>" << endl;
      }



      //Close XMl tag.  
      f<< "</threeDepictstate>" << endl;


      return true;
}

01490 bool VisController::loadState(const char *cpFilename, std::ostream &errStream, bool merge) 
{
      //Load the state from an XML file
      //here we use libxml2's loading routines
      //http://xmlsoft.org/
      //Tutorial: http://xmlsoft.org/tutorial/xmltutorial.pdf
      xmlDocPtr doc;
      xmlParserCtxtPtr context;

      context =xmlNewParserCtxt();

      if(!context)
      {
            errStream << "Failed to allocate parser" << std::endl;
            return false;
      }

      //Open the XML file
      //TODO: Nowhere do i check which DTD I have validated against.
      doc = xmlCtxtReadFile(context, cpFilename, NULL, XML_PARSE_DTDVALID);

      if(!doc)
      {
            //Open the XML file again, but without DTD validation
            doc = xmlCtxtReadFile(context, cpFilename, NULL, 0);

            if(!doc)
                  return false;
      }
      else
      {
            //Check for context validity
            if(!context->valid)
            {
                  errStream << "Document parse failure -- missing or invalid Document type Descriptor? (a file called "<< DTD_NAME << std::endl;
                  errStream << "Attempting to read without DTD validation. This may not work properly" << 
                              " - in the future, please find a valid" << DTD_NAME<< "file, and place it in the current directory..." << std::endl;
            }
      }
      //release the context
      xmlFreeParserCtxt(context);
      
      //Lets do some parsing goodness
      //ahh parsing - verbose and boring
      tree<Filter *> newFilterTree;
      vector<Camera *> newCameraVec;
      vector<pair<string,tree<Filter * > > > newStashes;

      unsigned int activeCam;
      try
      {
            std::stack<xmlNodePtr>  nodeStack;
            //retrieve root node    
            xmlNodePtr nodePtr = xmlDocGetRootElement(doc);

            //Umm where is our root node guys?
            if(!nodePtr)
                  throw 1;
            
            //This *should* be an threeDepict state file
            if(xmlStrcmp(nodePtr->name, (const xmlChar *)"threeDepictstate"))
                  throw 1;
            //push root tag   
            nodeStack.push(nodePtr);
            
            //Now in threeDepictstate tag
            nodePtr = nodePtr->xmlChildrenNode;
            xmlChar *xmlString;
            //check for version tag & number
            if(!XMLHelpFwdToElem(nodePtr,"writer"))
            {
                  xmlString=xmlGetProp(nodePtr, (const xmlChar *)"version"); 

            //FIXME: IMPLEMENT ME               
            //    if(versionNewer(xmlString,(const xmlChar *)"0.0.1"))
            //    {
            //          xmlFree(xmlString);
            //          errStream << "State was created by a newer version of this program.. " 
            //                << "file reading will continue, but may fail." << endl 
            //                << "\tfile version:" << (char *)xmlString << std::endl;
            //    }
                  xmlFree(xmlString);
            }
            else
                  throw 1;
            
            //find filtertree data
            if(XMLHelpFwdToElem(nodePtr,"filtertree"))
                  throw 1;

            //Load the filter tree
            if(loadFilterTree(nodePtr,newFilterTree))
                  throw 1;

            //Read camera states, if present
            nodeStack.push(nodePtr);
            if(!XMLHelpFwdToElem(nodePtr,"cameras"))
            {
                  //Move to camera active tag 
                  nodePtr=nodePtr->xmlChildrenNode;
                  if(XMLHelpFwdToElem(nodePtr,"active"))
                        throw 1;

                  //read ID of active cam
                  xmlString=xmlGetProp(nodePtr,(const xmlChar *)"value");
                  if(!xmlString)
                        throw 1;
                  if(stream_cast(activeCam,xmlString))
                        throw 1;

            
                  //Spin through the list of each camera    
                  while(!XMLHelpNextType(nodePtr,XML_ELEMENT_NODE))
                  {
                        std::string tmpStr;
                        tmpStr =(const char *)nodePtr->name;
                        Camera *thisCam;
                        thisCam=0;
                        
                        //work out the camera type
                        if(tmpStr == "persplookat")
                        {
                              thisCam = new CameraPerspLookAt;
                              if(!thisCam->readState(nodePtr->xmlChildrenNode))
                                    return false;
                        }
                        else
                              throw 1;

                        ASSERT(thisCam);
                        newCameraVec.push_back(thisCam);    
                  }

                  if(newCameraVec.size() < activeCam)
                        activeCam=0;

            }
            
            nodePtr=nodeStack.top();
            nodeStack.pop();
            
            nodeStack.push(nodePtr);
            //Read stashes if present
            if(!XMLHelpFwdToElem(nodePtr,"stashedfilters"))
            {
                  nodeStack.push(nodePtr);

                  //Move to stashes 
                  nodePtr=nodePtr->xmlChildrenNode;

                  while(!XMLHelpFwdToElem(nodePtr,"stash"))
                  {
                        string stashName;
                        tree<Filter *> newStashTree;
                        newStashTree.clear();

                        //read name of stash
                        xmlString=xmlGetProp(nodePtr,(const xmlChar *)"name");
                        if(!xmlString)
                              throw 1;
                        stashName=(char *)xmlString;

                        if(!stashName.size())
                              throw 1;

                        if(loadFilterTree(nodePtr,newStashTree))
                              throw 1;
                  
                        newStashes.push_back(make_pair(stashName,newStashTree));
                  }

                  nodePtr=nodeStack.top();
                  nodeStack.pop();
            }
            nodePtr=nodeStack.top();
            nodeStack.pop();
            
            nodeStack.push(nodePtr);
      }
      catch (int)
      {
            //Code threw an error, just say "bad parse" and be done with it
            xmlFreeDoc(doc);
            return false;
      }
      xmlFreeDoc(doc);  


      
      //Set the filter tree to the new one
      //the new mapping will be created during the next tree ctrl update
      if(!merge)
      {
            clear();
            for(tree<Filter * >::pre_order_iterator filtIt=filters.begin();
                                    filtIt!=filters.end(); filtIt++)
                  delete *filtIt;

      
            for(unsigned int ui=0;ui<stashedFilters.size();ui++)
            {
                  for(tree<Filter * >::pre_order_iterator filtIt=stashedFilters[ui].second.begin();
                                          filtIt!=stashedFilters[ui].second.end(); filtIt++)
                        delete *filtIt;
            }
      }

      //Check that stashes are uniquely named
      for(unsigned int ui=0;ui<newStashes.size();ui++)
      {
            for(unsigned int uj=0;uj<newStashes.size();uj++)
            {
                  if(ui == uj)
                        continue;

                  if(newStashes[ui].first == newStashes[uj].first)
                  {
                        //Shit. Stashes not uniquely named,
                        //this state file is invalid

                        //Clear the stashes & filter and abort
                        for(tree<Filter * >::pre_order_iterator filtIt=newFilterTree.begin();
                                                filtIt!=newFilterTree.end(); filtIt++)
                              delete *filtIt;

                        
                        for(unsigned int ui=0;ui<newStashes.size();ui++)
                        {
                              for(tree<Filter * >::pre_order_iterator filtIt=stashedFilters[ui].second.begin();
                                                      filtIt!=stashedFilters[ui].second.end(); filtIt++)
                                    delete *filtIt;


                        }


                        return false;
                  }

            }
      }




      if(!merge)
      {
            std::swap(filters,newFilterTree);

            std::swap(stashedFilters,newStashes);
      }
      else
      {
            for(unsigned int ui=0;ui<newStashes.size();ui++)
                  stashedFilters.push_back(newStashes[ui]);

            if(filters.size())
            {
                  filters.insert_subtree_after(filters.begin(),newFilterTree.begin());
            }
            else
                  std::swap(filters,newFilterTree);
      }


      //Wipe the existing cameras, and then put the new cameras in place
      targetScene->clearCams();
      
      //Set a default camera as needed. We don't need to track its unique ID, as this is
      //"invisible" to the UI
      if(!targetScene->getNumCams())
      {
            Camera *c=new CameraPerspLookAt();
            unsigned int id;
            id=targetScene->addCam(c);
            targetScene->setActiveCam(id);
      }

      for(unsigned int ui=0;ui<newCameraVec.size();ui++)
      {
            unsigned int id;
            id=targetScene->addCam(newCameraVec[ui]);
            //Use the unique identifier to set the active cam,
            //if this is the active camera.
            if(ui == activeCam)
                  targetScene->setActiveCam(id);
      }

      //Perform sanitisation on results
      return true;
}


01783 unsigned int VisController::loadFilterTree(const xmlNodePtr &treeParent, tree<Filter *> &newTree) const

{
      newTree.clear();
      //List of filters to delete if parsing goes pear shaped
      list<Filter *> cleanupList;

      //Parse the filter tree in the XML file.
      //generating a filter tree
      bool inTree=true;
      tree<Filter *>::iterator lastFilt=newTree.begin();
      tree<Filter *>::iterator lastTop=newTree.begin();
      stack<tree<Filter *>::iterator> treeNodeStack;

      xmlNodePtr nodePtr = treeParent->xmlChildrenNode;


      //push root tag   
      std::stack<xmlNodePtr>  nodeStack;
      nodeStack.push(nodePtr);

      while (inTree)
      {
            //Jump to the next XML node at this depth
            if (XMLHelpNextType(nodePtr,XML_ELEMENT_NODE))
            {
                  //If there is not one, pop the tree stack
                  if (treeNodeStack.size())
                  {
                        //Pop the node stack for the XML and filter trees.
                        nodePtr=nodeStack.top();
                        nodeStack.pop();
                        lastFilt=treeNodeStack.top();
                        treeNodeStack.pop();
                  }
                  else
                  {
                        //Did we run out of stack?
                        //then we have finished the tree.
                        inTree=false;
                  }
                  continue;
            }

            Filter *newFilt;
            bool nodeUnderstood;
            newFilt=0;
            nodeUnderstood=true; //assume by default we understand, and set false if not

            cerr << "DEBUG :" << (const char *)nodePtr->name << endl;
            //If we encounter a "children" node. then we need to look at the children of this filter
            if (!xmlStrcmp(nodePtr->name,(const xmlChar*)"children"))
            {
                  //Can't have children without parent
                  if (!newTree.size())
                        goto loadFilterTreeCleanup;

                  //Child node should have its own child
                  if (!nodePtr->xmlChildrenNode)
                        goto loadFilterTreeCleanup;

                  nodeStack.push(nodePtr);
                  treeNodeStack.push(lastFilt);

                  nodePtr=nodePtr->xmlChildrenNode;
                  continue;
            }
            else if (!xmlStrcmp(nodePtr->name,(const xmlChar*)"posload")) //Is this a "posload" filter?
            {
                  //It is!, lets make a new one and load the state
                  newFilt= new PosLoadFilter;
                  cleanupList.push_back(newFilt);
                  if (!newFilt->readState(nodePtr->xmlChildrenNode))
                        goto loadFilterTreeCleanup;
            }
            else if (!xmlStrcmp(nodePtr->name,(const xmlChar*)"iondownsample")) //Is this a "iondownsample" filter?
            {
                  //It is!, lets make a new one and load the state
                  newFilt= new IonDownsampleFilter;
                  cleanupList.push_back(newFilt);

                  if (!newFilt->readState(nodePtr->xmlChildrenNode))
                        goto loadFilterTreeCleanup;
            }
            else if (!xmlStrcmp(nodePtr->name,(const xmlChar*)"rangefile")) //Is this a "rangefile" filter?
            {
                  //It is!, lets make a new one and load the state
                  newFilt= new RangeFileFilter;
                  cleanupList.push_back(newFilt);

                  if (!newFilt->readState(nodePtr->xmlChildrenNode))
                        goto loadFilterTreeCleanup;
            }
            else if (!xmlStrcmp(nodePtr->name,(const xmlChar*)"spectrumplot")) //Is this a "spectrumplot" filter?
            {
                  //It is!, lets make a new one and load the state
                  newFilt= new SpectrumPlotFilter;
                  cleanupList.push_back(newFilt);

                  if (!newFilt->readState(nodePtr->xmlChildrenNode))
                        goto loadFilterTreeCleanup;
            }
            else if (!xmlStrcmp(nodePtr->name,(const xmlChar*)"ionclip")) //Is this a "ionclip" filter?
            {
                  //It is!, lets make a new one and load the state
                  newFilt= new IonClipFilter;
                  cleanupList.push_back(newFilt);

                  if (!newFilt->readState(nodePtr->xmlChildrenNode))
                        goto loadFilterTreeCleanup;
            }
            else if (!xmlStrcmp(nodePtr->name,(const xmlChar*)"ioncolour")) //Is this a "ioncolour" filter?
            {
                  //It is!, lets make a new one and load the state
                  newFilt= new IonColourFilter;
                  cleanupList.push_back(newFilt);

                  if (!newFilt->readState(nodePtr->xmlChildrenNode))
                        goto loadFilterTreeCleanup;
            }
            else if (!xmlStrcmp(nodePtr->name,(const xmlChar*)"compositionprofile")) //Is this a "compositionprofile" filter?
            {
                  cerr << "DEBUG: Created composition filter" << endl;
                  //It is!, lets make a new one and load the state
                  newFilt= new CompositionProfileFilter;
                  cleanupList.push_back(newFilt);
                  cerr << "DEBUG: Added to list" << endl;

                  if (!newFilt->readState(nodePtr->xmlChildrenNode))
                  {
                        cerr << "DEBUG: Aborting amd cleaning up" << endl;
                        goto loadFilterTreeCleanup;
                  }
                  cerr << "DEBUG: Composition read succesful" << endl;
            }
            else if (!xmlStrcmp(nodePtr->name,(const xmlChar*)"boundingbox")) //Is this a "boundingbox" filter?
            {
                  //It is!, lets make a new one and load the state
                  newFilt= new BoundingBoxFilter;
                  cleanupList.push_back(newFilt);

                  if (!newFilt->readState(nodePtr->xmlChildrenNode))
                        goto loadFilterTreeCleanup;
            }
            else if (!xmlStrcmp(nodePtr->name,(const xmlChar*)"transform")) //Is this a "transform" filter?
            {
                  //It is!, lets make a new one and load the state
                  newFilt= new TransformFilter;
                  cleanupList.push_back(newFilt);

                  if (!newFilt->readState(nodePtr->xmlChildrenNode))
                        goto loadFilterTreeCleanup;
            }
            else if (!xmlStrcmp(nodePtr->name,(const xmlChar*)"externalprog")) //Is this a "transform" filter?
            {
                  //It is!, lets make a new one and load the state
                  newFilt= new ExternalProgramFilter;
                  cleanupList.push_back(newFilt);

                  if (!newFilt->readState(nodePtr->xmlChildrenNode))
                        goto loadFilterTreeCleanup;
            }
            else if (!xmlStrcmp(nodePtr->name,(const xmlChar*)"spatialanalysis")) //Is this a "transform" filter?
            {
                  //It is!, lets make a new one and load the state
                  newFilt= new SpatialAnalysisFilter;
                  cleanupList.push_back(newFilt);

                  if (!newFilt->readState(nodePtr->xmlChildrenNode))
                        goto loadFilterTreeCleanup;
            }
            else
            {
                  nodeUnderstood=false;
            }

            cerr << "DEBUG OK:" << (const char *)nodePtr->name << endl;
            //Skip this item
            if (nodeUnderstood)
            {
                  ASSERT(newFilt);

                  //Add the new item the tree
                  if (!newTree.size())
                        lastFilt=newTree.insert(newTree.begin(),newFilt);
                  else
                  {
                        if (treeNodeStack.size())
                              lastFilt=newTree.append_child(treeNodeStack.top(),newFilt);
                        else
                        {
                              lastTop=newTree.insert(lastTop,newFilt);
                              lastFilt=lastTop;
                        }


                  }

            }
            else
            {
                  //TODO: Set warning; we encountered nodes that we cannot process.
                  //and will hence skip
            }
      }


      return 0;

//OK, so this is a bit unforgivable, but I need to have a cleanup function. The goto
//is purely local in scope.
loadFilterTreeCleanup:

      for(list<Filter*>::iterator it=cleanupList.begin();
                  it!=cleanupList.end(); it++)
            delete *it;

      return 1;

}

02004 void VisController::makeFiltersSafe()
{
      stripHazardousFilters(filters);
      
      for(unsigned int ui=0;ui<stashedFilters.size();ui++)
            stripHazardousFilters(stashedFilters[ui].second);
}

02012 void VisController::stripHazardousFilters(tree<Filter *> &thisTree)
{
      for(tree<Filter * >::pre_order_iterator it=thisTree.begin();
                              it!=thisTree.end(); it++)
      {
            if ((*it)->canBeHazardous())
            {
                  //delete filters from this branch
                  for(tree<Filter *>::pre_order_iterator itj(it); itj!=thisTree.end(); itj++)
                        delete *itj;
      

                  //nuke this branch
                  it=thisTree.erase(it);
                  it--;
            }
      }

}

02032 bool VisController::hasHazardous(const tree<Filter *> &thisTree) const
{
      for(tree<Filter * >::pre_order_iterator it=filters.begin();
                              it!=filters.end(); it++)
      {
            if ((*it)->canBeHazardous())
                  return true;
      }

      return false;
}

02044 bool VisController::hasHazardousContents() const
{
      //Check the filter system and the 
      //stashes for potentially dangerous content
      if(hasHazardous(filters))
            return true;
      for(unsigned int ui=0;ui<stashedFilters.size();ui++)
      {
            if(hasHazardous(stashedFilters[ui].second))
                  return true;
      }

      return false;
}

void VisController::clear()
{
      filterTreeMapping.clear();
      nextID=0;

      //clean filter tree
      for(tree<Filter * >::iterator it=filters.begin(); 
                                    it!=filters.end(); it++)
            delete *it;

      filters.clear();
      undoFilterStack.clear();
}

02073 void VisController::getCamData(std::vector<std::pair<unsigned int, std::string> > &camData) const
{
      targetScene->getCameraIDs(camData);
}

02078 unsigned int VisController::getActiveCamId() const
{ 
      return targetScene->getActiveCamId();
}

02083 unsigned int VisController::exportIonStreams(const std::vector<const FilterStreamData * > &selectedStreams,
            const std::string &outFile, unsigned int format) const
{

      //test file open, and truncate file to zero bytes
      ofstream f(outFile.c_str(),ios::trunc);
      
      if(!f)
            return 1;

      f.close();

      for(unsigned int ui=0; ui<selectedStreams.size(); ui++)
      {
            switch(selectedStreams[ui]->getStreamType())
            {
                  case STREAM_TYPE_IONS:
                  {
                        const IonStreamData *ionData;
                        ionData=((const IonStreamData *)(selectedStreams[ui]));
                        switch(format)
                        {
                              case IONFORMAT_POS:
                              {
                                    //Append this ion stream to the posfile
                                    appendPos(ionData->data,outFile.c_str());

                                    break;
                              }
                              default:
                                    ASSERT(false);
                                    break;
                        }
                  }
            }
      }

      return 0;
}

02123 void VisController::getFilterTypes(std::vector<const Filter *> &filtersOut, unsigned int type)
{
      for(tree<Filter * >::iterator it=filters.begin(); 
                                    it!=filters.end(); it++)
      {
            if((*it)->getType() == type)
                  filtersOut.push_back(*it);
      }
}

02133 void VisController::purgeFilterCache()
{
      for(tree<Filter *>::iterator it=filters.begin();it!=filters.end();it++)
            (*it)->clearCache();
}

02139 void VisController::addStashedToFilters(Filter *parent, unsigned int stashId)
{
      //Save current filter state to undo stack
      pushUndoStack();
      
      //Splice a copy of the stash into the subtree of the selected parent filter
      tree<Filter *>::iterator it= std::find(filters.begin(),filters.end(),parent);

      ASSERT(it != filters.end())

      
      tree<Filter*>::iterator node = filters.append_child(it,0);
      
      tree<Filter *> stashTree;

      getStashTree(stashId,stashTree);

      //Duplicate the contents of this filter
      
      //Replace each of the filters in the temporary_tree with a clone of the original
      for(tree<Filter*>::iterator copyIt=stashTree.begin();copyIt!=stashTree.end(); copyIt++)
            *copyIt= (*copyIt)->cloneUncached();

      //insert the tree as a sibling of the node 
      filters.insert_subtree_after(node,stashTree.begin()); 

      //nuke the appended node, which was just acting as a dummy
      filters.erase(node);
}

02169 void VisController::deleteStash(unsigned int stashId)
{
      unsigned int stashPos=stashUniqueIDs.getPos(stashId);

      //swap out the one we wish to kill with the last element
      if(stashPos != stashedFilters.size() -1)
            swap(stashedFilters[stashedFilters.size()-1],stashedFilters[0]);

      //nuke the tail
      stashedFilters.pop_back();
}

02181 unsigned int VisController::stashFilters(unsigned int filterId, const char *stashName)
{
 
      tree<Filter *> t;
      Filter *target = (Filter*)getFilterById(filterId);

      tree<Filter *>::iterator targetIt;
      for(tree<Filter *>::iterator it=filters.begin(); it!= filters.end(); it++)
      {
            if(*it == target)
            {
                  targetIt=it;
                  break;
            }
      }


      tree<Filter *> subTree;

      tree<Filter *>::iterator node= subTree.insert(subTree.begin(),0);
      subTree.replace(node,targetIt); //Note this doesn't kill the original

      //Replace each of the filters in the temporary_tree with a clone of the original
      for(tree<Filter*>::iterator it=subTree.begin();it!=subTree.end(); it++)
            *it= (*it)->cloneUncached();
      
      stashedFilters.push_back(std::make_pair(string(stashName),subTree));
      return stashUniqueIDs.genId(stashedFilters.size()-1); 
}

02211 void VisController::getStashTree(unsigned int stashId, tree<Filter *> &t) const
{
      unsigned int pos = stashUniqueIDs.getPos(stashId);
      ASSERT(pos < stashedFilters.size());
      t = stashedFilters[pos].second;
}

02218 void VisController::getStashList(std::vector<std::pair<std::string,unsigned int > > &stashList) const
{
      ASSERT(stashList.size() == 0); // should be empty     
      //Duplicate the stash list, but replace the filter tree ID
      //with the ID value that corresponds to that position
      stashList.reserve(stashedFilters.size()); 
      for(unsigned int ui=0;ui<stashedFilters.size(); ui++)
      {
            stashList.push_back(std::make_pair(stashedFilters[ui].first,
                              stashUniqueIDs.getId(ui)));
      }
}

void VisController::updateRawGrid() const
{
      vector<vector<pair<float,float> > > plotData;
      vector<std::pair<std::wstring,std::wstring> > labels;
      //grab the data for the currently visible plots
      targetPlots->getRawData(plotData,labels);

      //Clear the grid
      targetRawGrid->DeleteCols(0,targetRawGrid->GetNumberCols());
      targetRawGrid->DeleteRows(0,targetRawGrid->GetNumberRows());
      for(unsigned int ui=0;ui<plotData.size(); ui++)
      {
            //Create two new colums
            targetRawGrid->AppendCols(2);
            //Check to see if we need to add rows to make our data fit
            if(plotData[ui].size() > targetRawGrid->GetNumberRows())
                  targetRawGrid->AppendRows(plotData[ui].size()-targetRawGrid->GetNumberRows());

            targetRawGrid->SetColLabelValue(2*ui,wxStr(labels[ui].first));
            targetRawGrid->SetColLabelValue(2*ui+1,wxStr(labels[ui].second));

            //set the data
            for(unsigned int uj=0;uj<plotData[ui].size();uj++)
            {
                  std::string tmpStr;
                  stream_cast(tmpStr,plotData[ui][uj].first);
                  targetRawGrid->SetCellValue(uj,2*ui,wxStr(tmpStr));

                  stream_cast(tmpStr,plotData[ui][uj].second);
                  targetRawGrid->SetCellValue(uj,2*ui+1,wxStr(tmpStr));
            }

      }
}

02266 void VisController::setCachePercent(unsigned int newCache)
{
      ASSERT(newCache <= 100);
      if(!newCache)
            cacheStrategy=CACHE_NEVER;
      else
      {
            cacheStrategy=CACHE_DEPTH_FIRST;
            maxCachePercent=newCache;
      }
}


02279 void VisController::pushUndoStack()
{
      tree<Filter *> newTree;

      newTree = filters;
      
      //Replace each of the filters in the temporary_tree with a clone of the original
      for(tree<Filter*>::iterator it=newTree.begin();it!=newTree.end(); it++)
            *it= (*it)->cloneUncached();

      if(undoFilterStack.size() > MAX_UNDO_SIZE)
            undoFilterStack.pop_front();

      undoFilterStack.push_back(newTree);
      cerr << "Undo Stack size:" << undoFilterStack.size() << endl;
}

02296 void VisController::popUndoStack()
{
      ASSERT(undoFilterStack.size());

      //Delete the pointers in the current tree
      for(tree<Filter * >::iterator it=filters.begin(); 
                                    it!=filters.end(); it++)
      {
            delete *it;
      }

      //Swap the current filter cache out with the undo stack result
      std::swap(filters,undoFilterStack.back());

      //Pop the undo stack
      undoFilterStack.pop_back();
      
      cerr << "Undo Stack size:" << undoFilterStack.size() << endl;
}


Generated by  Doxygen 1.6.0   Back to index