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

netmodel.cc

/*
 * Copyright (c) 1991,1993 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the Computer Systems
 *      Engineering Group at Lawrence Berkeley Laboratory.
 * 4. Neither the name of the University nor of the Laboratory may be used
 *    to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * @(#) $Header: /cvsroot/nsnam/nam-1/netmodel.cc,v 1.113 2006/11/19 00:12:29 tom_henderson Exp $ (LBL)
 */

#include <stdlib.h>
#ifdef WIN32
#include <windows.h>
#endif
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <tcl.h>
#include <tclcl.h>

#include "config.h"
#include "netview.h"
#include "psview.h"
#include "testview.h"
#include "animation.h"
#include "group.h"
#include "tag.h"
#include "queue.h"
#include "drop.h"
#include "packet.h"
#include "edge.h"
#include "lan.h"
#include "node.h"
#include "agent.h"
#include "feature.h"
#include "route.h"
#include "netmodel.h"
#include "monitor.h"
#include "trace.h"
#include "paint.h"
#include "sincos.h"
#include "state.h"
#include "editview.h"
#include "address.h"
#include "animator.h"

#include <float.h>

extern int lineno;
// not used
//static int next_pat;

class NetworkModelClass : public TclClass {
public:
      NetworkModelClass() : TclClass("NetworkModel") {}
      TclObject* create(int argc, const char*const* argv) {
            if (argc < 5) 
                  return 0;
            return (new NetModel(argv[4]));
      }
} networkmodel_class;

//----------------------------------------------------------------------
//----------------------------------------------------------------------
NetModel::NetModel(const char *animator) :
      TraceHandler(animator),
      drawables_(0),
      animations_(0),
      queues_(0),
      views_(0),
      nodes_(0),
      lans_(0), 
      node_sizefac_(NODE_EDGE_RATIO),
      mon_count_(0),
      monitors_(NULL),
      wireless_(0),
      resetf_(0),
      selectedSrc_(-1),
      selectedDst_(-1),
      selectedFid_(-1),
      hideSrc_(-1),
      hideDst_(-1),
      hideFid_(-1),
      colorSrc_(-1),
      colorDst_(-1),
      colorFid_(-1),
      showData_(1), 
      showRouting_(1),
      showMac_(1),
      selectedColor_(-1),
      nGroup_(0), 
      nTag_(0),
      parsetable_(&traceevent_) {

      int i;
      for (i = 0; i < EDGE_HASH_SIZE; ++i) {
            hashtab_[i] = 0;
      }

      for (i = 0; i < PTYPELEN; ++i) {
            selectedTraffic_[i] = '\0' ;
            colorTraffic_[i] = '\0' ;
            hideTraffic_[i] = '\0' ;
      }

      // Default node size is 10.0 so a default packet will be 25% of that (2.5)
      // This value is modified whenever a node is added.  It will be based on 
      // the running average of the size of the last 5 nodes.  Look at 
      // NetModel::addNode(const TraceEvent &e) for more details
      packet_size_ = 2.5;
  
      /*XXX*/
      nymin_ = 1e6;
      nymax_ = -1e6;

      Paint *paint = Paint::instance();
      int p = paint->thin();
      // Initially 256 colors. Can be extended later.
      // See handling of otcl binding "color"
      nclass_ = 256;
      paintMask_ = 0xff;
      paint_ = new int[nclass_];
      oldpaint_ = new int[nclass_];
      for (i = 0; i < nclass_; ++i) {
            paint_[i] = p;
            oldpaint_[i] = p;
  }

      addrHash_ = new Tcl_HashTable;
      Tcl_InitHashTable(addrHash_, TCL_ONE_WORD_KEYS);
      grpHash_ = new Tcl_HashTable;
      Tcl_InitHashTable(grpHash_, TCL_ONE_WORD_KEYS);
      tagHash_ = new Tcl_HashTable;
      Tcl_InitHashTable(tagHash_, TCL_STRING_KEYS);

      objnameHash_ = new Tcl_HashTable;
      Tcl_InitHashTable(objnameHash_, TCL_STRING_KEYS);
      registerObjName("ALL", ClassAllID);
      registerObjName("ANIMATION", ClassAnimationID);
      registerObjName("NODE", ClassNodeID);
      registerObjName("PACKET", ClassPacketID);
      registerObjName("EDGE", ClassEdgeID);
      registerObjName("QUEUEITEM", ClassQueueItemID);
      registerObjName("LAN", ClassLanID);
      registerObjName("TAG", ClassTagID);
      registerObjName("AGENT", ClassAgentID);

      bind("bcast_duration_", &bcast_duration_);
      bind("bcast_radius_", &bcast_radius_);
}

NetModel::~NetModel()
{
      // We should delete everything here, if we want deletable netmodel...
      delete paint_;
      Animation *a, *n;
      for (a = animations_; a != 0; a = n) {
            n = a->next();
            delete a;
      }
      for (a = drawables_; a != 0; a = n) {
            n = a->next();
            delete a;
      }

      Tcl_DeleteHashTable(grpHash_);
      delete grpHash_;
      Tcl_DeleteHashTable(tagHash_);
      delete tagHash_;
      Tcl_DeleteHashTable(objnameHash_);
      delete objnameHash_;
}

void NetModel::update(double now)
{
      Animation *a, *n;
      for (a = animations_; a != 0; a = n) {
            n = a->next();
            a->update(now);
      }

      /*
       * Draw all animations and drawables on display to reflect
       * current time.
       */
      now_ = now;
      for (View* p = views_; p != 0; p = p->next_) {
            //  Calls View::draw() which calls NetView::render()
            //  which calls NetModel::render(View*)
            p->draw();
      }
}

void NetModel::update(double now, Animation* a) {
      a->update(now);
      for (View* p = views_; p != 0; p = p->next_)
            a->draw(p, now);
}

//----------------------------------------------------------------------
// void
// NetModel::reset(double now)
//   - Reset all animations and queues to time 'now'.
//----------------------------------------------------------------------
void
NetModel::reset(double now) {
      Animation* a;
      for (a = animations_; a != 0; a = a->next())
            a->reset(now);
      for (a = drawables_; a != 0; a = a->next())
            a->reset(now);
      for (Queue* q = queues_; q != 0; q = q->next_)
            q->reset(now);
}

//----------------------------------------------------------------------
// void
// NetModel::render(View * view)
//   - Draw this NetModel's drawables, animations, and monitors.
//     (tags, nodes, edges, packets, queues, etc.)
//----------------------------------------------------------------------
void
NetModel::render(View* view) {
      Animation *a;
      Monitor *m;

      for (a = drawables_; a != 0; a = a->next()) 
        a->draw(view, now_);

      for (a = animations_; a != 0; a = a->next())
        a->draw(view, now_);

      for ( m = monitors_; m != NULL; m = m->next())
        m->draw_monitor(view, nymin_, nymax_);
}

void NetModel::render(PSView* view) {
      Animation *a;
      for (a = drawables_; a != 0; a = a->next())
        a->draw(view, now_);
      for (a = animations_; a != 0; a = a->next())
        a->draw(view, now_);
}

void NetModel::render(TestView* view) {
      Animation *a;
      for (a = drawables_; a != 0; a = a->next())
        a->draw(view, now_);
      for (a = animations_; a != 0; a = a->next())
        a->draw(view, now_);
}

//----------------------------------------------------------------------
// NetModel::EdgeHashNode *
// NetModel::lookupEdgeHashNode(int src, int dst) const
//   - Return a pointer to the edge between 'src' and 'dst'. 
//----------------------------------------------------------------------
NetModel::EdgeHashNode *
NetModel::lookupEdgeHashNode(int source, int destination) const
{
      EdgeHashNode* h;
      for (h = hashtab_[ehash(source, destination)]; h != 0; h = h->next)
            if (h->src == source && h->dst == destination)
                  break;
      return (h);
}

int NetModel::addAddress(int id, int addr) const
{
      int newEntry = 1;
      Tcl_HashEntry *he = 
            Tcl_CreateHashEntry(addrHash_, (const char *)addr, &newEntry);
      if (he == NULL)
            return -1;
      if (newEntry) {
            Tcl_SetHashValue(he, (ClientData)id);
      }
      return 0;
}

int NetModel::addr2id(int addr) const 
{
      Tcl_HashEntry *he = Tcl_FindHashEntry(addrHash_, (const char *)addr);
      if (he == NULL)
            return -1;
      return *Tcl_GetHashValue(he);
}

//----------------------------------------------------------------------
//  Adds an edge to a hash table?
//----------------------------------------------------------------------
void NetModel::enterEdge(Edge* e) {
      int src = e->src();
      int dst = e->dst();
      EdgeHashNode *h = lookupEdgeHashNode(src, dst);
      if (h != 0) {
            /* XXX */
            fprintf(stderr, "nam: duplicate edge (%d,%d)\n", src, dst);
            //exit(1);
            return;
      }
      h = new EdgeHashNode;
      h->src = src;
      h->dst = dst;
      h->queue = 0;
      h->edge = e;
      int k = ehash(src, dst);
      h->next = hashtab_[k];
      hashtab_[k] = h;
}

//----------------------------------------------------------------------
// void
// NetModel::removeEdge(Edge* e)
//   - Remove an edge from the network model and delete it
//----------------------------------------------------------------------
void
NetModel::removeEdge(Edge* e) {
  int k;
  int src = e->src();
  int dst = e->dst();
  EdgeHashNode * h = lookupEdgeHashNode(src, dst);
  EdgeHashNode * f, * g;  

  if (h == 0) {
    fprintf(stderr, "nam: trying to delete nonesisting edge (%d,%d)\n", src, dst);
    exit(1);
  }

  //XXX do we need to process queue ? leave it to the future 10/01/98
  k = ehash(src, dst);

  for (f = hashtab_[k]; f != 0; f = f->next) {
    if (h->src == f->src && h->dst == f->dst) {
      if (f == hashtab_[k]) {
        hashtab_[k] = f->next;
        break;
      } else {
        g->next = f->next;      
        break;
      } 
    }
    g = f;
  }

  
  
  delete h;
}

//----------------------------------------------------------------------
// void 
// NetModel::BoundingBox(BBox& bb) {
// XXX Make this cheaper (i.e. cache it)
//----------------------------------------------------------------------
void NetModel::BoundingBox(BBox& bb) {
      /* ANSI C limits, from float.h */
      bb.xmin = bb.ymin = FLT_MAX;
      bb.xmax = bb.ymax = -FLT_MAX;

      for (Animation* a = drawables_; a != 0; a = a->next()) 
            a->merge(bb);
}

/*
 Animation* NetModel::inside(double now, float px, float py) const
 {
      for (Animation* a = animations_; a != 0; a = a->next())
            if (a->inside(now, px, py))
                  return (a);
      for (Animation* d = drawables_; d != 0; d = d->next())
            if (d->inside(now, px, py))
                  return (d);

      return (0);
 }
*/
// Used exclusively for start_info() in nam.tcl. It ignores all tag objects
// and therefore should *not* be used for editing.

Animation* NetModel::inside(float px, float py) const
{

      for (Animation* a = animations_; a != 0; a = a->next()) {
           if (a->type() == BPACKET) {
              BPacket* b = (BPacket* ) a ;
            if ((b->inside(px, py)) && (a->classid() != ClassTagID))
                  return (a);
           } 
           else  {
            if ((a->inside(px, py)) && (a->classid() != ClassTagID))
                  return (a);
           }
      }
      for (Animation* d = drawables_; d != 0; d = d->next()) {
            if ((d->inside(px, py) && (d->classid() != ClassTagID)))
                  return (d);
      }

      return (0);
}

int NetModel::add_monitor(Animation *a)
{
  Monitor *m = new Monitor(mon_count_, a, node_size_);
  m->next(monitors_);
  monitors_=m;
  return mon_count_++;
}

int NetModel::monitor(double now, int monitor, char *result, int len)
{
  /*XXX should get rid of this search*/
  for(Monitor *m=monitors_; m!=NULL; m=m->next()) {
    if (m->monitor_number()==monitor) {
      m->update(now, result, len);
      if (strlen(result)==0)
      delete_monitor(m);
      return 0;
    }
  }
  result[0]='\0';
  return -1;
}

