/* -*- c-basic-offset: 3 -*-
 *
 * PANDORE (PANTHEON Project)
 *
 * GREYC IMAGE
 * 6 Boulevard Maréchal Juin
 * F-14050 Caen Cedex France
 *
 * This file is free software. You can use it, distribute it
 * and/or modify it. However, the entire risk to the quality
 * and performance of this program is with you.
 *
 * Please email any bugs, comments, and/or additions to this file to:
 * pandore@greyc.ensicaen.fr
 * http://www.greyc.ensicaen.fr/EquipeImage/Pandore/
 */

/**
 * @author Regis Clouard - 1997-10-27
 * @author François Angot - 1999-10-11
 * @author Alexandre Duret-Lutz - 1999-10-11
 * @author Régis Clouard - 2001-04-11 (version 3.00)
 * @author Régis Clouard - 2002-12-09 (version 4.00)
 */

#include <pandore.h>
using namespace pandore;

/**
 * @file graph.cpp
 *
 */

/*
 * Adds a the node `n' in the list of adjacent nodes.
 * -> New dge with weight `w'.
 */
template< class Point >
GEdge *GNode< Point >::Add( Long n, Float w ) {
   return ( adjacents=new GEdge( n, adjacents, w ) );
}

/*
 * Deletes n from the list of adjacents nodes.
 * Del does not really delete GEdge because some
 * loop variables may point to the neighbours()
 * list (ex: for (p=Neighbours(); p!=NULL; p=p->next) ...
 * @param n the node to be deleted.
 * @return	the new list of adjencent nodes.
 */
template< class Point >
GEdge *GNode< Point >::Del( Long n ) {
   GEdge *p = Neighbours();
   GEdge *q;
   
   if (!p) return NULL;
   
   if (p->Node()==n){
      q=p->Next();
      // delete(p); // Problem when using Neighbours() list: cannot delete
      adjacents=q;
   }else{
      for (q=p->Next();q && (q->Node() !=n); p=q,q=q->Next());
      if (q){
	 p->Next(q->Next());
	 // delete(q); // Problem when using Neighbours() list: cannot delete
      }
   }
   return adjacents;
}

/*
 * If the node `n' is in the adjacent list
 * returns its address.
 * @param n the node number.
 */
template< class Point >
GEdge *GNode< Point >::Search( Long n ) const {
   GEdge *p;
   for (p=adjacents; p && (p->Node()!=n); p=p->Next());
   return p;
}

/*
 * Node destructor.
 * -> Destroy the list of adjacent node,
 * without worrying about linked nodes.
 */
template< class Point >
GNode< Point >::~GNode( ) {
   GEdge *q,*p=adjacents;

   while ((q=p)){
      p=p->Next();
      delete q;
   }
}

/*
 * GRAPH 2D
 */

/*
 * Initializor.
 * Need a value for tnode.
 */
void Graph2d::New( Long s, Long nr, Long nc ) {
   if (tnode)
      Delete();

   ncol=nc;nrow=nr;
   if ((size=s)>0){
      tnode=new GNode< Point2d >*[s];
      for (--s;s>=0;tnode[s--]=NULL);
   }
}

/*
 * Destructor.
 * Delete all nodes.
 */
void Graph2d::Delete( ) {
   if (size>0 && tnode){
      for (int i=0;i<size;i++){
	 if (tnode[i])
	    delete tnode[i];
      }
      delete [] tnode;
   }
   tnode=NULL;
}

/*
 * Adds a node in graph
 * at position `node' in graph array,
 * with item=`item',
 * and seed coordinates=`p'.
 */
Errc Graph2d::Add( Long node, Long item, const Point2d &p ) {
   if (node<size && node>=0){
      tnode[node] = new GNode<Point2d>(item,p);
      return SUCCESS;
   }
   return FAILURE;
}

/*
 * Removes the node `n' in graph.
 * -> Remove all refences in its
 * neighbourhood nodes.
 */
Errc Graph2d::Del( Long n ) {
   GEdge *p;
   
   if (!tnode[n])
      return FAILURE;
   
   for (p=tnode[n]->Neighbours(); p!=NULL;p=p->Next()){
      tnode[p->Node()]->Del(n);
   }
   delete tnode[n];
   tnode[n]=NULL;

   return SUCCESS;
}

/**
 * Sets the values of the graph from the
 * specified graph values.
 * @param src	the reference graph.
 */
Graph2d &Graph2d::operator=( const Graph2d &g ) {
   Long	i;
   GEdge *l;
   
   New(g.size,g.nrow,g.ncol);
   for (i=0; i<size; i++){
      if (g[i] != NULL){
	 Add(i,g[i]->Item(),g[i]->seed);
	 tnode[i]->value=g[i]->value;
	 for (l=g[i]->Neighbours(); l!=NULL; l=l->Next()){
	    if (i>l->Node())
	       Link(i,l->Node(),l->weight);
	 }
      }
   }
   return *this;
}

/*
 * Creates a copy.
 */
Pobject *Graph2d::Clone( ) const {
   Graph2d *tmp = new Graph2d(size,nrow,ncol);
   *tmp=*this;
   return tmp;
}

/*
 * Builds a graph from a region map.
 * Do not use the region 0.
 * Use the 2nd region map to set seed cordinates.
 */
Errc Graph2d::Init( const Reg2d &rgs, const Reg2d &grm ) {
   int x,y;

   if (Init(rgs)!=SUCCESS)
      return FAILURE;

   for (y=1; y<rgs.Height()-1; y++)
      for (x=1; x<rgs.Width()-1; x++) {
	 if ((grm[y][x])){
	    tnode[rgs[y][x]]->seed.y=y;
	    tnode[rgs[y][x]]->seed.x=x;
	 }
      }
   return SUCCESS;
}

/*
 * Builds a graph from a region map.
 * Do not use the region 0.
 * Seed on left upper coordinate.
 */
Errc Graph2d::Init( const Reg2d &rgs ) {
   register int x,y;
   Ulong a,b;
   int v;
   
   // label + 1 to add region 0.
   New(rgs.Labels()+1,rgs.Height(),rgs.Width());
   
   for (Ulong i=1; i<=rgs.Labels(); i++)
      if (!Add(i,i))
	 return FAILURE;

   for (x=0; x<rgs.Width(); x++){
      if (!(a=rgs[0][x]))
	 if (tnode[a]->seed.x==0) tnode[a]->seed.x=x;
      if (!(a=rgs[rgs.Height()-1][x]))
	 if (tnode[a]->seed.x==0) tnode[a]->seed.x=x;
   }
   for (y=0; y<rgs.Height(); y++){
      if (!(a=rgs[y][0]))
	 if (tnode[a]->seed.x==0) tnode[a]->seed.y=y;
      if (!(a=rgs[y][rgs.Width()-1]))
	 if (tnode[a]->seed.x==0) tnode[a]->seed.y=y;
   }
   
   for (y=1; y<rgs.Height()-1; y++)
      for (x=1; x<rgs.Width()-1; x++) {
	 if (!(a=rgs[y][x])) continue;
	 if (tnode[a]->seed.x==0) tnode[a]->seed.x=x;
	 if (tnode[a]->seed.y==0) tnode[a]->seed.y=y;
	 for (v=0; v<8; v++) { 	 // Look for 8-connected regions.
	    if (((b=rgs[y+v8y[v]][x+v8x[v]])!=0) && (b!=a)){
	       Link(a,b,1);
	    }
	 }
      }
   return SUCCESS;
}


/*
 * Creates a new edge between n1 and n2 and between n2 and n1.
 * If the link already exists,
 * just updates the weight value.
 * @param add if false set the value, if true add the value.
 */