//----------------------------------------------------------------------
// void NetModel::check_monitors(Animation *a)
//   - A new animation just got created.  Check to see if we should 
//     already have a monitor on it.
//----------------------------------------------------------------------
void NetModel::check_monitors(Animation *a) {
      MonState * ms;
      Monitor * m;
      // Returns a "newed" MonState
      ms = a->monitor_state();

      if (ms == NULL) {
            return;
      }

      for(m = monitors_; m != NULL; m = m->next()) {
            if ((m->mon_state_ != NULL) &&
                        (m->mon_state_->type = ms->type)) {

                  switch (ms->type) {
                        case MON_PACKET:
                              if (m->mon_state_->pkt.id == ms->pkt.id) {
                                    m->anim(a);
                                    delete ms;
                                return;
                              }
                              break;
                        case MON_ROUTE: 
                              if ((m->mon_state_->route.src == ms->route.src)&&
                                          (m->mon_state_->route.group == ms->route.group)) {
                                    m->anim(a);
                                    delete ms;
                                    return;
                              }
                              break;
                  }
            }
      }

//  What happens to ms after the end of this function?  Maybe the memory leak?
//    fprintf(stderr, "Reached outside of check_monitors\n");
}

void NetModel::delete_monitor(int monitor)
{
  /*this version of delete_monitor is called from the GUI*/
  /*given a monitor, remove it from the model's monitor list*/
  Monitor *tm1, *tm2;
  if (monitors_==NULL)
    return;
  tm1=monitors_;
  tm2=monitors_;
  while ((tm1!=NULL)&&(tm1->monitor_number()!=monitor))
    {
      tm2=tm1;
      tm1=tm1->next();
    }
  if (tm1!=NULL)
    {
      tm2->next(tm1->next());
      if (tm1==monitors_)
      monitors_=tm1->next();
      if (tm1->anim()!=NULL)
      tm1->anim()->remove_monitor();
      delete tm1;
    }
}

void NetModel::delete_monitor(Monitor *m)
{
  /*given a monitor, remove it from a node's agent list*/
  Monitor *tm1, *tm2;
  if (monitors_==NULL)
    return;
  tm1=monitors_;
  tm2=monitors_;
  while ((tm1!=m)&&(tm1!=NULL))
    {
      tm2=tm1;
      tm1=tm1->next();
    }
  if (tm1!=NULL)
    {
      tm2->next(tm1->next());
      if (tm1==monitors_)
      monitors_=tm1->next();
      tm1->anim()->remove_monitor();
      delete tm1;
    }
}

void NetModel::delete_monitor(Animation *a)
{
  /*given a monitor, remove it from a node's agent list*/
  /*this version gets called when an animation deletes itself*/
  /*XXX animations sometimes get deleted when the packet changed link*/
  Monitor *tm1, *tm2;
  if (monitors_==NULL)
    return;
  tm1=monitors_;
  tm2=monitors_;
  while ((tm1!=NULL)&&(tm1->anim()!=a))
    {
      tm2=tm1;
      tm1=tm1->next();
    }
  if (tm1!=NULL)
    {
      tm2->next(tm1->next());
      if (tm1==monitors_)
      monitors_=tm1->next();
      delete tm1;
    }
}

void NetModel::saveState(double tim)
{
      State* state = State::instance();
      StateInfo min;
      min.time = 10e6;
      min.offset = 0;
      Animation *a, *n;
      StateInfo si;

      /*
       * Update the animation list first to remove any unnecessary
       * objects in the list.
       */
      for (a = animations_; a != 0; a = n) {
            n = a->next();
            a->update(tim);
      }
      for (a = animations_; a != 0; a = n) {
            n = a->next();
            si = a->stateInfo();
            if (si.time < min.time)
                  min = si;
      }
      if (min.offset)
            state->set(tim, min);
}

//---------------------------------------------------------------------
// void 
// NetModel::handle(const TraceEvent& e, double now, int direction)
//   - Trace event handler.
//---------------------------------------------------------------------
void 
NetModel::handle(const TraceEvent& e, double now, int direction) {
  QueueItem *q;
  EdgeHashNode *ehn, *ehnrev;
  Edge* ep;
  Node *n;
  //Packet *p;
  Route *r;
  Agent *a;
  Feature *f;
  float x, y;
  int pno;
  double txtime;
  bool do_relayout = false;

  if (e.time > State::instance()->next())
    saveState(e.time);

  switch (e.tt) {
    case 'T':
      // Dummy event no-op used for realtime time synchronization
      break;
              
    case 'v': {  
      // 'variable' -- just execute it as a tcl command 
      if (nam_ == 0) {
        fprintf(stderr, "Couldn't evaluate %s without animator\n",
          e.image);
        break;
      }
      const char *p = e.image + e.ve.str;
      char *q = new char[strlen(nam_->name()) + strlen(p) + 2];
      sprintf(q, "%s %s", nam_->name(), p);
      Tcl::instance().eval(q);
      delete []q;
      break;
    }
    
    case 'h':
      // traffic filter
      if (wireless_) {
        if (strcmp(e.pe.pkt.wtype,"AGT") == 0 ) return ; 
        if (!showData_) { // filter out data packet
           if ((strcmp(e.pe.pkt.wtype,"RTR") == 0 ) ||
               (strcmp(e.pe.pkt.wtype,"MAC") == 0 )) {
              if (((strcmp(e.pe.pkt.type,"cbr") == 0) ||
                   (strcmp(e.pe.pkt.type,"tcp") == 0) ||
                   (strcmp(e.pe.pkt.type,"ack") == 0))) 
              return ;
           }
        }
        if (!showRouting_){ // filter out routing packet
           if (strcmp(e.pe.pkt.wtype,"RTR") == 0 ) {
              if (!((strcmp(e.pe.pkt.type,"cbr") == 0)||
                   (strcmp(e.pe.pkt.type,"tcp") == 0)   ||
                   (strcmp(e.pe.pkt.type,"ack") == 0))) 
              return ;
           }
        }
        if (!showMac_){ // filter out routing packet
           if (strcmp(e.pe.pkt.wtype,"MAC") == 0 ) {
              if (!((strcmp(e.pe.pkt.type,"cbr") == 0)||
                   (strcmp(e.pe.pkt.type,"tcp") == 0)   ||
                   (strcmp(e.pe.pkt.type,"ack") == 0))) 
              return ;
           }
        }
      }

      // show only packet with same chracteristics as selected packet
      if (selectedSrc_ != -1) { //filter being trigger
        if (e.pe.pkt.esrc != selectedSrc_ ) return ;
      }
      if (selectedDst_ != -1) { 
        if (e.pe.pkt.edst != selectedDst_ ) return ;
      }
      if (selectedFid_ != -1) { 
        if (atoi(e.pe.pkt.convid) != selectedFid_ ) return ;
      }
      if (selectedTraffic_[0] != '\0') { 
        if (strcmp(e.pe.pkt.type,selectedTraffic_) != 0 ) return ;
      }

      // hide packet with same chracteristics as selected packet
      if (hideSrc_ != -1) { // filter being trigger
        if (e.pe.pkt.esrc == hideSrc_ ) return ;
      }
      if (hideDst_ != -1) { 
        if (e.pe.pkt.edst == hideDst_ ) return ;
      }
      if (hideFid_ != -1) { 
        if (atoi(e.pe.pkt.convid) == hideFid_ ) return ;
      }
      if (hideTraffic_[0] != '\0') { 
        if (strcmp(e.pe.pkt.type,hideTraffic_) == 0 ) return ;
      }

      //change the packet color on the fly
      if (selectedColor_ < 0 ) {
        Paint *paint = Paint::instance();
        selectedColor_ = paint->lookup("black",1);
      }
      if (colorSrc_ != -1) { 
        if (e.pe.pkt.esrc == colorSrc_ ) 
          paint_[atoi(e.pe.pkt.convid)] = selectedColor_  ;
      }
      if (colorDst_ != -1) { 
        if (e.pe.pkt.edst == colorDst_ ) 
          paint_[atoi(e.pe.pkt.convid)] = selectedColor_  ;
      }
      if (colorFid_ != -1) { 
        if (atoi(e.pe.pkt.convid) == colorFid_ ) 
          paint_[atoi(e.pe.pkt.convid)] = selectedColor_  ;
      }
      if (colorTraffic_[0] != '\0') { 
        if (strcmp(e.pe.pkt.type,colorTraffic_) == 0 ) 
            paint_[atoi(e.pe.pkt.convid)] = selectedColor_  ;
      }

      if (resetf_) 
         paint_[atoi(e.pe.pkt.convid)] = oldpaint_[atoi(e.pe.pkt.convid)] ;

      if (direction==BACKWARDS)
        break;

      if (e.pe.dst == -1) { //broadcasting
        //a quick hack to give fixed transmission + delay time for
        //broadcasting packet
        if (e.time + BPACKET_TXT_TIME < now)
          break ;

        n = lookupNode(e.pe.src);
        BPacket * p = new BPacket(n->x(), n->y(),e.pe.pkt,
                                  e.time,e.offset,direction,
                          e.pe.pkt.wBcastDuration
                              ? e.pe.pkt.wBcastDuration
                              : bcast_duration_,
                          e.pe.pkt.wBcastRadius
                              ? e.pe.pkt.wBcastRadius
                              : bcast_radius_);
        p->insert(&animations_);
        p->paint(paint_[e.pe.pkt.attr & paintMask_]);
        check_monitors(p);
        break;
      }

      ehn = lookupEdgeHashNode(e.pe.src, e.pe.dst);
      if (ehn == 0 || (ep = ehn->edge) == 0)
        return;

      /*
       * If the current packet will arrive at its destination
       * at a later time, insert the arrival into the queue
       * of animations and set its paint id (id for X graphics
       * context.
       */
      txtime = ep->txtime(e.pe.pkt.size);
      if (e.time + txtime + ep->delay() >= now) {
        /* XXX */
        Packet *p = new Packet(ep, e.pe.pkt, e.time,
                               txtime, e.offset);
        p->insert(&animations_);
        p->paint(paint_[e.pe.pkt.attr & paintMask_]);
        check_monitors(p);

       // fprintf(stderr, "packet %d sent from %d towards %d\n",
       //         e.pe.pkt.id, e.pe.src, e.pe.dst);
      }
      break;

    case 'a':
      n = lookupNode(e.ae.src);
      if (n == 0)
        return;
      a = n->find_agent((char *) e.ae.agent.name);
      if ((direction==FORWARDS)&&(e.ae.agent.expired==0)) {
        if (a == NULL) {
          // will only create an agent if there isn't one already 
          // there with the same name
          a = new BoxAgent(e.ae.agent.name, n->size());
          a->Animation::insert(&animations_);
          n->add_agent(a);
          placeAgent(a, n);
        }
      } else {
        if (a != NULL) {
          n->delete_agent(a);
          delete a;
        }
      }
      break;

    case 'f':
      // We don't need any redraw for this, because it's always 
      // displayed in monitors in a separate pane
      n = lookupNode(e.fe.src);
      if (n == 0)
        return;
      a = n->find_agent((char *)e.fe.feature.agent);
      if (a == 0)
        return;
      f = a->find_feature((char *)e.fe.feature.name);
      if (f == 0) {
        switch (e.fe.feature.type) {
        case 'v':
          f = new VariableFeature(a, e.fe.feature.name);
          break;
        case 'l':
          f = new ListFeature(a, e.fe.feature.name);
          break;
        case 's':
        case 'u':
        case 'd':
          f = new TimerFeature(a, e.fe.feature.name);
          break;
        }
        a->add_feature(f);
      }

      if (((direction==FORWARDS) && (e.fe.feature.expired == 0)) ||
          ((direction==BACKWARDS) && (strlen(e.fe.feature.oldvalue) > 0))) {
        char *value;
        double time_set;
        if (direction==FORWARDS) {
          value=(char *)e.fe.feature.value;
          time_set=now;
        } else {
          value=(char *)e.fe.feature.oldvalue;
          /*XXX need an extra value here*/
          time_set=0.0;
        }
        switch (e.fe.feature.type) {
          case 'v':
          case 'l':
            f->set_feature(value);
            break;
          case 's':
            f->set_feature(atof(value), TIMER_STOPPED, time_set);
            break;
          case 'u':
            f->set_feature(atof(value), TIMER_UP, time_set);
            break;
          case 'd':
            f->set_feature(atof(value), TIMER_DOWN, time_set);
            break;
        }
      } else {
        a->delete_feature(f);
        delete f;
      }
      break;

    case 'r':
      if (direction == FORWARDS)
        break;

      if (e.pe.dst == -1) { //broadcasting
        //a quick hack to give fixed transmission + delay time for
        //broadcasting packet
        if (e.time - BPACKET_TXT_TIME > now)
          break ;

        n = lookupNode(e.pe.src);
        BPacket * p = new BPacket(n->x(), n->y(),e.pe.pkt,
                                  e.time,e.offset,direction,
                          e.pe.pkt.wBcastDuration
                              ? e.pe.pkt.wBcastDuration
                              : bcast_duration_,
                          e.pe.pkt.wBcastRadius
                              ? e.pe.pkt.wBcastRadius
                              : bcast_radius_);
        p->insert(&animations_);
        p->paint(paint_[e.pe.pkt.attr & paintMask_]);
        check_monitors(p);
        break;
      }

      ehn = lookupEdgeHashNode(e.pe.src, e.pe.dst);
      if (ehn == 0 || (ep = ehn->edge) == 0)
        return;

      /*
       * If the current packet will arrive at its destination
       * at a later time, insert the arrival into the queue
       * of animations and set its paint id (id for X graphics
       * context.
       */
      txtime = ep->txtime(e.pe.pkt.size);
      if (e.time - (txtime + ep->delay()) <= now) {
        /* XXX */
        Packet *p = new Packet(ep, e.pe.pkt,
                               e.time-(ep->delay() + txtime),
                               txtime, e.offset);
        p->insert(&animations_);
        p->paint(paint_[e.pe.pkt.attr & paintMask_]);
        check_monitors(p);
      }
      break;

    case '+':
      ehn = lookupEdgeHashNode(e.pe.src, e.pe.dst);
      if (direction == FORWARDS) {
        if (ehn != 0 && ehn->queue != 0) {
          QueueItem *qi = new QueueItem(e.pe.pkt, e.time,
                                        e.offset);
          qi->paint(paint_[e.pe.pkt.attr & paintMask_]);
          ehn->queue->enque(qi,QUEUE_TAIL);
          qi->insert(&animations_);
          check_monitors(qi);
        }
      } else {
        if (ehn != 0 && ehn->queue != 0) {
          q = ehn->queue->remove(e.pe.pkt);
          delete q;
        }
      }
      break;

    case '-':
      ehn = lookupEdgeHashNode(e.pe.src, e.pe.dst);
      if (direction == FORWARDS) {
        if (ehn != 0 && ehn->queue != 0) {
          q = ehn->queue->remove(e.pe.pkt);
          delete q;
        }
      } else {
        if (ehn != 0 && ehn->queue != 0) {
          QueueItem *qi = new QueueItem(e.pe.pkt, e.time,
                                        e.offset);
          qi->paint(paint_[e.pe.pkt.attr & paintMask_]);
          ehn->queue->enque(qi,QUEUE_HEAD);
          qi->insert(&animations_);
          check_monitors(qi);
        }
      }
      break;

    case 'E': {
      // Nothing for now
      Group *grp = lookupGroup(e.pe.dst);
      if (grp == NULL) {
        fprintf(stderr, "Packet destination group %d not found\n",
          e.pe.dst);
        return;
      }
      int *mbr = new int[grp->size()];
      grp->get_members(mbr);
      for (int i = 0; i < grp->size(); i++) {
        QueueItem *qi = new QueueItem(e.pe.pkt, e.time,
              e.offset);
        qi->paint(paint_[e.pe.pkt.attr & paintMask_]);
        n = lookupNode(mbr[i]);
        if (n == 0) {
          fprintf(stderr, 
            "Group member %d not found\n",
            mbr[i]);
          return;
        }
        if (direction == FORWARDS) {
          n->queue()->enque(qi, QUEUE_TAIL);
          qi->insert(&animations_);
          check_monitors(qi);
        } else {
          qi = n->queue()->remove(e.pe.pkt);
          delete qi;
        }
      }
      delete mbr;    
      break;
    }

    case 'D': {
      n = lookupNode(e.pe.dst);
      if (n == NULL) {
        fprintf(stderr, "Bad node id %d for session deque event\n",
          e.pe.dst);
        return;
      }
      if (direction == FORWARDS) {
        q = n->queue()->remove(e.pe.pkt);
        delete q;
      } else {
        QueueItem *qi = new QueueItem(e.pe.pkt, e.time, e.offset);
        qi->paint(paint_[e.pe.pkt.attr & paintMask_]);
        n->queue()->enque(qi, QUEUE_HEAD);
        qi->insert(&animations_);
        check_monitors(qi);
      }
      break;
    }

    case 'P':  // session drop
      // Get packet to drop.
      if (direction == FORWARDS) {
        // fprintf(stderr, "drop on %d -> %d\n", e.pe.src, e.pe.dst);
        n = lookupNode(e.pe.dst);
        if (n == 0)
          return;
        q = 0;
        if (n->queue() != 0) {
          // Remove packet from session queue 
          q = n->queue()->remove(e.pe.pkt);
          if (q == 0) {
            // No such packet in queue??? 
            fprintf(stderr, "No packet drop %id in queue\n",
                            e.pe.pkt.id);
            return;
          }
          q->position(x,y);
          pno = q->paint();
          delete q;
        }
        
      // Compute the point at which the dropped packet disappears.
      // Let's just make this sufficiently far below the lowest
      // thing on the screen.
    
      // Watch out for topologies that have all their nodes lined
      // up horizontally. In this case, nymin_ == nymax_ == 0.
      // Set the bottom to -0.028. This was chosen empirically.
      // The nam display was set to the maximum size and the lowest
      // position on the display was around -0.028.
     
      float bot;
      if (nymin_ - nymax_ < 0.01)
        bot = nymin_ - 10 * n->size() ;
      else
        bot = nymin_ - (nymax_ - nymin_);
    
      Drop *d = new Drop(x, y, bot, n->size()*0.5, /* This is a hack */
                         e.time, e.offset, e.pe.pkt);
      d->paint(pno);
      d->insert(&animations_);
      check_monitors(d);
      break;
    } else {
      /*direction is BACKWARDS - need to create the packet*/
      //fprintf(stderr, "Packet drop backwards\n");
    }

    case 'G': {
      /* Group event */
      Group *grp = lookupGroup(e.ge.src);
      if (grp == NULL) {
        grp = new Group(e.ge.grp.name, e.ge.src);
        add_group(grp);
      }
      
      if (e.ge.grp.flag == GROUP_EVENT_JOIN) {
        grp->join(e.ge.grp.mbr);
        // XXX
        // Hard-coded queue angle for session queues. :(
        // Create session queue here because they are not like
        // traditional queues which are created when nam
        // started. Group member may dynamically join/leave,
        // so may session queues. We create them here because 
        // there's a 1-1 relationship between join and creating
        // session queues.
        n = lookupNode(e.ge.grp.mbr);
        if (n == NULL) {
          fprintf(stderr, "Bad node %d for group event\n",
            e.ge.grp.mbr);
          return;
        }
        // Need more consideration on the placement of these queues
        Queue *q = new Queue(0.5);
        q->next_ = queues_;
        queues_ = q;
        n->add_sess_queue(e.ge.src, q);
      } else if (e.ge.grp.flag == GROUP_EVENT_LEAVE)
        // No deletion of session queues.
        grp->leave(e.ge.grp.mbr);
      break;
    }
    case 'l': 
      /*link event*/
      // if src or dst = -1 this is a layout link (-t *)
      // so skip over it
      if (e.le.src == -1 || e.le.dst == -1)
        return;

      ehn = lookupEdgeHashNode(e.le.src, e.le.dst);
      if (ehn == 0) {
        // if edge doesn't exist try to create it dynamically
        ep = addEdge(e.le.src, e.le.dst, e);
        if (!ep) {
          fprintf(stderr, "Unable to create edge(%d,%d) dynamically.\n",
                           e.le.src, e.le.dst);
          return;
        }
        ehn = lookupEdgeHashNode(e.le.src, e.le.dst);
        do_relayout= true;
      }

      ehnrev = lookupEdgeHashNode(e.le.dst, e.le.src);
      if (ehnrev == 0) {
        // if edge doesn't exist try to create it dynamically
        ep = addEdge(e.le.dst, e.le.src, e);
        if (!ep) {
          fprintf(stderr, "Unable to create reverse edge(%d,%d) dynamically.\n",
                           e.le.dst, e.le.src);
          return;
        }
        ehnrev = lookupEdgeHashNode(e.le.dst, e.le.src);
        do_relayout = true;
      }

      if (do_relayout) {
        //relayout();
        relayoutNode(lookupNode(e.le.src));
        relayoutNode(lookupNode(e.le.dst));
        do_relayout = false;
      }
      /*note: many link events are bidirectional events so be careful to
        apply them to both a link and the reverse of it*/

      if (direction==FORWARDS)  {
        // Always save the color before the last DOWN event
        if (strncmp(e.le.link.state, "DOWN", 4)==0) {
          ehn->edge->set_down("red");
          ehnrev->edge->set_down("red");
        } else if (strncmp(e.le.link.state, "UP", 2)==0) {
          /* XXX Assume an UP event always follows a DOWN event */
          ehn->edge->set_up();
          ehnrev->edge->set_up();
        } else if (strncmp(e.le.link.state, "COLOR", 5) == 0) {
          ehn->edge->color((char *)e.le.link.color);
           ehnrev->edge->color((char *)e.le.link.color);
        } else if (strncmp(e.le.link.state, "DLABEL", 6) == 0) {
          ehn->edge->dlabel((char *)e.le.link.dlabel);
          ehnrev->edge->dlabel((char *)e.le.link.dlabel);
        } else if (strncmp(e.le.link.state, "DCOLOR", 6) == 0) {
          ehn->edge->dcolor((char *)e.le.link.dcolor);
          ehnrev->edge->dcolor((char *)e.le.link.dcolor);
        } else if (strncmp(e.le.link.state, "DIRECTION", 9) == 0) {
          ehn->edge->direction((char *)e.le.link.direction);
          ehnrev->edge->direction((char *)e.le.link.direction);
        } else if (strncmp(e.le.link.state, "DIRECTION", 9) == 0) {
          ehn->edge->direction((char *)e.le.link.direction);
          ehnrev->edge->direction((char *)e.le.link.direction);
        } 

      } else {
        if (strncmp(e.le.link.state, "UP", 2)==0) {
          ehn->edge->set_down("red");
          ehnrev->edge->set_down("red");
        } else if (strncmp(e.le.link.state, "DOWN", 4)==0) {
          ehn->edge->set_up();
        } else if (strncmp(e.le.link.state, "COLOR", 5) == 0) {
          ehn->edge->color((char *)e.le.link.oldColor);
          ehnrev->edge->color((char *)e.le.link.oldColor);
        } else if (strncmp(e.le.link.state, "DLABEL", 6) == 0) {
          ehn->edge->dlabel((char *)e.le.link.odlabel);
          ehnrev->edge->dlabel((char *)e.le.link.odlabel);
        } else if (strncmp(e.le.link.state, "DCOLOR", 6) == 0) {
          ehn->edge->dcolor((char *)e.le.link.odcolor);
          ehnrev->edge->dcolor((char *)e.le.link.odcolor);
        } else if (strncmp(e.le.link.state, "DIRECTION", 9) == 0) {
          ehn->edge->direction((char *)e.le.link.odirection);
          ehnrev->edge->direction((char *)e.le.link.odirection);
        } else if (strncmp(e.le.link.state, "DIRECTION", 9) == 0) {
          ehn->edge->direction((char *)e.le.link.odirection);
          ehnrev->edge->direction((char *)e.le.link.odirection);
        }
    }
      break;
    case 'n':
      /* node event */
      // Return if node has -t * 
      // Ths node is only used for initial layout
      if (e.ne.src == -1)
        return;

      n = lookupNode(e.ne.src);
      if (n == 0) {
        // if node doesn't exist try to create it dynamically
        n = addNode(e);
        if (!n)
          return;
      }
      if (direction==FORWARDS) {
        if (strncmp(e.ne.node.state, "DOWN", 4)==0) {
                n->set_down("gray");
        } else if (strncmp(e.ne.node.state, "UP", 2)==0) {
                n->set_up();
        } else if (strncmp(e.ne.node.state, "COLOR", 5) == 0) {
                // normal color can be defined by user
                n->color((char *)e.ne.node.color);
                n->lcolor((char *)e.ne.node.lcolor);
        } else if (strncmp(e.ne.node.state, "DLABEL", 6) == 0) {
                n->dlabel((char *)e.ne.node.dlabel); 
        } else if (strncmp(e.ne.node.state, "DCOLOR", 6) == 0) {
                n->dcolor((char *)e.ne.node.dcolor);
        } else if (strncmp(e.ne.node.state, "DIRECTION", 9) == 0) {
                n->direction((char *)e.ne.node.direction);
        }
      } else {
        if (strncmp(e.ne.node.state, "UP", 4)==0) {
                n->set_down("gray");
        } else if (strncmp(e.ne.node.state, "DOWN", 2)==0) {
                n->set_up();
        } else if (strncmp(e.ne.node.state, "COLOR", 5) == 0) {
                n->color((char *)e.ne.node.oldColor);
                n->lcolor((char *)e.ne.node.olcolor);
        }  else if (strncmp(e.ne.node.state, "DLABEL", 6) == 0) {
                n->dlabel((char *)e.ne.node.odlabel);
        } else if (strncmp(e.ne.node.state, "DCOLOR", 6) == 0) {
                n->dcolor((char *)e.ne.node.odcolor);
        } else if (strncmp(e.ne.node.state, "DIRECTION", 9) == 0) {
                n->direction((char *)e.ne.node.odirection);
        }
      }  
      break;

    case 'm':
      /* node mark event */
      NodeMark *cm;
      n = lookupNode(e.me.src);
      if (n == 0)
        return;
      cm = n->find_mark((char *) e.me.mark.name);
      if (direction == FORWARDS) {
        if (e.me.mark.expired == 0) {
          /* once created, a node mark cannot be changed*/
          if (cm == NULL) 
            n->add_mark((char *)e.me.mark.name, 
                        (char *)e.me.mark.color,
                        (char *)e.me.mark.shape);
        } else 
          /* expired */
          n->delete_mark((char *)e.me.mark.name);
      } else {
        /* 
         * backward: 
         * (1) create it if expired == 1
         * (2) delete it if expired == 0
         */
        if (e.me.mark.expired == 0) 
          n->delete_mark((char *)e.me.mark.name);
        else {
          /* re-create the circle */
          if (cm == NULL)
            n->add_mark((char *)e.me.mark.name, 
            (char *)e.me.mark.color,
            (char *)e.me.mark.shape);
        }
      }
      break;

    case 'R':
      // route event
      if (((e.re.route.expired==0)&&(direction==FORWARDS))||
          ((e.re.route.expired==1)&&(direction==BACKWARDS))) {
         // this is a new route
        n = lookupNode(e.re.src);
        if (n == 0)
          return;

        ehn = lookupEdgeHashNode(e.re.src, e.re.dst);
        if (ehn == 0)
          return;
        int oif=1;
        if (strncmp(e.re.route.mode, "iif", 3)==0)
          oif=0;
        r = new Route(n, ehn->edge, e.re.route.group, e.re.route.pktsrc, 
                      e.re.route.neg, oif, e.re.route.timeout, now);
        n->add_route(r);
        n->place_route(r);
        r->insert(&animations_);
        r->paint(paint_[e.re.route.group & paintMask_]);
        check_monitors(r);
      } else {
        // an old route expired
        n = lookupNode(e.re.src);
        if (n == 0)
          return;
        
        // src and dst are node ids 
        ehn = lookupEdgeHashNode(e.re.src, e.re.dst);
        if (ehn == 0)
          return;
        int oif = 1;
        if (strncmp(e.re.route.mode, "iif", 3) == 0) {
          oif=0;
        }
        r = n->find_route(ehn->edge, e.re.route.group,
                          e.re.route.pktsrc, oif);
        if (r == 0) {
          fprintf(stderr, "nam: attempt to delete non-existent route\n");
          abort();
        }
        n->delete_route(r);
        delete r;
      }
      break;

    case 'd':
      add_drop(e, now, direction);
  }
}