Errc Graph2d::Link( Long n1, Long n2, Float w, bool add ) {
   GEdge *p;
   
   // New edge between n1 and n2.
   if (!(p=tnode[n1]->Search(n2)))
      tnode[n1]->Add(n2,w);
   else
      if (add) p->weight+=w; else p->weight=w;
   
   // New edge between n2 and n1
   if (!(p=tnode[n2]->Search(n1)))
      tnode[n2]->Add(n1,w);
   else
      if (add) p->weight+=w; else p->weight=w;
   return SUCCESS;
}

/*
 * Removes the edge between n1 and n2 (and vice versa).
 */
Errc Graph2d::Unlink( Long n1, Long n2 ) {
   tnode[n1]->Del(n2);
   tnode[n2]->Del(n1);
   return SUCCESS;
}


/*
 * Merges 2 nodes in 1 ->n1
 * Updates the adjacent list of all node (n2 ->n1).
 * and delete n2.
 */
Errc Graph2d::Merge( Long n1, Long n2 ) {
   GEdge *ptr;

   if ((n1==n2) ||  (!tnode[n1]) || (!tnode[n2])) return FAILURE;

   Unlink(n1,n2);

   while((ptr=tnode[n2]->Neighbours())){
      Link(n1,ptr->Node(),ptr->weight,true);
      Unlink(n2,ptr->Node());
   }
   Del(n2);

   return SUCCESS;
}

/*
 * Splits the node n1 in n2.
 * Updates all adjacent lists.
 */
Errc Graph2d::Split( Long n1, Long n2 ) {
   Add(n2,tnode[n1]->Item(),tnode[n1]->seed);
   for (GEdge *ptr=tnode[n1]->Neighbours();ptr;ptr->Next())
      Link(n2,ptr->Node());
   return SUCCESS;
}

/*      
 * Loads graph atributes from the file.
 */
Errc Graph2d::LoadAttributes( FILE *df ) {
   Long attr[3];

   if (Fdecode(attr,sizeof(Long),3,df) < 3)
      return FAILURE;
   New(attr[0],attr[1],attr[2]);
   return SUCCESS;
}

/*
 * Saves graph attributes into the file.
 */
Errc Graph2d::SaveAttributes( FILE *df ) const {
   Long attr[3];
   attr[0]=size; attr[1]=nrow;attr[2]=ncol;
   if (Fencode(attr,sizeof(Long),3,df) < 3)
      return FAILURE;
   return SUCCESS;
}

/*
 * Loads a graph from a file.
 */
Errc Graph2d::LoadData( FILE *df ) {
   register int i;
   Long suiv;
   Graph2d::ValueType val;
   Graph2d::ValueType wgh;
   Point2d p;

   for (i=0;i<size;i++) {
      if (Fdecode(&suiv,sizeof(suiv),1,df) < 1)
	 return FAILURE;
      if (suiv >= 0) {
	 if (Fdecode(&val,sizeof(val),1,df)<1)
	    return FAILURE;
	 if (p.Load(df,_inversionMode) == FAILURE)
	    return FAILURE;
	 if (!Add(i,suiv,p)) return FAILURE;
	 tnode[i]->value=val;
	 
	 if (Fdecode(&suiv,sizeof(suiv),1,df)<1)
	    return FAILURE;
	 while (suiv>-1){
	    if (Fdecode(&wgh,sizeof(wgh),1,df)<1)
	       return FAILURE;
	    if (i>suiv)	// Link with former nodes.
	       Link(i,suiv,wgh);
	    if (Fdecode(&suiv,sizeof(suiv),1,df)<1)
	       return FAILURE;
	 }
      } else tnode[i]=NULL;
   }
   
   return SUCCESS;
}

/*
 * Saves graph data into the file.
 */