//---------------------------------------------------------------------
//  void
// NetModel::add_drop(const TraceEvent &e, double now, int direction)
//   - This method adds a packet drop animation to the animations_ list
//   - Packet drops can occur from queues and edges.  If the queue is 
//     not being displayed the packet is dropped from the node
//     position.
//   - If the animation direction is BACKWARDS a packet should be
//     created but it appears that this code does not do that.
//---------------------------------------------------------------------
void
NetModel::add_drop(const TraceEvent &e, double now, int direction) {
  EdgeHashNode *ehn;
  QueueItem *q;
  Packet *p;
  Lan *lan;
  float x, y;
  int pno;  // paint number (color with which to draw)
  
  // Get packet to drop. 
  if (direction == FORWARDS) {
    ehn = lookupEdgeHashNode(e.pe.src, e.pe.dst);
    if (ehn == 0) {
     return;
    }

    q = 0;
    if (ehn->queue != 0) {
      // if there's a queue, try removing it from the queue first
      // queue drops are more common than link drops...
      q = ehn->queue->remove(e.pe.pkt);
    }

    if (q == 0) {
      // perhaps it's a link packet drop
      p = lookupPacket(e.pe.src, e.pe.dst, e.pe.pkt.id);
      if (p != NULL) {
        // it was a link packet drop
        p->position(x, y, now);
        ehn->edge->DeletePacket(p);
        pno = p->paint();
        delete p;
      } else if ((lan = lookupLan(e.pe.src)) != NULL) {
        /* 
         * If it's a Lan (selective) packet drop, which means
         * the packet is only dropped on some of the lan links,
         * register the packet on the lan and drop it when the
         * packet is actually transmitted to that lan link.
         *
         * When the packet is actually dropped, this function will be 
         * called again, but at that time the packet will be actually
         * on the link and this code will not be executed.
         */
        lan->register_drop(e);
        return;
      } else {
        // probably it was a queue drop, but we're not displaying
        // that queue
       
        // It's possible that this packet is dropped directly from 
        // the queue, even before the enqT_ module. In this case, 
        // we should still produce a packet drop; we use the position
        // of the node to generate the drop.
        Node *s = lookupNode(e.pe.src);
        if (s == NULL) {
          fprintf(stderr, "NetModel::add_drop(): cannot find src node for packet drop.\n");
          abort();
        }
        x = s->x();
        y = s->y();
        pno = paint_[e.pe.pkt.attr & paintMask_];
      }
    } else {
      // packet dropped from queue
      // get x,y position of queue item
      q->position(x, y);
      pno = q->paint();
      delete q;
    }

    /*
     * Compute the point at which the dropped packet disappears.
     * Let's just make this sufficiently far below the lowest
     * thing on the screen.
     *
     * Watch out for topologies that have all their nodes lined
     * up horizontally. In this case, nymin_ == nymax_ == 0.
     * Set the bottom to -0.028. This was chosen empirically.
     * The nam display was set to the maximum size and the lowest
     * position on the display was around -0.028.
     */
    float bottom;
    if (nymin_ - nymax_ < 0.01) {
      bottom = nymin_ - 20.0 * ehn->edge->PacketHeight() ;
    } else {
      bottom = nymin_ - (nymax_ - nymin_);
    }
    
    // The drop animation is taken care of by the drop class
    Drop * d = new Drop(x, y, bottom, 4 * ehn->edge->PacketHeight(),
                        e.time, e.offset, e.pe.pkt);
    d->paint(pno);
    d->insert(&animations_);
    check_monitors(d);
    return;
  } else {
    // direction is BACKWARDS - need to create the packet
    Lan *lan = lookupLan(e.pe.src);
    if (lan != NULL) {
       // We need to remove drop status in lans
      lan->remove_drop(e);
      //fprintf(stderr, "lan dropped packet %d is removed on lan link %d->%d\n",
      //                e.pe.pkt.id, e.pe.src, e.pe.dst);
      return;
    }
  }
}


//----------------------------------------------------------------------------
// Node *
// NetModel::addNode(const TraceEvent &e)
//   - adds a node to the netmodel getting configuration info from the fields
//     in the TraceEvent
//----------------------------------------------------------------------------
Node *
NetModel::addNode(const TraceEvent &e)  {
  Node * n = NULL;
  char src[32];

  if (e.tt == 'n') {
    sprintf(src, "%d", e.ne.src);
    n = lookupNode(e.ne.src);
    // And remove them to be replaced by this node
    if (n != NULL) {
      fprintf(stderr, "Skipping duplicate node %s definition. \n", src);
      //removeNode(n);
    }

    // Determine Node Type
    if (!strncmp(e.ne.mark.shape, "circle",6)) {
      n = new CircleNode(src, e.ne.size);
    } else if (!strncmp(e.ne.mark.shape, "box", 3) || 
               !strncmp(e.ne.mark.shape, "square", 6)) {
      n = new BoxNode(src, e.ne.size);
    } else if (!strncmp(e.ne.mark.shape, "hexagon",7)) { 
      n = new HexagonNode(src, e.ne.size);
    } else {
      return NULL;
    }

    // Check for wireless node
    if (e.ne.wireless) {
      n->wireless_ = true;
      //fprintf(stderr, "We have wireless nodes :-) !!!\n");
    }

    // Node Address
    //  May need to check for no address passed in
    n->setaddr(e.ne.node.addr);
    addAddress(n->num(), e.ne.node.addr);

    // Node Color
    n->init_color(e.ne.node.color);
    n->lcolor(e.ne.node.lcolor);

    // dlabel initilization
    n->dlabel(e.ne.node.dlabel);

    // Set X,Y cordinates
    n->place(e.ne.x, e.ne.y);
    
    // Add Node to drawables list
    n->next_ = nodes_;
    nodes_ = n;
    n->Animation::insert(&drawables_);

    // Set Packet size to be running average of the last 5 nodes (25% of node size)
    packet_size_ = (4.0 * packet_size_ + e.ne.size*0.25)/5.0;
  }
  return (n);
}

//----------------------------------------------------------------------------
// Edge * 
// NetModel::addEdge(int argc, const char *const *argv)
//
//  <net> link <src> <dst> <bandwidth> <delay> <angle>
//  Create a link/edge between the specified source
//  and destination. Add it to this NetModel's list
//  of drawables and to the source's list of links.
//----------------------------------------------------------------------------
Edge * 
NetModel::addEdge(int argc, const char *const *argv) {
  Node * src, * dst;
  Edge * edge = NULL;
  double bandwidth, delay, length, angle;

  if (strcmp(argv[1], "link") == 0) {
    src = lookupNode(atoi(argv[2]));
    dst = lookupNode(atoi(argv[3]));
    
    if (src && dst) {
      bandwidth = atof(argv[4]);
      delay = atof(argv[5]);
      length = atof(argv[6]);
      angle = atof(argv[7]);
                        
      //enlarge link if the topology is a mixture of
      //wired and wireless network
      if (wireless_) {
        length = delay * WIRELESS_SCALE ;
      }

      edge = new Edge(src, dst, packet_size_, bandwidth, delay, length, angle, wireless_);
      edge->init_color("black");
      enterEdge(edge);
      edge->Animation::insert(&drawables_);
      src->add_link(edge);
    }
  }
  return edge;
}

//----------------------------------------------------------------------------
//
//----------------------------------------------------------------------------
Edge * 
NetModel::addEdge(int src_id, int dst_id, const TraceEvent &e) {
  Node *src, *dst;
  Edge * edge = NULL;
  double bandwidth, delay, length, angle;

  if (e.tt == 'l') {
    src = lookupNode(src_id);
    dst = lookupNode(dst_id);
    
    if (src && dst) {
      bandwidth = e.le.link.rate;
      delay = e.le.link.delay;
      length = e.le.link.length;
      angle = e.le.link.angle;
                        
      //enlarge link if the topology is a mixture of
      //wired and wireless network
      if (wireless_) {
        length = delay * WIRELESS_SCALE ;
      }

      edge = new Edge(src, dst, packet_size_, bandwidth, delay, length, angle, wireless_);
      edge->init_color("black");
      enterEdge(edge);
      edge->Animation::insert(&drawables_);
      src->add_link(edge);
    }
  }
  return edge;
}

void NetModel::addView(NetView* p)
{
      p->next_ = views_;
      views_ = p;
}

//----------------------------------------------------------------------
// Node * 
// NetModel::lookupNode(int nn) const
//----------------------------------------------------------------------
Node *
NetModel::lookupNode(int nn) const {
  for (Node* n = nodes_; n != 0; n = n->next_)
    if (n->num() == nn)
      return (n);
  return NULL;
}