Errc Graph2d::SaveData( FILE *df ) const {
   register int i;
   register GEdge *v;
   Long fin=-1;
   Long suiv;

   for (i=0;i<size;i++){
      if (tnode[i]==NULL){
	 if (Fencode(&fin,sizeof(fin),1,df)<1)
	    return FAILURE;
      } else {
	 suiv=tnode[i]->Item();
	 if (Fencode(&suiv,sizeof(suiv),1,df)<1)
	    return FAILURE;
	 if (Fencode(&(tnode[i]->value),sizeof(tnode[i]->value),1,df)<1)
	    return FAILURE;
	 if (tnode[i]->seed.Save(df) == FAILURE)
	    return FAILURE;
	 for (v=tnode[i]->Neighbours();v!=NULL;v=v->Next()){
	    suiv=v->Node();
	    if (Fencode(&suiv,sizeof(suiv),1,df)<1)
	       return FAILURE;
	    if (Fencode(&(v->weight),sizeof(v->weight),1,df)<1)
	       return FAILURE;
	 }
	 if (Fencode(&fin,sizeof(fin),1,df)<1)
	    return FAILURE;
      }
   }

   return SUCCESS;
}

/*
 * Creates a new graph only with the unmasked nodes by `mask'.
 */
Pobject *Graph2d::Mask( const Pobject *mask ) {
   if ((!mask)||(mask->Type()!=Po_Reg2d)||(((Reg2d*)mask)->Size()!=ImageSize())){
      std::cerr << "Warning: bad mask format... ignored" << std::endl;
      return this;
   }

   Graph2d *objd = (Graph2d*)Clone();
   Reg2d *m=(Reg2d*)mask;
   bool *regions=(bool*)calloc(m->Labels()+1,sizeof(bool));
   Ulong *pm=m->Vector();
   
   for (int i=0; i<nrow*ncol; i++, pm++)
      regions[*pm]=true; // List of masked/unmasked regions.

   for (int g=1;g<objd->Size();g++)
      if (!regions[g]) // Delete masked nodes.
	 objd->Del(g);

   delete [] regions;
   return objd;
}

/*
 * Creates the output igraph with the previous masked node 
 * and the new nodes.
 */
Pobject *Graph2d::UnMask( const Pobject *mask, const Pobject *gri ) {
   if ((!mask)||(mask->Type()!=Po_Reg2d)||(((Reg2d*)mask)->Size()!=ImageSize())||(gri->Type() != Type())||(((Graph2d*)gri)->ImageSize()!=ImageSize())){
      std::cerr << "Warning: bad unmask format... ignored" << std::endl;
      return this;
   }
   if ((mask == NULL) || (mask->Type() != Po_Reg2d) || (gri->Type() != Type())){
      return this;
   }

   Graph2d *objs = (Graph2d*)gri;
   Graph2d *grs = (Graph2d*)this;
   Graph2d *objd = (Graph2d*)gri->Clone();
   Reg2d *m=(Reg2d*)mask;
   bool *regions=(bool*)calloc(m->Labels()+1,sizeof(bool));
   Ulong *pm=m->Vector();
   
   for (int i=0; i<nrow*ncol; i++, pm++)
      regions[*pm]=true; // List of masked/unmasked regions.

   // Add or remove node from the input graph with processed graph.
   for (int g=1;g<objd->Size();g++){
      if ( !((*objs)[g]) && (*grs)[g]){  // Added node
	 objd->Add(g,(*grs)[g]->Item(),(*grs)[g]->seed);
	 for (GEdge *ptr=(*grs)[g]-> Neighbours(); ptr!=NULL; ptr=ptr->Next())
	    objd->Link(g,ptr->Node(),ptr->weight);
      }
      if (regions[g]){
	 if ( (*objs)[g] && !((*grs)[g]) ) // Removed node.
	    objd->Del(g);
	 if ((*objs)[g] && (*grs)[g] ) // Update neighbours.
	    for (GEdge *ptr=(*grs)[g]-> Neighbours(); ptr!=NULL; ptr=ptr->Next())
	       objd->Link(g,ptr->Node(),ptr->weight);
      }
   }
   delete [] regions;
   return objd;
}


/*
 * GRAPH 3D 
 */

/*
 * Initializator.
 * Need a value for tnode.
 */
void Graph3d::New( Long s, Long nd, Long nr, Long nc ) {
   if (tnode)
      Delete();
   
   ncol=nc;nrow=nr;ndep=nd;
   if ((size=s)>0){
      tnode=new GNode< Point3d >*[s];
      for (--s;s>=0;tnode[s--]=NULL);
   }
}

/*
 * Destructor.
 * Delete all nodes.
 */
void Graph3d::Delete( ) {
   if (size>0 && tnode){
      for (int i=0;i<size;i++){
	 if (tnode[i])
	    delete tnode[i];
      }
      delete [] tnode;
   }
   tnode=NULL;
}

/*
 * Adds a node in graph
 * at position `node' in graph array,
 * with item=`item',
 * and seed coordinates=`p'.
 */
Errc Graph3d::Add( Long node, Long item, const Point3d &p ) {
   if (node<size && node>=0){
      tnode[node] = new GNode<Point3d>(item,p);
      return SUCCESS;
   }
   return FAILURE;
}

/*
 * Removes the node `n' in graph.
 * -> Removes all refences in its
 * neighbourhood nodes.
 */
Errc Graph3d::Del( Long n ) {
   GEdge *p;
   
   if (!tnode[n])
      return FAILURE;
   
   for (p=tnode[n]->Neighbours(); p!=NULL;p=p->Next()){
      tnode[p->Node()]->Del(n);
   }
   delete tnode[n];
   tnode[n]=NULL;

   return SUCCESS;
}

/*
 * Creates a copy of graph g.
 */
Graph3d &Graph3d::operator=( const Graph3d &g ) {
   Long	i;
   GEdge *l;

   New(g.size,g.ndep,g.nrow,g.ncol);
   for (i=0; i<size; i++){
      if (g[i] != NULL){
	 Add(i,g[i]->Item(),g[i]->seed);
	 tnode[i]->value=g[i]->value;
	 for (l=g[i]->Neighbours(); l!=NULL; l=l->Next()){
	    if (i>l->Node())
	       Link(i,l->Node(),l->weight);
	 }
      }
   }
   return *this;
}

/*
 * Creates a copy.
 */
Pobject *Graph3d::Clone( ) const {
   Graph3d *tmp = new Graph3d(size,ndep,nrow,ncol);
   *tmp=*this;
   return tmp;
}

/*
 * Builds a graph from a region map.
 * Do not use the region 0.
 * Use the 2nd region map as seed cordinates.
 */
Errc Graph3d::Init( const Reg3d &rgs, const Reg3d &grm ) {
   int x,y,z;
   
   if (Init(rgs) != SUCCESS)
      return FAILURE;
   
   for (z=1; z<rgs.Depth()-1; z++)
      for (y=1; y<rgs.Height()-1; y++)
	 for (x=1; x<rgs.Width()-1; x++){
	    if ((grm[z][y][x])){
	       tnode[rgs[z][y][x]]->seed.z=z;
	       tnode[rgs[z][y][x]]->seed.y=y;
	       tnode[rgs[z][y][x]]->seed.x=x;
	 }
      }
   
   return SUCCESS;
}

/*
 * Builds a graph from a region map.
 * Do not use the region 0.
 * No seed.
 */
Errc Graph3d::Init( const Reg3d &rgs ) {
   register short int x,y,z;
   Ulong a,b;
   int v;
   
   New(rgs.Labels()+1,rgs.Depth(),rgs.Height(),rgs.Width());
   
   for (Ulong i=1; i<=rgs.Labels(); i++)
      if (!Add(i,i))
	 return FAILURE;
   
   for (z=1; z<rgs.Depth()-1; z++)
      for (y=1; y<rgs.Height()-1; y++)
	 for (x=1; x<rgs.Width()-1; x++){
	    if (!(a=rgs[z][y][x])) continue;
	    for (v=0; v<26; v++){ 	 // Look for 26-connected regions.
	       if (((b=rgs[z+v26z[v]][y+v26y[v]][x+v26x[v]])!=0) && (b!=a)){
		  Link(a,b,1);
	       }
	    }
	 }
   return SUCCESS;
}

/*
 * Creates a new edge between n1 and n2.
 * If the link already exists,
 * just updates the weight value.
 */