//----------------------------------------------------------------------
// Edge *
// NetModel::lookupEdge(int source, int destination) const
//----------------------------------------------------------------------
Edge *
NetModel::lookupEdge(int source, int destination) const {
      EdgeHashNode * edge_hash_node;
      edge_hash_node = lookupEdgeHashNode(source, destination);
      return edge_hash_node->edge;
}


void NetModel::removeNode(Node *n) 
{
      Node *p, *q;
      // Remove node n from nodes_ list, then delete it
      for (p = nodes_; p != 0; q = p, p = p->next_)
            if (p == n)
                  break;
      if (p == nodes_)
            nodes_ = p->next_;
      else
            q->next_ = p->next_;
      delete p;
}

Agent *NetModel::lookupAgent(int id) const
{
      for (Node* n = nodes_; n != 0; n = n->next_) 
            for(Agent* a= n->agents(); a != 0; a = a->next_) 
                  if (a->number() == id) 
                        return (a);
      return (0); 
}

Lan *NetModel::lookupLan(int nn) const
{
      for (Lan* l = lans_; l != 0; l = l->next_)
            if (l->num() == nn)
                  return (l);
      /* XXX */
      //fprintf(stderr, "nam: no such lan %d\n", nn);
      //exit(1);
      return (0);// make visual c++ happy
}

Packet *NetModel::newPacket(PacketAttr &pkt, Edge *e, double time)
{
  /*this is called when we get a triggered event such as a packet
    getting duplicated within a LAN*/
  Packet *p = new Packet(e, pkt, time, e->txtime(pkt.size), 0);
  p->insert(&animations_);
  p->paint(paint_[pkt.attr & paintMask_]);
  check_monitors(p);
  return p;
}

Packet *NetModel::lookupPacket(int src, int dst, int id) const
{
  EdgeHashNode *h = lookupEdgeHashNode(src, dst);
  if (h == 0)
    return NULL;
  int ctr=0;
  for (Packet *p=h->edge->packets(); p!=NULL; p=p->next())
    {
#define PARANOID
#ifdef PARANOID
      ctr++;
      if (ctr>h->edge->no_of_packets()) abort();
#endif
      if (p->id() == id)
      return p;
    }
  /*have to fail silent or we can't cope with link drops when doing settime*/
  return 0;
}

/* Do not delete groups, because they are not explicitly deleted */
int NetModel::add_group(Group *grp)
{
      int newEntry = 1;
      Tcl_HashEntry *he = Tcl_CreateHashEntry(grpHash_,
                                              (const char *)grp->addr(), 
                                              &newEntry);
      if (he == NULL)
            return -1;
      if (newEntry) {
            Tcl_SetHashValue(he, (ClientData)grp);
            nGroup_++;
      }
      return 0;
}

Group* NetModel::lookupGroup(unsigned int addr)
{
      Tcl_HashEntry *he = Tcl_FindHashEntry(grpHash_, (const char *)addr);
      if (he == NULL)
            return NULL;
      return (Group *)Tcl_GetHashValue(he);
}

// Remove a view from the views link, but not delete it
void NetModel::remove_view(View *v)
{
      View *p, *q;
      p = q = views_;
      if (p == v) {
            views_ = p->next_;
            return;
      }
      while (p != NULL) {
            q = p;
            p = p->next_;
            if (p == v) {
                  q->next_ = p->next_;
                  return;
            }
      }
}