Errc Graph3d::Link( Long n1, Long n2, Float w, bool add ) {
   GEdge *p;
   
   if (!(p=tnode[n1]->Search(n2)))
      tnode[n1]->Add(n2,w);
   else
      if (add) p->weight+=w; else p->weight=w;
   
   if (!(p=tnode[n2]->Search(n1)))
      tnode[n2]->Add(n1,w);
   else
      if (add) p->weight+=w; else p->weight=w;
   return SUCCESS;
}

/*
 * Removes the edge between n1 and n2 (and vice versa).
 */
Errc Graph3d::Unlink( Long n1, Long n2 ) {
   tnode[n1]->Del(n2);
   tnode[n2]->Del(n1);
   return SUCCESS;
}

/*
 * Merges 2 nodes in 1 ->n1
 * Updates the adjacent list of all node (n2 ->n1).
 * and delete n2.
 */
Errc Graph3d::Merge( Long n1, Long n2 ) {
   GEdge *ptr;

   if ((n1==n2) ||  (!tnode[n1]) || (!tnode[n2])) return FAILURE;

   Unlink(n1,n2);
   while((ptr=tnode[n2]->Neighbours())){
      Link(n1,ptr->Node(),ptr->weight);
      Unlink(n2,ptr->Node());
   }
   Del(n2);

   return SUCCESS;
}

/*
 * Splits the node n1 in n2.
 * Updates all adjacent lists.
 */
Errc Graph3d::Split( Long n1, Long n2 ) {
   Add(n2,tnode[n1]->Item(),tnode[n1]->seed);
   for (GEdge *ptr=tnode[n1]->Neighbours();ptr;ptr->Next())
      Link(n2,ptr->Node());
   return SUCCESS;
}

/*      
 * Loads graph atributes from the file.
 */
Errc Graph3d::LoadAttributes( FILE *df ) {
   Long attr[4];
   
   if (Fdecode(attr,sizeof(Long),4,df) < 4)
      return FAILURE;
   New(attr[0],attr[1],attr[2],attr[3]);
   
   return SUCCESS;
}

/*
 * Saves graph attributes into the file.
 */
Errc Graph3d::SaveAttributes( FILE *df ) const {
   Long attr[4];
   attr[0]=size; attr[1]=ndep; attr[2]=nrow;attr[3]=ncol;
   if (Fencode(attr,sizeof(Long),4,df) < 4)
      return FAILURE;
   return SUCCESS;
}

/*
 * Loads a graph from a file.
 */
Errc Graph3d::LoadData( FILE *df ) {
   register int i;
   Long suiv;
   Graph2d::ValueType val;
   Graph2d::ValueType wgh;
   Point3d p;

   for (i=0;i<size;i++){
      if (Fdecode(&suiv,sizeof(suiv),1,df) < 1)
	 return FAILURE;
      if (suiv >= 0){
	 if (Fdecode(&val,sizeof(val),1,df)<1)
	    return FAILURE;
	 if (p.Load(df,_inversionMode) == FAILURE)
	    return FAILURE;
	 if (!Add(i,suiv,p)) return FAILURE;
	 tnode[i]->value=val;
	 
	 if (Fdecode(&suiv,sizeof(suiv),1,df)<1)
	    return FAILURE;
	 while (suiv>-1){
	    if (Fdecode(&wgh,sizeof(wgh),1,df)<1)
	       return FAILURE;
	    if (i>suiv)	// Link with former nodes.
	       Link(i,suiv,wgh);
	    if (Fdecode(&suiv,sizeof(suiv),1,df)<1)
	       return FAILURE;
	 }
      }else tnode[i]=NULL;
   }
   
   return SUCCESS;
}

/*
 * Saves graph data into the file.
 */
Errc Graph3d::SaveData( FILE *df ) const {
   register int i;
   register GEdge *v;
   Long fin=-1;
   Long suiv;

   for (i=0;i<size;i++){
      if (tnode[i]==NULL){
	 if (Fencode(&fin,sizeof(fin),1,df)<1)
	    return FAILURE;
      }else{
	 suiv=tnode[i]->Item();
	 if (Fencode(&suiv,sizeof(suiv),1,df)<1)
	    return FAILURE;
	 if (Fencode(&(tnode[i]->value),sizeof(tnode[i]->value),1,df)<1)
	    return FAILURE;
	 if (tnode[i]->seed.Save(df) == FAILURE)
	    return FAILURE;
	 for (v=tnode[i]->Neighbours();v!=NULL;v=v->Next()){
	    suiv=v->Node();
	    if (Fencode(&suiv,sizeof(suiv),1,df)<1)
	       return FAILURE;
	    if (Fencode(&(v->weight),sizeof(v->weight),1,df)<1)
	       return FAILURE;
	 }
	 if (Fencode(&fin,sizeof(fin),1,df)<1)
	    return FAILURE ;
      }
   }
   return SUCCESS;
}

/*
 * Creates a new graph only with the unmasked nodes by `mask'.
 */
Pobject *Graph3d::Mask( const Pobject *mask ) {
   if ((!mask)||(mask->Type()!=Po_Reg3d)||(((Reg3d*)mask)->Size()!=ImageSize())){
      std::cerr << "Warning: bad mask format... ignored" << std::endl;
      return this;
   }

   Graph3d *objd = (Graph3d*)Clone();
   Reg3d *m=(Reg3d*)mask;
   bool *regions=(bool*)calloc(m->Labels()+1,sizeof(bool));
   Ulong *pm=m->Vector();
   
   for (int i=0; i<ndep*nrow*ncol; i++, pm++)
      regions[*pm]=true; // List of masked/unmasked regions.

   for (int g=1;g<objd->Size();g++)
      if (!regions[g]) // Delete masked nodes.
	 objd->Del(g);

   delete [] regions;
   return objd;
}

/*
 * CreateS the output graph with the previous masked node 
 * and the new nodes.
 */
Pobject *Graph3d::UnMask( const Pobject *mask, const Pobject *gr ) {
   if ((!mask)||(mask->Type()!=Po_Reg3d)||(((Reg3d*)mask)->Size()!=ImageSize())||(gr->Type() != Type())||(((Graph3d*)gr)->ImageSize()!=ImageSize())){
      std::cerr << "Warning: bad unmask format... ignored" << std::endl;
      return this;
   }
   if ((mask == NULL) || (mask->Type() != Po_Reg3d) || (gr->Type() != Type())){
      return this;
   }

   Graph3d *objs = (Graph3d*)gr;
   Graph3d *grs = (Graph3d*)this;
   Graph3d *objd = (Graph3d*)gr->Clone();
   Reg3d *m=(Reg3d*)mask;
   bool *regions=(bool*)calloc(m->Labels()+1,sizeof(bool));
   Ulong *pm=m->Vector();
   
   for (int i=0; i<ndep*nrow*ncol; i++, pm++)
      regions[*pm]=true; // List of masked/unmasked regions.

   // Add or remove node from the input graph with processed graph.
   for (int g=1;g<objd->Size();g++){
      if ( !((*objs)[g]) && (*grs)[g]){  // Added node
	 objd->Add(g,(*grs)[g]->Item(),(*grs)[g]->seed);
	 for (GEdge *ptr=(*grs)[g]-> Neighbours(); ptr!=NULL; ptr=ptr->Next())
	    objd->Link(g,ptr->Node(),ptr->weight);
      }
      if (regions[g]){
	 if ( (*objs)[g] && !((*grs)[g]) ) // Removed node.
	    objd->Del(g);
	 if ((*objs)[g] && (*grs)[g] ) // Update neighbours.
	    for (GEdge *ptr=(*grs)[g]-> Neighbours(); ptr!=NULL; ptr=ptr->Next())
	       objd->Link(g,ptr->Node(),ptr->weight);
      }
   }
   delete [] regions;
   return objd;
}


// Explicit instanciation: not necessary with Visual
template class pandore::GNode< Point2d >;
template class pandore::GNode< Point3d >;