//----------------------------------------------------------------------
// int
// NetModel::command(int argc, const char *const *argv)
//   - Parses tcl commands (hook to enter c++ code from tcl)
//----------------------------------------------------------------------
int NetModel::command(int argc, const char *const *argv) {
      Tcl& tcl = Tcl::instance();
      int i;
      Node * node;
      double time;
      
      if (argc == 2) {
            if (strcmp(argv[1], "layout") == 0) {
                  /*
                   * <net> layout
                   * Compute reasonable defaults for missing node or edge
                   * sizes based on the maximum link delay. Lay out the
                   * nodes and edges as specified in the layout file.
                   */
                  scale_estimate();
                  placeEverything();
                  return (TCL_OK);
            }
            if (strcmp(argv[1], "showtrees") == 0) {
                  /*
                   * <net> showtrees
                   */
                  color_subtrees();
                  for (View* p = views_; p != 0; p = p->next_) {
                        p->draw();
                  }
                  return (TCL_OK);
            }
            if (strcmp(argv[1],"resetFilter")==0) {
                    resetf_ = 1 ;
                      selectedSrc_ = -1 ;
                    selectedDst_ = -1 ;
                    selectedFid_ = -1 ;
                      colorSrc_ = -1 ;
                    colorDst_ = -1 ;
                    colorFid_ = -1 ;
                      hideSrc_ = -1 ;
                    hideDst_ = -1 ;
                    hideFid_ = -1 ;
                      for (i = 0; i < PTYPELEN; ++i) {
                         selectedTraffic_[i] = '\0' ;
                         colorTraffic_[i] = '\0' ;
                         hideTraffic_[i] = '\0' ;
                        }

                  return (TCL_OK);
            }

      } else if (argc == 3) {
            if (strcmp(argv[1], "incr-nodesize") == 0) {
                  /*
                   * <net> incr-nodesize <factor>
                   */
                  node_sizefac_ *= atof(argv[2]);
                  for (Node *n = nodes_; n != 0; n = n->next_)
                        for (Edge *e=n->links(); e != 0; e = e->next_)
                              e->unmark();
                  scale_estimate();
                  placeEverything();
                  for (View *p = views_; p != 0; p = p->next_)
                        if ((p->width() > 0) && (p->height() > 0)) {
                              p->redrawModel();
                              p->draw();
                        }
                  return (TCL_OK);
            }
            if (strcmp(argv[1], "decr-nodesize") == 0) {
                  node_sizefac_ /= atof(argv[2]);
                  for (Node *n = nodes_; n != 0; n = n->next_)
                        for (Edge *e=n->links(); e != 0; e = e->next_)
                              e->unmark();
                  scale_estimate();
                  placeEverything();
                  for (View *p = views_; p != 0; p = p->next_)
                        if ((p->width() > 0) && (p->height() > 0)) {
                              p->redrawModel();
                              p->draw();
                        }
                  return(TCL_OK);
            }

            if (strcmp(argv[1], "updateNodePositions") == 0) {
                  time = strtod(argv[2], NULL);
                  for (node = nodes_; node; node = node->next_) {
                        node->updatePositionAt(time);
                        moveNode(node);  // This updates the links and agents connected to the node
                  }
                  return TCL_OK;
            }

            if (strcmp(argv[1], "view") == 0) {
                  /*
                   * <net> view <viewName>
                   * Create the window for the network layout/topology.
                   * Used for nam editor
                   */

                  EditView *v = new EditView(argv[2], this, 300, 700);
                  v->next_ = views_;
                  views_ = v;
                  return (TCL_OK);
            }

            if (strcmp(argv[1], "psview") == 0) {
                  /*
                   * <net> PSView <fileName>
                   * Print the topology to a file
                   */
                  PSView *v = new PSView(argv[2], this);
                  v->render();
                  delete(v);
                  return (TCL_OK);
            }
            if (strcmp(argv[1], "testview") == 0) {
                  /*
                   * Added for nam validation test 
                   */
                  TestView *v = new TestView(argv[2], this);
                  v->render();
                  delete(v);
                  return (TCL_OK);
            }
            if (strcmp(argv[1], "editview") == 0) {
                  /*
                   * <net> editview <viewname>
                   */
                  EditView *v = new EditView(argv[2], this);
                  v->next_ = views_;
                  views_ = v;
                  return (TCL_OK);
            }
            if (strcmp(argv[1],"savelayout")==0) {
                    save_layout(argv[2]);
                  return (TCL_OK);
            }
            if (strcmp(argv[1],"select-traffic")==0) {
                    strcpy(selectedTraffic_,argv[2]);
                  return (TCL_OK);
            }
            if (strcmp(argv[1],"select-src")==0) {
                    selectedSrc_ = atoi(argv[2]);
                  return (TCL_OK);
            }
            if (strcmp(argv[1],"select-dst")==0) {
                    selectedDst_ = atoi(argv[2]);
                  return (TCL_OK);
            }
            if (strcmp(argv[1],"select-fid")==0) {
                    selectedFid_ = atoi(argv[2]);
                  return (TCL_OK);
            }
            if (strcmp(argv[1],"hide-traffic")==0) {
                    strcpy(hideTraffic_,argv[2]);
                  return (TCL_OK);
            }
            if (strcmp(argv[1],"hide-src")==0) {
                    hideSrc_ = atoi(argv[2]);
                  return (TCL_OK);
            }
            if (strcmp(argv[1],"hide-dst")==0) {
                    hideDst_ = atoi(argv[2]);
                  return (TCL_OK);
            }
            if (strcmp(argv[1],"hide-fid")==0) {
                    hideFid_ = atoi(argv[2]);
                  return (TCL_OK);
            }
            if (strcmp(argv[1],"color-traffic")==0) {
                    resetf_ = 0 ;
                    strcpy(colorTraffic_,argv[2]);
                  return (TCL_OK);
            }
            if (strcmp(argv[1],"color-src")==0) {
                    resetf_ = 0 ;
                    colorSrc_ = atoi(argv[2]);
                  return (TCL_OK);
            }
            if (strcmp(argv[1],"color-dst")==0) {
                    resetf_ = 0 ;
                    colorDst_ = atoi(argv[2]);
                  return (TCL_OK);
            }
            if (strcmp(argv[1],"color-fid")==0) {
                    resetf_ = 0 ;
                    colorFid_ = atoi(argv[2]);
                  return (TCL_OK);
            }
            if (strcmp(argv[1],"select-color")==0) {
                  Paint *paint = Paint::instance();
                    selectedColor_ = paint->lookup(argv[2], 1);
                  if (selectedColor_ < 0) {
                    fprintf(stderr,"%s color: no such color: %s\n",
                          argv[0], argv[2]);
                    selectedColor_ = paint->lookup("black",1);
                    if (selectedColor_ < 0) {
                      tcl.resultf("%s no black! - bailing");
                      return (TCL_ERROR);
                    }
                  }
                  return (TCL_OK);
    }
            
        if (strcmp(argv[1], "node") == 0) {

//     else if (argc == 3 && strcmp(argv[1], "node") == 0)
            /*
             * <net> node <name> <shape> <color> <addr> [<size>]
             * Create a node using the specified name
             * and the default size and insert it into this
             * NetModel's list of drawables.
             */
            //Node* n = addNode(argc, argv);
      
      char * line = new char[strlen(argv[2])];
      strncpy(line, argv[2],strlen(argv[2]));
      parsetable_.parseLine(line);
            Node * node = addNode(traceevent_);
      delete line;
            if (node) {
                  return (TCL_OK);
            } else {
                  tcl.resultf("Unable to create Node %s.", argv[2]);
                  return (TCL_ERROR);
            }
    }


      } else if (argc >= 4 && strcmp(argv[1], "agent") == 0) {
            // Create a new agent
    
            /*
             * <net> agent <label> <role> <node> <flowcolor> <winInit> 
             *             <win> <maxcwnd>
             *             <tracevar> <start> <producemore> <stop>
             * <net> agent <label> <role> <node> <flowcolor> <packetSize>
             *       <interval> <start> <stop>
             */
            int node = atoi(argv[4]);
            int partner_num;
            Node *n = lookupNode(node);
    
            if (n == 0) {
                  tcl.resultf("Node %d doesn't exist.", node);
                  return (TCL_ERROR);
            }

            Agent *a = new BoxAgent((char *)argv[2], n->size());
            if (a == 0) {
                  tcl.resultf("Cannot create Agent %s exist on Node %d",
                            argv[2], node);
                  return (TCL_ERROR);
            }

            placeAgent(a, n);
            a->node_ = n;
            n->add_agent(a);
    
            a->AgentRole_ = (atoi(argv[3]));

            if (a->AgentRole_ == 20) {
                  a->setNumber(atoi(argv[5]));
                  partner_num = (atoi(argv[6]));

                  Agent *partner = lookupAgent(partner_num);
                  if (partner) {
                        a->AgentPartner_ = partner;
                        partner->AgentPartner_ = a;
                  }

            } else if ((a->AgentRole_ == 10) && 
                     (strcmp(argv[2], "CBR") == 0)) {
                  if (strcmp(argv[5],"(null)")!=0)
                        a->flowcolor((char *)argv[5]);
                  a->packetSize(atoi(argv[6]));
                  a->interval(atoi(argv[7]));
                  a->startAt(atof(argv[8]));
                  a->stopAt(atof(argv[9]));
                  a->setNumber(atoi(argv[10]));
                  partner_num = (atoi(argv[11]));

                  Agent *partner = lookupAgent(partner_num);
                  if (partner) {
                        a->AgentPartner_ = partner;
                        partner->AgentPartner_ = a;
                  }

            } else if (a->AgentRole_ != 0) {
                  if (strcmp(argv[5],"(null)")!=0)
                        a->flowcolor((char *)argv[5]);
                  a->windowInit(atoi(argv[6]));
                  a->window(atoi(argv[7]));
                  a->maxcwnd(atoi(argv[8]));
                  a->tracevar((char *)argv[9]);
                  a->startAt(atof(argv[10]));
                  a->produce(atoi(argv[11]));
                  a->stopAt(atof(argv[12]));
                  a->setNumber(atoi(argv[13]));
                  partner_num = (atoi(argv[14]));

                  Agent *partner = lookupAgent(partner_num);
                  if (partner) {
                        a->AgentPartner_ = partner;
                        partner->AgentPartner_ = a;
                  }
            }
            a->Animation::insert(&animations_);
            return (TCL_OK);

      } else if (argc == 4) {
            if (strcmp(argv[1], "new_monitor_agent") == 0) {
                  /* 
                   * <net> new_monitor_agent <node_id> <agent_name>
                   */
                  int node = atoi(argv[2]);
                  Node *n = lookupNode(node);
                  if (n == 0) {
                        tcl.resultf("Node %d doesn't exist.", node);
                        return (TCL_ERROR);
                  }
                  Agent *a = n->find_agent((char *)argv[3]);
                  if (a == 0) {
                        tcl.resultf("Agent %s not exist at node %d",
                                  argv[3], node);
                        return (TCL_ERROR);
                  }
                  tcl.resultf("%d", add_monitor(a));
                  return (TCL_OK);
            }

            if (strcmp(argv[1], "color") == 0) {
                  /*
                   * <net> color <packetClass> <colorName>
                   * Display packets of the specified class using
                   * the specified color.
                   */
                  int c = atoi(argv[2]);
//                if ((unsigned int)c > 1024) {
//                      tcl.resultf("%s color: class %d out of range",
//                                argv[0], c);
//                      return (TCL_ERROR);
//                }
                  // Convert this color index to [0,255], so that 
                  // it matches correctly with packet flow ids.
                  Paint *paint = Paint::instance();
                  if (c > nclass_) {
                        int n, i;
                        for (n = nclass_; n < c; n <<= 1);
                        int *p = new int[n];
                        for (i = 0; i < nclass_; ++i)
                              p[i] = paint_[i];
                        delete paint_;
                        paint_ = p;
                        nclass_ = n;
                        paintMask_ = nclass_ - 1;
                        int pno = paint->thin();
                        for (; i < n; ++i)
                              paint_[i] = pno;
                  }
                  int pno = paint->lookup(argv[3], 1);
                  if (pno < 0) {
                    fprintf(stderr,"%s color: no such color: %s\n",
                          argv[0], argv[3]);
                    pno = paint->lookup("black",1);
                    if (pno < 0) {
                      tcl.resultf("%s no black! - bailing");
                      return (TCL_ERROR);
                    }
                  }
                  paint_[c] = pno;
                  oldpaint_[c] = pno;

                  return (TCL_OK);
            }
    
            if (strcmp(argv[1], "ncolor") == 0) {
                  /*
                   * <net> ncolor <node> <colorName>
                   * set color of node to the specified color.
                   */
                  Paint *paint = Paint::instance();
                  Node *n = lookupNode(atoi(argv[2]));
                  if (n == NULL) {
                        fprintf(stderr, "No such node %s\n", argv[2]);
                        exit(1);
                  }
                  int pno = paint->lookup(argv[3], 3);
                  if (pno < 0) {
                    fprintf(stderr,"%s ncolor: no such color: %s\n",
                          argv[0], argv[3]);
                    int pno = paint->lookup("black",1);
                    if (pno < 0) {
                      tcl.resultf("%s no black! - bailing");
                      return (TCL_ERROR);
                    }
                  }
                  n->paint(pno);
                  return (TCL_OK);
            }



      
  // ----- 5 tcl arguments ------
      } else if (argc == 5) {
            if (strcmp(argv[1], "select-pkt") == 0) {
                  selectPkt(atoi(argv[2]), atoi(argv[3]), atoi(argv[4]));
                  return(TCL_OK);
            }


            if (strcmp(argv[1], "lookupColorName") == 0) {
                  /*
                   * <net> lookupColorName red green blue
                   * get color name from its rgb value 
                   */   
                   Paint *paint = Paint::instance();
                   int r = atoi(argv[2]);
                   int g = atoi(argv[3]);
                   int b = atoi(argv[4]);
                   tcl.resultf("%s",paint->lookupName(r,g,b));
                   return(TCL_OK);
            }


            if (strcmp(argv[1], "queue") == 0) {
                  /*
                   * <net> queue <src> <dst> <angle>
                   * Create a queue for the edge from 'src' to 'dst'.
                   * Add it to this NetModel's queue list.
                   * Display the queue at the specified angle from
                   * the edge to which it belongs.
                   */
                  int src = atoi(argv[2]);
                  int dst = atoi(argv[3]);
                  EdgeHashNode *h = lookupEdgeHashNode(src, dst);
                  if (h == 0) {
                        tcl.resultf("%s queue: no such edge (%d,%d)",
                                  argv[0], src, dst);
                        return (TCL_ERROR);
                  }
                  /* XXX can we assume no duplicate queues? */
                  double angle = atof(argv[4]);
                  Edge *e = h->edge;
                  angle += e->angle();
                  Queue *q = new Queue(angle);
                  h->queue = q;
                  q->next_ = queues_;
                  queues_ = q;
                  return (TCL_OK);
            }


            if (strcmp(argv[1], "ecolor") == 0) {
                  /*
                   * <net> ecolor <src> <dst> <colorName>
                   * set color of edge to the specified color.
                   */
                  Paint *paint = Paint::instance();
                  EdgeHashNode* h = lookupEdgeHashNode(atoi(argv[2]), atoi(argv[3]));
                  if (h == 0) {
                        tcl.resultf("%s ecolor: no such edge (%s,%s)",
                                  argv[0], argv[2], argv[3]);
                        return (TCL_ERROR);
                  }
                  int pno = paint->lookup(argv[4], 3);
                  if (pno < 0) {
                    fprintf(stderr,"%s ncolor: no such color: %s\n",
                          argv[0], argv[3]);
                    pno = paint->lookup("black",1);
                    if (pno < 0) {
                      tcl.resultf("%s no black! - bailing");
                      return (TCL_ERROR);
                    }
                  }
                  h->edge->paint(pno);
                  return (TCL_OK);
            }


            if (strcmp(argv[1], "edlabel") == 0) {
                  /*
                   * <net> edlabel <src> <dst> <colorName>
                   * set label of edge.
                   */
                  EdgeHashNode* h = lookupEdgeHashNode(atoi(argv[2]), 
                                         atoi(argv[3]));
                  if (h == 0) {
                        tcl.resultf("%s ecolor: no such edge (%s,%s)",
                                  argv[0], argv[2], argv[3]);
                        return (TCL_ERROR);
                  }
                  if (strcmp(argv[4],"(null)")!=0) {
                        h->edge->dlabel((char *)argv[4]);
                        // XXX Should never set edge size outside
                        // scale_estimate()!!!!
//                      h->edge->size(25.0);
                  }
                  return (TCL_OK);
            }


            if (strcmp(argv[1], "lanlink") == 0) {
                  /*
                   * <net> lanlink <src> <lan> <angle>
                   * Create a link/edge between the specified source
                   * and a lan.
                   */
                  Node * src = lookupNode(atoi(argv[2]));
                  Lan * lan = lookupLan(atoi(argv[3]));

                  if (lan == NULL) {
                        fprintf(stderr, "Error: lan %s does not exist.\n", argv[3]);
                        exit(1);
                  }

                  double angle = atof(argv[4]);
                  double bw = lan->bw();
                  double delay = (lan->delay())/2.0;

                  Edge *e1 = new Edge(src, lan->virtual_node(), packet_size_, bw, delay, 0, angle+1);
                  e1->init_color("black");
                  enterEdge(e1);

                  e1->Animation::insert(&drawables_);
                  src->add_link(e1);

                  Edge *e2 = new Edge(lan->virtual_node(), src, packet_size_, bw, delay, 0, angle);
                  e2->init_color("black");
                  enterEdge(e2);
                  e2->Animation::insert(&drawables_);
                  lan->add_link(e2);
                  return (TCL_OK);
            }


            if (strcmp(argv[1], "set_node_tclscript") == 0) {
                  //------------------------------------------------------------------
                  // $netModel set_node_tclscript $node_id $button_label $tcl_command
                  //   - from tcl/node.tcl
                  //          Animator instproc node_tclscript
                  //------------------------------------------------------------------
                  // Sets the tcl_script for a node to be run when the
                  // start_info exec button is pressed.
                        Node * node = lookupNode(atoi(argv[2]));
                        if (node) {
                              // The following strings are deleted by the node when it 
                              // changes to another script or it is deleted
                              node->setTclScript(argv[3], argv[4]);
                              return TCL_OK;
                        } else {
                              return TCL_ERROR;
                        }
                  }



      } else if (argc == 6) {
            if (strcmp(argv[1], "lan") == 0) {
                  /*
                   * <net> lan <name> <bandwidth> <delay> <angle>
                   * Create a link/edge between the specified source
                   * and destination. Add it to this NetModel's list
                   * of drawables and to the source's list of links.
                   */
                  double bw = atof(argv[3]);
                  double delay = atof(argv[4]);
                  double angle = atof(argv[5]);

                  Lan *l = new Lan(argv[2], this, packet_size_, bw, delay, angle);
                  l->next_ = lans_;
                  lans_ = l;
                  l->insert(&drawables_);

                  Node *n = l->virtual_node();
                  n->next_ = nodes_;

                  nodes_ = n;
                  return (TCL_OK);
            }

      } else if (argc == 8) {
            if (strcmp(argv[1], "link") == 0) {
                  /*
                   * <net> link <src> <dst> <bandwidth> <delay> <angle>
                   * Create a link/edge between the specified source
                   * and destination. Add it to this NetModel's list
                   * of drawables and to the source's list of links.
                   */
                  Edge * edge = addEdge(argc,argv);
                  if (!edge) {
                        tcl.resultf("node %s or %s is not defined... ", argv[2], argv[3]);
                        return TCL_ERROR;
                  } else { 
                        tcl.resultf("%g", edge->delay());
                        return (TCL_OK);
                  }
            }
      }
      /* If no NetModel commands matched, try the Object commands. */
      fprintf(stderr, "No matching command found for %s\n",argv[0]);
      return (TclObject::command(argc, argv));
}


void NetModel::selectPkt(int sData, int sRoute, int sMac) 
{
    showData_ = sData;
    showRouting_ = sRoute;
    showMac_ = sMac;
}



// ---------------------------------------------------------------------
// void
// NetModel::placeEdgeByAngle(Edge* e, Node* src) const {
// Place edges by their angles
// ---------------------------------------------------------------------
void 
NetModel::placeEdgeByAngle(Edge * e, Node * src) const {
  double nsin, ncos, x0, x1, y0, y1;
  Node * destination;
  EdgeHashNode * h;

  if (e->marked() == 0) {
    // get destination node
    destination = e->neighbor();

    SINCOSPI(e->angle(), &nsin, &ncos);

    x0 = src->x(e) + src->size() * ncos * 0.75;
    y0 = src->y(e) + src->size() * nsin * 0.75;
    x1 = destination->x(e) - destination->size() * ncos * 0.75;
    y1 = destination->y(e) - destination->size() * nsin * 0.75;

    e->place(x0, y0, x1, y1);
    
    // Place the queue here too.
    h = lookupEdgeHashNode(e->src(), e->dst());
    if (h->queue != 0) {
      h->queue->place(e->size(), e->x0(), e->y0());
    }

    e->mark();
  }
}

void NetModel::placeEdge(Edge * e, Node * src) const {
      if (e->marked() == 0) {
            double hyp, dx, dy;
            Node * dst = e->neighbor();
            dx = dst->x(e) - src->x(e);
            dy = dst->y(e) - src->y(e);
            hyp = sqrt(dx*dx + dy*dy);
            e->setAngle(atan2(dy,dx));
            double x0 = src->x(e) + src->size() * (dx/hyp) * .75;
            double y0 = src->y(e) + src->size() * (dy/hyp) * .75;
            double x1 = dst->x(e) - dst->size() * (dx/hyp) * .75;
            double y1 = dst->y(e) - dst->size() * (dy/hyp) * .75;
            e->place(x0, y0, x1, y1);
            
            /* Place the queue here too.  */
            EdgeHashNode *h = lookupEdgeHashNode(e->src(), e->dst());
            if (h->queue != 0)
                  h->queue->place(e->size(), e->x0(), e->y0());

            e->mark();
      }
}

//----------------------------------------------------------------------
// void
// NetModel::placeAgent(Agent *a, Node *src) const
//----------------------------------------------------------------------
void 
NetModel::placeAgent(Agent *a, Node *src) const {
  double x0, y0, nsin, ncos;
  Agent * agents;
  Edge * links;
  int choices[8], i, ix;
  double angle;
  TrafficSource * traffic_source;

  if (a->marked() == 0) {
    if (a->angle()==NO_ANGLE) {
      if (a->edge()==NULL) {
        /* determine where to put the label so it
           won't overlap a link (if possible) */
        for(i = 0; i < 8; i++) {
          choices[i] = 1;
        }

        // Get list of agents attached to the src node
        agents = src->agents();

        // Loop through list of agents checking their locations
        while (agents != NULL) {
          angle = agents->angle();
          if (angle < 0) {
            angle = angle+2*M_PI;
          }
          ix = int(angle*4);
          if (ix < 8) {
            choices[ix] = 0;
          }
          agents=agents->next_;
        }

        // Get a list of links attached to the src node
        links = src->links();

        // Loop through the list checking their locations
        while (links!=NULL) {
          angle = links->angle();
          if (angle < 0) {
            angle = angle + 2*M_PI;
          }
          ix = int(angle * 4);
          if (ix < 8) {
            choices[ix] = 0;
          }
          links=links->next_;
        }

        // Choose an angle from the marked choices list
        if (choices[0] == 1) {
          a->angle(0);
        } else if (choices[4] == 1) {
          a->angle(1.0);
        } else if ((choices[1] == 1) && (choices[2] == 1)) {
          a->angle(0.5);
        } else if ((choices[6]==1)&&(choices[7]==1)) {
          a->angle(1.5);
        } else if (choices[1]==1) {
          a->angle(0.25);
        } else {
          a->angle(1.75);
        }
      } else {
        a->angle(a->edge()->angle() + 0.25);
      }
    }

    SINCOSPI(a->angle(), &nsin, &ncos);
    x0 = src->x() + src->size() * ncos * .75;
    y0 = src->y() + src->size() * nsin * .75;
    a->place(x0, y0);
    a->mark(1);

    // Place any traffic sources that are connected to this agent
    for (traffic_source = a->traffic_sources_;
         traffic_source;
         traffic_source = traffic_source->next_) {
      traffic_source->place();
    }
    
  }
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
void
NetModel::hideAgentLinks() {
      for (Node* n = nodes_; n != 0; n = n->next_) 
            for(Agent* a= n->agents(); a != 0; a = a->next_) 
                  a->hideLink();
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
void NetModel::set_wireless() {
      wireless_ = 1 ;
      for (Node *n = nodes_; n != 0; n = n->next_)
            for (Edge *e=n->links(); e != 0; e = e->next_)
                  e->unmark();
      scale_estimate();
      placeEverything();
      for (View *p = views_; p != 0; p = p->next_)
            if ((p->width() > 0) && (p->height() > 0)) {
                  p->redrawModel();
                  p->draw();
            }
}

//----------------------------------------------------------------------
// void
// NetModel::scale_estimate() {
//   - Compute reasonable defaults for missing node or edge sizes
//     based on the maximum link delay.
//----------------------------------------------------------------------
void
NetModel::scale_estimate() {
  /* Determine the maximum link delay. */
  // XXX Must use length(). This is essential for appropriate 
  // packet height when using a pre-made layout.
  double emax = 0.;
  Node *n;
  for (n = nodes_; n != 0; n = n->next_) {
    for (Edge* e = n->links(); e != 0; e = e->next_)
      if (e->length() > emax)
        emax = e->length();
  }

  /*store this because we need it for monitors*/
  node_size_ = node_sizefac_ * emax;

  /*
   * Check for missing node or edge sizes. If any are found,
   * compute a reasonable default based on the maximum edge
   * dimension.
   */
  for (n = nodes_; n != 0; n = n->next_) {
    if (n->size() <= 0.)
      n->size(node_size_);

    for (Edge* e = n->links(); e != 0; e = e->next_)
      if (e->size() <= 0.)
        e->size(.03 * emax);
  }
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
void NetModel::placeEverything() {
  // If there is no fixed node, anchor the first one entered at (0,0).
  Node *n;
  Edge * e;
  int nodes_to_be_placed;

  for (n = nodes_; n != 0; n = n->next_) {
    n->mark(0);
    n->clear_routes();
  }

  if (nodes_) {
    //nodes_->place(0., 0.);
    nodes_->place(nodes_->x(), nodes_->y());
  }

  do {
    nodes_to_be_placed = 0;
    for (n = nodes_; n != 0; n = n->next_) {
      nodes_to_be_placed |= traverseNodeConnections(n);
    }
  } while (nodes_to_be_placed);

  // Place edges
  for (n = nodes_; n != 0; n = n->next_) { 
    for (e = n->links(); e != 0; e = e->next_) {
      placeEdgeByAngle(e, n);
    }
  }

  // After edges are laied out, place all routes.
  for (n = nodes_; n != 0; n = n->next_) {
    n->place_all_routes();
  }
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
void NetModel::move(double& x, double& y, double angle, double d) const {
      double ncos, nsin;
      SINCOSPI(angle, &nsin, &ncos);
      x += d * ncos;
      y += d * nsin;
}

//----------------------------------------------------------------------
// int NetModel::traverseNodeConnections(Node* n)
//   - Traverse node n's neighbors and place them based on the
//     delay of their links to n.  The two branches of the if..else
//     are to handle unidirectional links -- we place ourselves if
//     we haven't been placed & our downstream neighbor has.
//----------------------------------------------------------------------
int NetModel::traverseNodeConnections(Node* n) {
  double x, y, distance;
  int placed_node = 0;

  for (Edge* e = n->links(); e != 0; e = e->next_) {
    Node *neighbor = e->neighbor();
    distance = e->length() + (n->size() + neighbor->size()) * 0.75;

    if (n->marked() && !neighbor->marked()) {
      x = n->x(e);
      y = n->y(e);
      move(x, y, e->angle(), distance);
      neighbor->place(x, y);

      placed_node |= traverseNodeConnections(neighbor);

      if (nymax_ < y)
        nymax_ = y;
      if (nymin_ > y)
        nymin_ = y;

    } else if (!n->marked() && neighbor->marked()) {
      x = neighbor->x(e);
      y = neighbor->y(e);
      move(x, y, e->angle(), -distance);
      n->place(x, y);
      placed_node = 1;
    }
  }
  return placed_node;
}

int NetModel::save_layout(const char *filename)
{
  FILE *file;
  Node *n;
  Edge *e;
  int ret;
  file=fopen(filename, "w");
  if (file==0)
  {
    fprintf(stderr, "nam: Couldn't open file: %s\n", filename);
    return -1;
  }
  for (n = nodes_; n != 0; n = n->next_)
  {
    ret = n->save(file);
    if (ret!=0) {
      fclose(file);
      return -1;
    }
  }
  for (n = nodes_; n != 0; n = n->next_)
    for(e= n->links(); e !=0; e = e->next_)
    {
      ret = e->save(file);
      if (ret!=0) {
      fclose(file);
      return -1;
      }
    }
  return(fclose(file));
}

void NetModel::color_subtrees()
{
  Node *n, *dst, *prevdst, *newdst;
  Edge* e;
  for (n = nodes_; n != 0; n = n->next_) {
    int ctr=0;
    for (e = n->links(); e != 0; e = e->next_)
      ctr++;
    if (ctr==1)
    {
      n->color("grey");
      n->links()->color("grey");
      dst=n->links()->neighbor();
      for (e = dst->links(); e != 0; e = e->next_)
      if (e->neighbor()==n)
      {
        e->color("grey");
        break;
      }
      ctr=2;
      prevdst=n;
      while(ctr==2) {
      ctr=0;
      for (e = dst->links(); e != 0; e = e->next_)
        ctr++;
      if (ctr==2) {
        dst->color("grey");
        dst->links()->color("grey");
        for (e = dst->links(); e != 0; e = e->next_)
          if (e->neighbor()!=prevdst)
          {
            newdst=e->neighbor();
            break;
          }
        e->color("grey");
        for (e = newdst->links(); e != 0; e = e->next_)
          if (e->neighbor()==dst)
          {
            e->color("grey");
            break;
          }
        prevdst=dst;
        dst=newdst;
      }
      }
    }
  }
}


//----------------------------------------------------------------------
// int
// NetModel::add_tag(Tag *tag)
//----------------------------------------------------------------------
int
NetModel::add_tag(Tag *tag) {
  int newEntry = 1;
  Tcl_HashEntry * he;
  he = Tcl_CreateHashEntry(tagHash_, (const char *)tag->name(), &newEntry);

  if (he == NULL) {
    return (TCL_ERROR);
  }

  if (newEntry) {
    Tcl_SetHashValue(he, (ClientData)tag);
    nTag_++;
  }

  tag->insert(&drawables_);

  return (TCL_OK);
}

// Remove a tag from netmodel, and clear all its memberships but not 
// actually delete it
void NetModel::delete_tag(const char *tn)
{
      Tcl_HashEntry *he = 
            Tcl_FindHashEntry(tagHash_, (const char *)tn);
      if (he != NULL) {
            // Do *NOT* delete tag here.
            Tcl_DeleteHashEntry(he);
            nTag_--;
      }
                          
}

Tag* NetModel::lookupTag(const char *tn)
{
      Tcl_HashEntry *he = Tcl_FindHashEntry(tagHash_, tn);
      if (he == NULL)
            return NULL;
      return (Tag *)Tcl_GetHashValue(he);
}

int NetModel::registerObjName(const char *name, int id)
{
      int newEntry = 1;
      Tcl_HashEntry *he = 
            Tcl_CreateHashEntry(objnameHash_, name, &newEntry);
      if (he == NULL)
            return (TCL_ERROR);
      if (newEntry) {
            Tcl_SetHashValue(he, (ClientData)id);
            nTag_++;
      }
      return (TCL_OK);
}

//XXX: maximum name length is 256. 
int NetModel::lookupObjname(const char *name)
{
#define STATIC_NAMELEN 256
      char n[STATIC_NAMELEN];
      size_t len = strlen(name);
      len = (len < STATIC_NAMELEN) ? len : STATIC_NAMELEN;
      for (size_t i = 0; i < len; i++)
            n[i] = toupper(name[i]);

      Tcl_HashEntry *he = Tcl_FindHashEntry(objnameHash_, n);
      if (he == NULL)
            return -1;
      return *Tcl_GetHashValue(he);
#undef STATIC_NAMELEN
}

/*
 * Unite tag name space and animation id space:
 * 
 * (1) if tag is a number, we'll first look it up in our 
 *     animation objects table. 
 * (2) if tag is a string, it must be in the tag table
 */
Animation* NetModel::lookupTagOrID(const char *name)
{
      char end[256];
      if (name == NULL)
            return NULL;
      unsigned int id = (unsigned int)strtoul(name, (char **)&end, 0);
      if (*end != 0)
            // This must be a tag string name
            return lookupTag(name);
      else 
            return Animation::find(id);
}

/*
 * Handling Tcl command "addtags" of EditView
 *
 * <view> addtag tag <searchSpec> arg...
 *
 * searchSpec can be: 
 *   all <ObjectType>
 *   closest x y ?halo? 
 *   enclosed x1 y1 x2 y2
 *   overlapping x1 y1 x2 y2
 *   withTag tagOrID
 * specification copied from Tk's canvas
 *
 * If newTag isn't null, then first lookup the tag. If found, add
 * the searchSpec results to that tag, otherwise create a new tag
 * and add results to that tag.
 * Return error if newTag == NULL.
 *
 * Tagging policies, e.g., whether an object can simultaneously belong 
 * to multiple tags, are left to the users.
 */
int NetModel::tagCmd(View *v, int argc, char **argv, 
                 char *newTag, char *cmdName)
{
      Tag *tag = NULL;
      size_t length;
      Tcl& tcl = Tcl::instance();
      int res = (TCL_OK), bNew = 0;

      if (newTag != NULL) {
            tag = (Tag *)lookupTagOrID(newTag);
            if ((tag != NULL) && (tag->classid() != ClassTagID)) {
                  Tcl_AppendResult(tcl.interp(),
                               newTag, "should be a tag ",
                               (char *)NULL);
                  return TCL_ERROR;
            }
            if (tag == NULL) {
                  tag = new Tag(newTag);
                  bNew = 1;
            }
      }

      int c = argv[0][0];
      length = strlen(argv[0]);

      if ((c == 'a') && (strncmp(argv[0], "all", length) == 0)
          && (length >= 2)) {
            if (argc > 2) {
                  Tcl_AppendResult(tcl.interp(), 
                               "wrong # args: should be \"", cmdName,
                               " all ?Obj?", (char *) NULL);
                  res = TCL_ERROR;
                  goto error;
            }
            Animation *a;
            int objType = ClassAllID;
            if (argc == 2) {
                  // Map object name to class id
                  objType = lookupObjname(argv[1]);
                  if (objType == -1) {
                        Tcl_AppendResult(tcl.interp(),
                                     "Bad object name ", argv[1]);
                        res = (TCL_ERROR);
                        goto error;
                  }
            }
            if (objType == ClassAllID) {
                  for (a = animations_; a != NULL; a = a->next())
                        if (!a->isTagged())
                              tagObject(tag, a);
                  for (a = drawables_; a != NULL; a = a->next())
                        if (!a->isTagged())
                              tagObject(tag, a);
            } else {
                  for (a = animations_; a != NULL; a = a->next())
                        if (!a->isTagged() && a->classid() == objType)
                              tagObject(tag, a);
                  for (a = drawables_; a != NULL; a = a->next())
                        if (!a->isTagged() && a->classid() == objType)
                              tagObject(tag, a);
            }

      } else if ((c == 'e') && (strncmp(argv[0], "enclosed", length) == 0)) {
            /* 
             * enclosed x1 y1 x2 y2
             */
            if (argc != 5) {
                  Tcl_AppendResult(tcl.interp(), 
                               "wrong # args: should be \"",
                               cmdName, " enclosed x1 y1 x2 y2", 
                               (char *) NULL);
                  res = TCL_ERROR;
                  goto error;
            }
            BBox bb;
            // Translation/scaling will not change the order of bbox. :)
            if ((v->getCoord(argv[1], argv[2],
                         bb.xmin, bb.ymin) != TCL_OK) ||
                (v->getCoord(argv[3], argv[4], bb.xmax, bb.ymax)!=TCL_OK))
                  res = TCL_ERROR;
            else 
                  res = tagArea(bb, tag, 1);

      } else if ((c == 'o') && 
               (strncmp(argv[0], "overlapping", length) == 0)) {
            /*
             * Overlapping x1 y1 x2 y2 
             */
            if (argc != 5) {
                  Tcl_AppendResult(tcl.interp(), 
                               "wrong # args: should be \"",
                               cmdName, " overlapping x1 y1 x2 y2",
                               (char *) NULL);
                  res = TCL_ERROR;
                  goto error;
            }
            BBox bb;
            // Translation/scaling will not change the order of bbox. :)
            if ((v->getCoord(argv[1], argv[2],
                         bb.xmin, bb.ymin) != TCL_OK) ||
                (v->getCoord(argv[3], argv[4], bb.xmax, bb.ymax)!=TCL_OK))
                  res = TCL_ERROR;
            else 
                  res = tagArea(bb, tag, 0);

      } else if ((c == 'w') && (strncmp(argv[0], "withtag", length) == 0)) {
            if (argc != 2) {
                  Tcl_AppendResult(tcl.interp(), 
                               "wrong # args: should be \"",
                               cmdName, " withtag tagOrId", 
                               (char *) NULL);
                  res = TCL_ERROR;
                  goto error;
            }
            // Find that tag or ID
            Animation *p = lookupTagOrID(argv[1]);
            if (p == NULL) {
                  // Wrong tag
                  Tcl_AppendResult(tcl.interp(),
                               "wrong tag ", argv[1],
                               (char *)NULL);
                  res = TCL_ERROR;
            } else {
                  // Label them as this tag
                  tagObject(tag, p);
            }

      } else if ((c == 'c') && (strncmp(argv[0], "closest", length) == 0)) {
            float coords[2], halo;
            if ((argc < 3) || (argc > 5)) {
                  Tcl_AppendResult(tcl.interp(), 
                               "wrong # args: should be \"",
                               cmdName, " closest x y ?halo? ",
                               (char *) NULL);
                  res = TCL_ERROR;
                  goto error;
            }
            if (v->getCoord(argv[1], argv[2], 
                        coords[0], coords[1]) != TCL_OK) {
                  Tcl_AppendResult(tcl.interp(),
                               "bad coordinates ",
                               argv[1], " ", argv[2], (char *)NULL);
                  res = TCL_ERROR;
                  goto error;
            }
            if (argc > 3) {
                  // XXX: It's impossible to convert length in 
                  // screen space to world space with translation
                  // and scaling. We think it's in world space
                  halo = strtod(argv[3], NULL);
                  if (halo < 0.0) {
                        Tcl_AppendResult(tcl.interp(), 
                               "can't have negative halo value \"",
                                     argv[3], "\"", (char *) NULL);
                        res = TCL_ERROR;
                        goto error;
                  }
            } else {
                  halo = 0.0;
            }
            Animation *p = findClosest(coords[0], coords[1], halo);
            if (p != NULL)
                  tagObject(tag, p);
            else 
                  res = TCL_ERROR;

      } else  {
            Tcl_AppendResult(tcl.interp(), 
                         "bad search command \"", argv[0],
                   "\": must be above, all, below, closest, enclosed, ",
                         "overlapping, or withtag", (char *) NULL);
            res = TCL_ERROR;
            goto error;
      }

      if (res == TCL_OK) {
            if (bNew)
                  add_tag(tag);
            return TCL_OK;
      }
error:
      if (tag != NULL)
            delete tag;
      return res;
}

Animation* NetModel::findClosest(float dx, float dy, double halo)
{
      double closestDist;
      Animation *startPtr, *closestPtr, *itemPtr;
      BBox bb;

      startPtr = animations_;
      itemPtr = startPtr;
      if (itemPtr == NULL) {
            return NULL;
      }
      closestDist = itemPtr->distance(dx, dy) - halo;

      if (closestDist < 0.0) {
            closestDist = 0.0;
      }
      while (1) {
            double newDist;
            /*
             * Update the bounding box using itemPtr, which is the
             * new closest item.
             */
            bb.xmin = dx - closestDist - halo - 1,
            bb.ymin = dy - closestDist - halo - 1,
            bb.xmax = dx + closestDist + halo + 1,
            bb.ymax = dy + closestDist + halo + 1;
            closestPtr = itemPtr;
            /*
             * Search for an item that beats the current closest 
             * one.
             * Work circularly through the canvas's item list until
             * getting back to the starting item.
             */
            while (1) {
                  itemPtr = itemPtr->next();
                  if (itemPtr == NULL) {
                        if (startPtr == animations_) {
                              startPtr = drawables_;
                              itemPtr = startPtr;
                        } else 
                              return closestPtr;
                  }
                  if (itemPtr->isTagged()|| !itemPtr->bbox().overlap(bb))
                        continue;
                  newDist = itemPtr->distance(dx, dy)-halo;
                  if (newDist < 0.0) {
                        newDist = 0.0;
                  }
                  if (newDist <= closestDist) {
                        closestDist = newDist;
                        break;
                  }
            }
      }
}

//----------------------------------------------------------------------
// void
// NetModel::tagObject(Tag *tag, Animation * animation_object)
//----------------------------------------------------------------------
void
NetModel::tagObject(Tag *tag, Animation * animation_object) {
      Tcl & tcl = Tcl::instance();

      if (tag == NULL) {
            char str[20];
            sprintf(str, "No tag for %d", animation_object->id());
            Tcl_AppendElement(tcl.interp(), str);
            return;
      }

      if (tag == animation_object)
            return;

      // add animation object to the tag group 
      tag->add(animation_object);
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
int NetModel::tagArea(BBox &bb, Tag *tag, int bEnclosed)
{
      Animation *p;

      // Because the area is a rectangle, we only need to 
      // find out all objects whose bounding boxes are 
      // within/overlapping the given rectangle.
      if (bEnclosed == 0) {
            // overlapping and enclosed
            for (p = animations_; p != NULL; p = p->next()) 
                  if (!p->isTagged() && p->bbox().overlap(bb) 
                      && (p != tag))
                        tagObject(tag, p);
            for (p = drawables_; p != NULL; p = p->next())
                  if (!p->isTagged() && p->bbox().overlap(bb)
                      && (p != tag))
                        tagObject(tag, p);
      } else {
            // enclosed only
            for (p = animations_; p != NULL; p = p->next()) 
                  if (!p->isTagged() && bb.inside(p->bbox())
                      && (p != tag))
                        tagObject(tag, p);
            for (p = drawables_; p != NULL; p = p->next())
                  if (!p->isTagged() && bb.inside(p->bbox())
                      && (p != tag))
                        tagObject(tag, p);
      }
      return TCL_OK;
}

int NetModel::deleteTagCmd(char *tagName, char *tagDel)
{
      Tag *p;
      Animation *q = NULL;

      p = (Tag *)lookupTagOrID(tagName);
      if (tagDel != NULL)
            q = lookupTagOrID(tagDel);

      if (p == NULL) {
            Tcl_AppendResult(Tcl::instance().interp(),
                         "bad tag ", tagName, (char *)NULL);
            return (TCL_ERROR);
      }
      if ((p->classid() != ClassTagID) || (p == q) || (q == NULL)){
            // If given an ID, or given a group only
            // delete it and do nothing else
            delete p;
            return TCL_OK; 
      }
      // Now delete tag p from object q
      q->deleteTag(p);
      return TCL_OK;
}

// ---------------------------------------------------------------------
// void
// NetModel::render(EditView* view, BBox &bb)
//   - This is a partial render which does not seem to be complete.
//     Above here are several other render functions which redisplay 
//     the entire model.
//
// XXX: Partial redraw is only working for EditView! Not for other
// animation views for now. We need a method in each Animation object
// to decide their "dirtiness" to make this redraw work. :( And the
// computation of clipping box should be purely within NetModel but
// not fed in externally
// ---------------------------------------------------------------------
void
NetModel::render(EditView* view, BBox &bb) {
  Animation *a;

  for (a = drawables_; a != 0; a = a->next()) {
    if (!a->bbox().overlap(bb)) {
      // if outside of our views bounding box then skip 
      // this animation object
      continue;
    }
    a->draw(view, now_);
  }

  for (a = animations_; a != 0; a = a->next()) {
    if (!a->bbox().overlap(bb)) {
      // if outside of our views bounding box then skip 
      // this animation object
      continue;
    }
    a->draw(view, now_);
  }
}

void NetModel::moveNode(Node *n) {
      //fprintf(stderr, "Moving nodes can only be performed by NetModel.\n");
      for (Edge *e = n->links(); e != 0; e = e->next_) {
            e->unmark();
            placeEdge(e, n);
            Node *dst = e->neighbor();
            // Should place reverse edges too
            Edge *p = dst->find_edge(n->num());
            if (p != NULL) {
                  p->unmark();
                  placeEdge(p, dst);
            }
            dst->clear_routes();
            dst->place_all_routes();
      }
      for (Agent *a = n->agents(); a != NULL; a = a->next()) {
            a->mark(0), a->angle(NO_ANGLE);
            placeAgent(a, n);
      }
      // Relayout all routes
      n->clear_routes();
      n->place_all_routes();
}


Generated by  Doxygen 1.6.0   Back to index