//==================================================================================================
// G r a p h                                                                              Interface
// L a y o u t
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ Library. This library is free software; you can redistribute it
// and/or modify it under the terms of the GNU Library General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
// the GNU Library General Public License for more details (http://www.gnu.org).

/*DESCRIPTION*/
/* This module provides facilities to define and manipulate the layout (i.e. the coordinates
   of the nodes) of a graph. */

// File Name //-------------------------------------------------------------------------------------
#line __LINE__ "graph/layout.hpp"

// Guardian //--------------------------------------------------------------------------------------
#ifndef guGraphLayout
#define guGraphLayout

// Headers //---------------------------------------------------------------------------------------
#include <bpp/graph/structure.hpp> /*INCLUDE*/

namespace bpp {

// Importation/Exportation //-----------------------------------------------------------------------
#ifdef GRAPH_DLL
 #define dll_export DLL_EXPORT
#else
 #define dll_export DLL_IMPORT
#endif

// Namespaces //------------------------------------------------------------------------------------
#define public_area  graphLayout
#define private_area graphLayout_private

namespace public_area  {
 /*NAMESPACE*/ using namespace dataStructure;
 /*NAMESPACE*/ using namespace graphStructure;
}

namespace private_area { using namespace public_area; }

extern_module_name;

// Initialization //--------------------------------------------------------------------------------
#define iniGraphLayout
has_initializer;

// Macrocommands //---------------------------------------------------------------------------------
/*ALIAS*/
#define tdGraph class prArcData,class prNodeData //

/*ALIAS*/
#define tuGraph prArcData,prNodeData //

// Types & Classes //-------------------------------------------------------------------------------
namespace public_area {
 //-----------------------------------------------------------------------------------Variable Types
 /*TYPE*/ /* Coordinates of a node in a graph layout. */
 structure {
  tyCardinal x;
  tyCardinal y;
 }
 tyCoordinate; //

 /*TYPE*/ /* Layout of a graph: association of node keys with their coordinate. */
 typedef std_map(tyNodeKey,tyCoordinate) clGraphLayout;
 //-----------------------------------------------------------------------------------Constant Types
 typedef const tyCoordinate  tcCoordinate;
 typedef const clGraphLayout ctGraphLayout;
}

namespace private_area {
 structure {
  tyReal x;
  tyReal y;
  tyReal dx;
  tyReal dy;
 }
 tyNodeCoordinate;
}

// Functions Interface //---------------------------------------------------------------------------
namespace public_area {
 template <tdGraph> void generateGraphLayout(clGraph<tuGraph> &,clGraphLayout &);

 template <class prData> void generateBinaryTreeLayout(clGraph<clNoData,prData> &,
                                                       const clBinaryTree<prData> &,
                                                       clGraphLayout &);

 template <tdGraph> void improveGraphLayout(clGraph<tuGraph> &,clGraphLayout &);

 function clInStream & operator >> (clInStream &,clGraphLayout &);
 function clOutStream & operator << (clOutStream &,ctGraphLayout &);

 function void getBoundingBox(ctGraphLayout &,tyCoordinate &,tyCoordinate &);
 function void numberLayoutKeys(clGraphLayout &);

 function void convertEps(clOutStream &,ctGenericGraph &,clGraphLayout &,tyBoolean,tyBoolean,
                          tyBoolean,tyBoolean,tyBoolean,tyBoolean,tyBoolean,tyBoolean,
                          tyReal,tyReal);
}

namespace private_area {
 template <class prData>
 clNode<clNoData,prData> * createNodeForBinaryTree(clGraph<clNoData,prData> &,
                                                   const clBinaryTree<prData> &,clGraphLayout &,
                                                   tyCardinal,tyCardinal);
}

// Errors //----------------------------------------------------------------------------------------
namespace public_area {}

// Constants & Variables //-------------------------------------------------------------------------
/*CONSTANT*/ /* Height of the layout of a graph. */
extern_static_constant(public,tyCardinal,goGraphLayoutHeight,graphLayoutHeight);

/*CONSTANT*/ /* Width of the layout of a graph. */
extern_static_constant(public,tyCardinal,goGraphLayoutWidth,graphLayoutWidth);

extern_static_constant(private,tcString,goGraphLayoutEndFlag,?);
extern_static_constant(private,tcString,goGraphLayoutStartFlag,?);
extern_static_constant(private,tcString,goNodeFlag,?);

extern_dynamic_constant(private,clString,goEpsHeaderFileName1,?);
extern_dynamic_constant(private,clString,goEpsHeaderFileName2,?);

// X X X  Interface //------------------------------------------------------------------------------
namespace {}

// Functions Inline //------------------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

// X X X  Inline //---------------------------------------------------------------------------------
namespace {}

// Functions Implementation //----------------------------------------------------------------------
namespace public_area {
 //------------------------------------------------------------------------------GenerateGraphLayout
 /*FUNCTION*/ /* Generates randomly the layout of a graph. */
 template <tdGraph> void generateGraphLayout(clGraph<tuGraph> & agGraph,clGraphLayout & agLayout) {
  typedef typename clGraph<tuGraph>::cpNodeX::const_iterator cpIterator;

  tyCoordinate lcCoordinate;
  tyBoolean ** lcMap;
  tyCardinal   lcX;
  tyCardinal   lcY;

  cpIterator lcCurrentNode = agGraph.nodes().begin();
  cpIterator lcLastNode    = agGraph.nodes().end();

  // Map Building //
  lcMap=new_array(tyBoolean *,graphLayoutWidth());
  lcX=0;

  while (lcX<graphLayoutWidth()) {
   lcY=0;
   lcMap[lcX]=new_array(tyBoolean,graphLayoutHeight());

   while (lcY<graphLayoutHeight()) {
    lcMap[lcX][lcY]=false;
    lcY++;
   }

   lcX++;
  }

  // Coordinates Generation //
  agLayout.erase(agLayout.begin(),agLayout.end());

  while (lcCurrentNode!=lcLastNode) {
   do {
    lcCoordinate.x=randomCardinal(graphLayoutWidth());
    lcCoordinate.y=randomCardinal(graphLayoutHeight());
   }
   while (lcMap[lcCoordinate.x][lcCoordinate.y]);

   lcMap[lcCoordinate.x][lcCoordinate.y]=true;
   agLayout.insert(clGraphLayout::value_type((*lcCurrentNode).second->key(),lcCoordinate));
   lcCurrentNode++;
  }

  // Map Deletion //
  lcX=0;

  while (lcX<graphLayoutWidth()) {
   delete_array(lcMap[lcX]);
   lcX++;
  }

  delete_array(lcMap);
 }
 //-------------------------------------------------------------------------GenerateBinaryTreeLayout
 /*FUNCTION*/
 /* Generates the layout of a binary tree. The tree is converted into a graph and a layout is
    built for this graph. */
 template <class prData>
 void generateBinaryTreeLayout(clGraph<clNoData,prData> & agGraph,
                               const clBinaryTree<prData> & agTree,clGraphLayout & agLayout) {
  agGraph.clear();
  agLayout.erase(agLayout.begin(),agLayout.end());
  private_area::createNodeForBinaryTree(agGraph,agTree,agLayout,agTree.depth(),0);
 }
 //-------------------------------------------------------------------------------ImproveGraphLayout
 /*FUNCTION*/
 /* Attempts to improve the layout of a graph according to the graph structure. This is a
    heuristic, so the result is sometimes not very good, especially for large graphs. */
 template <tdGraph> void improveGraphLayout(clGraph<tuGraph> & agGraph,clGraphLayout & agLayout) {
  typedef private_area::tyNodeCoordinate                    tyNode;
  typedef std_map(tyNodeKey,tyNode)                         clNodeX;
  typedef typename clGraph<tuGraph>::cpArcX::const_iterator cpArcIterator;
  typedef clNodeX::iterator                                 cpNodeIterator1;
  typedef clGraphLayout::const_iterator                     cpNodeIterator2;

  safe_static tcReal lcArcLength          = 20.0;
  safe_static tcReal lcRepulsionLength    = 400.0;
  safe_static tcReal lcRepulsionStart     = 10.0;
  safe_static tcReal lcRepulsionEnd       = 7.0;
  safe_static tcReal lcRepulsionStep      = 0.01;
  safe_static tcReal lcRepulsionThreshold = 8.0;
  safe_static tcReal lcRandomStart        = 5.0;
  safe_static tcReal lcRandomEnd          = 0.01;
  safe_static tcReal lcRandomStep         = 0.02;

  tyNode          lcCoordinate;
  cpArcIterator   lcCurrentArc;
  cpNodeIterator2 lcCurrentNode1;
  cpNodeIterator1 lcCurrentNode2;
  cpNodeIterator1 lcCurrentNode3;
  cpArcIterator   lcLastArc;
  cpNodeIterator2 lcLastNode1;
  cpNodeIterator1 lcLastNode2;
  tyNode *        lcNode1;
  tyNode *        lcNode2;
  tyCoordinate *  lcNode3;
  clNodeX         lcNodeX;

  tyReal lcCoefficient;
  tyReal lcDx;
  tyReal lcDy;
  tyReal lcLength;
  tyReal lcMaxX;
  tyReal lcMaxY;
  tyReal lcMinX;
  tyReal lcMinY;
  tyReal lcVectorX;
  tyReal lcVectorY;
  tyReal lcX;
  tyReal lcY;

  tyCardinal lcFinalIteration = 1000;
  tyReal     lcRepulsion      = lcRepulsionStart;
  tyReal     lcRandom         = lcRandomStart;

  // Initialization //
  lcCurrentNode1=agLayout.begin();
  lcLastNode1=agLayout.end();

  while (lcCurrentNode1!=lcLastNode1) {
   lcCoordinate.x=(*lcCurrentNode1).second.x;
   lcCoordinate.y=(*lcCurrentNode1).second.y;
   lcCoordinate.dx=0.0;
   lcCoordinate.dy=0.0;
   lcNodeX.insert(clNodeX::value_type((*lcCurrentNode1).first,lcCoordinate));
   lcCurrentNode1++;
  }

  // Layout Improvement //
  while (lcRepulsion>lcRepulsionEnd or lcRandom>lcRandomEnd or lcFinalIteration>0) {

   // Parameters Evolution //
   lcRepulsion-=lcRepulsionStep;
   if (lcRepulsion<lcRepulsionEnd) lcRepulsion=lcRepulsionEnd;

   lcRandom-=lcRandomStep;
   if (lcRandom<lcRandomEnd) lcRandom=lcRandomEnd;

   if (not (lcRepulsion>lcRepulsionEnd or lcRandom>lcRandomEnd)) lcFinalIteration--;

   // Arcs Attraction //
   lcCurrentArc=agGraph.arcs().begin();
   lcLastArc=agGraph.arcs().end();

   while (lcCurrentArc!=lcLastArc) {
    lcNode1=&(lcNodeX[(*lcCurrentArc).second->sourceNode()->key()]);
    lcNode2=&(lcNodeX[(*lcCurrentArc).second->targetNode()->key()]);
    lcVectorX=lcNode2->x-lcNode1->x;
    lcVectorY=lcNode2->y-lcNode1->y;
    lcLength=std::sqrt(lcVectorX*lcVectorX + lcVectorY*lcVectorY);

    if (lcLength!=0.0) {
     lcCoefficient=(lcArcLength-lcLength)/(lcLength*3.0);
     lcNode1->dx-=lcCoefficient*lcVectorX;
     lcNode1->dy-=lcCoefficient*lcVectorY;
     lcNode2->dx+=lcCoefficient*lcVectorX;
     lcNode2->dy+=lcCoefficient*lcVectorY;
    }

    lcCurrentArc++;
   }

   // Nodes Repulsion //
   lcCurrentNode2=lcNodeX.begin();
   lcLastNode2=lcNodeX.end();

   while (lcCurrentNode2!=lcLastNode2) {
    lcNode1=&((*lcCurrentNode2).second);
    lcDx=0.0;
    lcDy=0.0;
    lcCurrentNode3=lcNodeX.begin();

    while (lcCurrentNode3!=lcLastNode2) {
     if (lcCurrentNode2!=lcCurrentNode3) {
      lcNode2=&((*lcCurrentNode3).second);
      lcVectorX=lcNode2->x-lcNode1->x;
      lcVectorY=lcNode2->y-lcNode1->y;
      lcLength=std::sqrt(lcVectorX*lcVectorX + lcVectorY*lcVectorY);

      if (lcLength==0.0) {
       if (randomCardinal(2)) lcX=0.001*tyReal(randomCardinal(1000));
       else lcX=-0.001*tyReal(randomCardinal(1000));

       if (randomCardinal(2)) lcY=0.001*tyReal(randomCardinal(1000));
       else lcY=-0.001*tyReal(randomCardinal(1000));

       lcLength=std::sqrt(lcX*lcX+lcY*lcY);

       if (lcLength!=0.0) {
        lcDx+=lcRepulsionLength*(lcX/lcLength);
        lcDy+=lcRepulsionLength*(lcY/lcLength);
       }
      }
      else {
       lcDx-=(lcRepulsionLength-lcLength)*(lcVectorX/lcLength);
       lcDy-=(lcRepulsionLength-lcLength)*(lcVectorY/lcLength);
      }
     }

     lcCurrentNode3++;
    }

    lcLength=std::sqrt(lcDx*lcDx+lcDy*lcDy);

    if (lcLength!=0.0) {
     lcNode1->dx+=lcRepulsion*lcDx/lcLength;
     lcNode1->dy+=lcRepulsion*lcDy/lcLength;
    }

    lcCurrentNode2++;
   }

   // Nodes Moving //
   lcCurrentNode2=lcNodeX.begin();
   lcLastNode2=lcNodeX.end();

   while (lcCurrentNode2!=lcLastNode2) {
    lcNode1=&((*lcCurrentNode2).second);

    if (lcRandom>lcRandomEnd) {
     if (randomCardinal(2)) lcNode1->dx+=0.01*randomCardinal(tyCardinal(lcRandom*100.0));
     else lcNode1->dx-=0.01*randomCardinal(tyCardinal(lcRandom*100.0));

     if (randomCardinal(2)) lcNode1->dy+=0.01*randomCardinal(tyCardinal(lcRandom*100.0));
     else lcNode1->dy-=0.01*randomCardinal(tyCardinal(lcRandom*100.0));
    }

    if (lcRepulsion>lcRepulsionThreshold) {
     if (lcNode1->dx>lcRepulsionLength*0.25) {
      lcCoefficient=lcNode1->dx/(lcRepulsionLength*0.25);
      lcNode1->dx/=lcCoefficient;
      lcNode1->dy/=lcCoefficient;
     }

     if (lcNode1->dy>lcRepulsionLength*0.25) {
      lcCoefficient=lcNode1->dy/(lcRepulsionLength*0.25);
      lcNode1->dx/=lcCoefficient;
      lcNode1->dy/=lcCoefficient;
     }

     lcNode1->x+=lcNode1->dx;
     lcNode1->y+=lcNode1->dy;
    }
    else {
     lcLength=std::sqrt(lcNode1->dx*lcNode1->dx + lcNode1->dy*lcNode1->dy);

     if (lcLength!=0.0) {
      lcNode1->x+=lcNode1->dx/lcLength;
      lcNode1->y+=lcNode1->dy/lcLength;
     }
    }

    lcNode1->dx/=2.0;
    lcNode1->dy/=2.0;
    lcCurrentNode2++;
   }
  }

  // Bounding Box //
  lcMinX=realMax();
  lcMinY=realMax();
  lcMaxX=realMin();
  lcMaxY=realMin();
  lcCurrentNode2=lcNodeX.begin();
  lcLastNode2=lcNodeX.end();

  while (lcCurrentNode2!=lcLastNode2) {
   lcNode1=&((*lcCurrentNode2).second);
   if (lcMinX>lcNode1->x) lcMinX=lcNode1->x;
   if (lcMinY>lcNode1->y) lcMinY=lcNode1->y;
   if (lcMaxX<lcNode1->x) lcMaxX=lcNode1->x;
   if (lcMaxY<lcNode1->y) lcMaxY=lcNode1->y;
   lcCurrentNode2++;
  }

  // Scaling //
  if (lcMaxY-lcMinY>lcMaxX-lcMinX) lcCoefficient=(lcMaxY-lcMinY)/100.0;
  else lcCoefficient=(lcMaxX-lcMinX)/100.0;

  if (lcCoefficient==0.0) lcCoefficient=1.0;
  lcCurrentNode2=lcNodeX.begin();
  lcLastNode2=lcNodeX.end();

  while (lcCurrentNode2!=lcLastNode2) {
   lcNode1=&((*lcCurrentNode2).second);
   lcNode3=&(agLayout[(*lcCurrentNode2).first]);
   lcNode3->x=tyCardinal((lcNode1->x-lcMinX)/lcCoefficient);
   lcNode3->y=tyCardinal((lcNode1->y-lcMinY)/lcCoefficient);
   lcCurrentNode2++;
  }
 }
}

namespace private_area {
 //--------------------------------------------------------------------------CreateNodeForBinaryTree
 template <class prData>
 clNode<clNoData,prData> * createNodeForBinaryTree(clGraph<clNoData,prData> & agGraph,
                                                   const clBinaryTree<prData> & agTree,
                                                   clGraphLayout & agLayout,tyCardinal agDepth,
                                                   tyCardinal agOffset) {
  typedef clArc<clNoData,prData>  cpArc;
  typedef clNode<clNoData,prData> cpNode;

  tyCoordinate lcCoordinate;
  cpNode *     lcNode;

  cpNode * lcLeftNode  = nil;
  cpNode * lcRightNode = nil;

  if (agTree.empty()) return (nil);

  lcCoordinate.x=agOffset + 4*agTree.left().width();
  lcCoordinate.y=8*agDepth;

  lcNode=new_object(cpNode(agGraph,agGraph.getNewNodeKey(),agTree.data()));
  agLayout.insert(clGraphLayout::value_type(lcNode->key(),lcCoordinate));

  if (not agTree.left().empty())
   lcLeftNode=createNodeForBinaryTree(agGraph,agTree.left(),agLayout,agDepth-1,agOffset);

  if (not agTree.right().empty())
   lcRightNode=createNodeForBinaryTree(agGraph,agTree.right(),agLayout,agDepth-1,lcCoordinate.x+2);

  if (not agTree.left().empty())
   new_object(cpArc(agGraph,agGraph.getNewArcKey(),clNoData(),lcNode->key(),lcLeftNode->key()));

  if (not agTree.right().empty())
   new_object(cpArc(agGraph,agGraph.getNewArcKey(),clNoData(),lcNode->key(),lcRightNode->key()));

  return (lcNode);
 }
}

// End //-------------------------------------------------------------------------------------------
}
#undef dll_export
#undef tdGraph
#undef tuGraph
#undef public_area
#undef private_area
#endif
 
//==================================================================================================
// G r a p h                                                                         Implementation
// L a y o u t
//                                                                                By Bruno Bachelet
//==================================================================================================
// Copyright (c) 1999-2016
// Bruno Bachelet - bruno@nawouak.net - http://www.nawouak.net
//
// This file is part of the B++ Library. This library is free software; you can redistribute it
// and/or modify it under the terms of the GNU Library General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your option) any later
// version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
// the GNU Library General Public License for more details (http://www.gnu.org).

// File Name //-------------------------------------------------------------------------------------
#line __LINE__ "graph/layout.cpp"

// DLL Belonging //---------------------------------------------------------------------------------
#define GRAPH_DLL

// Headers //---------------------------------------------------------------------------------------
#include <bpp/graph/layout.hpp> /*INTERFACE*/
#include <bpp/file_name.hpp> /*NEED*/

namespace bpp {

// Namespaces //------------------------------------------------------------------------------------
#define public_area  graphLayout
#define private_area graphLayout_private
#define dll_export   DLL_EXPORT

namespace public_area  {}
namespace private_area {}

static_module_name("Graph/Layout");

// Initialization //--------------------------------------------------------------------------------
#undef iniGraphLayout
static_constant(private_area::clInitializer,goInitializer);

// Errors //----------------------------------------------------------------------------------------
namespace public_area {}

// Constants & Variables //-------------------------------------------------------------------------
static_constant(tyCardinal,goGraphLayoutHeight);
static_constant(tyCardinal,goGraphLayoutWidth);

static_constant(tcString,goGraphLayoutEndFlag);
static_constant(tcString,goGraphLayoutStartFlag);
static_constant(tcString,goNodeFlag);

dynamic_constant(clString,goEpsHeaderFileName1);
dynamic_constant(clString,goEpsHeaderFileName2);

// Static Members //--------------------------------------------------------------------------------
namespace public_area  {}
namespace private_area {}

// Functions Implementation //----------------------------------------------------------------------
namespace public_area {
 //--------------------------------------------------------------------------GraphLayout Operator <<
 /*FUNCTION*/ /* Writes the layout of a graph into a stream. */
 function clOutStream & operator << (clOutStream & agStream,ctGraphLayout & agLayout) {
  method_name("graphLayout::operator <<");

  clGraphLayout::const_iterator lcCurrentNode = agLayout.begin();
  clGraphLayout::const_iterator lcLastNode    = agLayout.end();

  if (lcCurrentNode!=lcLastNode) {
   agStream << private_area::goGraphLayoutStartFlag << end_line;

   while (lcCurrentNode!=lcLastNode) {
    agStream << private_area::goNodeFlag << " " << (*lcCurrentNode).first << " = ";
    agStream << (*lcCurrentNode).second.x << " , " << (*lcCurrentNode).second.y << end_line;
    lcCurrentNode++;
   }

   agStream << private_area::goGraphLayoutEndFlag << end_line;
  }

  if (agStream.fail()) send_error(erStreamWriting);
  return (agStream);
 }
 //--------------------------------------------------------------------------GraphLayout Operator >>
 /*FUNCTION*/ /* Reads the layout of a graph from a stream. */
 function clInStream & operator >> (clInStream & agStream,clGraphLayout & agLayout) {
  method_name("graphLayout::operator >>");

  tyCoordinate lcCoordinate;
  tyNodeKey    lcNodeKey;
  clString     lcString;

  agStream >> lcString;
  if (lcString=="") return (agStream);
  if (lcString!=private_area::goGraphLayoutStartFlag) send_error(erStreamReading);
  agStream >> lcString;

  while (lcString==private_area::goNodeFlag) {
   agStream >> lcNodeKey >> lcString >> lcCoordinate.x >> lcString >> lcCoordinate.y >> lcString;
   agLayout.insert(clGraphLayout::value_type(lcNodeKey,lcCoordinate));
  }

  if (lcString!=private_area::goGraphLayoutEndFlag) send_error(erStreamReading);

  if (agStream.fail()) send_error(erStreamWriting);
  return (agStream);
 }
 //-----------------------------------------------------------------------------------GetBoundingBox
 /*FUNCTION*/ /* Finds the bounding box of the layout of a graph. */
 function void getBoundingBox(ctGraphLayout & agLayout,tyCoordinate & agCorner1,
                              tyCoordinate & agCorner2) {
  typedef ctGraphLayout::const_iterator clIterator;

  clIterator lcCurrentCoordinate = agLayout.begin();
  clIterator lcLastCoordinate    = agLayout.end();

  tyCoordinate lcCoordinate;

  agCorner1.x=cardinalMax();
  agCorner1.y=cardinalMax();
  agCorner2.x=0;
  agCorner2.y=0;

  while (lcCurrentCoordinate!=lcLastCoordinate) {
   lcCoordinate=(*lcCurrentCoordinate).second;
   if (lcCoordinate.x<agCorner1.x) agCorner1.x=lcCoordinate.x;
   if (lcCoordinate.y<agCorner1.y) agCorner1.y=lcCoordinate.y;
   if (lcCoordinate.x>agCorner2.x) agCorner2.x=lcCoordinate.x;
   if (lcCoordinate.y>agCorner2.y) agCorner2.y=lcCoordinate.y;
   lcCurrentCoordinate++;
  }
 }
 //---------------------------------------------------------------------------------NumberLayoutKeys
 /*FUNCTION*/ /* Numbers from 0 the keys of the layout. */
 function void numberLayoutKeys(clGraphLayout & agLayout) {
  clGraphLayout::const_iterator lcCurrentNode = agLayout.begin();
  clGraphLayout::const_iterator lcLastNode    = agLayout.end();
  tyNodeKey                     lcNodeKey     = 0;

  tyCoordinate          lcCoordinate;
  std_vector(tyNodeKey) lcNodeS;

  // Nodes List //
  while (lcCurrentNode!=lcLastNode) {
   lcNodeS.push_back((*lcCurrentNode).first);
   lcCurrentNode++;
  }

  // Nodes Numbering //
  while (lcNodeKey<lcNodeS.size()) {
   if (lcNodeS[lcNodeKey]!=lcNodeKey) {
    lcCoordinate=agLayout[lcNodeS[lcNodeKey]];
    agLayout.erase(lcNodeS[lcNodeKey]);
    agLayout.insert(clGraphLayout::value_type(lcNodeKey,lcCoordinate));
   }

   lcNodeKey++;
  }
 }
 //---------------------------------------------------------------------------------------ConvertEps
 /*FUNCTION*/
 /* Converts a graph into the EPS (Encapsulated PostScript) format and sends it into a stream. */
 function void convertEps(clOutStream & agStream,ctGenericGraph & agGraph,clGraphLayout & agLayout,
                          tyBoolean agNodeData,tyBoolean agArcData,tyBoolean agNodeKey,
                          tyBoolean agArcKey,tyBoolean agTextCentered,tyBoolean agReversed,
                          tyBoolean agMirrored,tyBoolean agFlipped,tyReal agSpaceCoefficient,
                          tyReal agNodeRadius) {
  method_name("convertEps");

  typedef clGenericGraph::cpArcX::const_iterator            clArcIterator;
  typedef clGenericGraph::cpNodeX::const_iterator           clNodeIterator;
  typedef std_map(std_pair(tyNodeKey,tyNodeKey),tyCardinal) clArcCounterX;

  safe_static tcReal     lcArrowSize = 5;
  safe_static tyCardinal lcOffset    = 50;
  safe_static tyCardinal lcStep      = 6;

  clGenericArc *  lcArc;
  clArcCounterX   lcArcCounterX;
  tyCoordinate    lcCorner;
  tyCardinal *    lcCounter;
  clArcIterator   lcCurrentArc;
  clNodeIterator  lcCurrentNode;
  clInFile        lcHeaderFile;
  clArcIterator   lcLastArc;
  clNodeIterator  lcLastNode;
  clGenericNode * lcNode;
  tyCoordinate    lcPoint1;
  tyCoordinate    lcPoint2;

  tyReal lcCoefficient1;
  tyReal lcCoefficient2;
  tyReal lcDx;
  tyReal lcDy;
  tyReal lcLength;

  std_pair(tyArcKey,tyArcKey) lcPair(0,0);

  tcCardinal lcTextSize = tyCardinal(agNodeRadius*8/10);

  // Header Part 1 //
  open(lcHeaderFile,private_area::goEpsHeaderFileName1->data(),ios::in|ios::binary);
  agStream << "%!PS-Adobe-2.0 EPSF-2.0" << end_line;
  getBoundingBox(agLayout,lcPoint1,lcPoint2);

  if (agReversed) {
   agStream << "%%BoundingBox: " << lcStep*lcPoint1.y << " " << lcStep*lcPoint1.x << " ";
   agStream << (lcStep*lcPoint2.y + 2*lcOffset) << " ";
   agStream << (lcStep*lcPoint2.x + 2*lcOffset) << end_line;
   lcCorner.x=lcOffset+lcStep*lcPoint2.y+lcStep*lcPoint1.y;
   lcCorner.y=lcOffset+lcStep*lcPoint2.x+lcStep*lcPoint1.x;
  }
  else {
   agStream << "%%BoundingBox: " << lcStep*lcPoint1.x << " " << lcStep*lcPoint1.y << " ";
   agStream << (lcStep*lcPoint2.x + 2*lcOffset) << " ";
   agStream << (lcStep*lcPoint2.y + 2*lcOffset) << end_line;
   lcCorner.x=lcOffset+lcStep*lcPoint2.x+lcStep*lcPoint1.x;
   lcCorner.y=lcOffset+lcStep*lcPoint2.y+lcStep*lcPoint1.y;
  }

  copy(agStream,lcHeaderFile,false);
  close(lcHeaderFile);
  if (lcHeaderFile.fail()) send_error(erFileReading);

  // Header Part 2 //
  agStream << "/vertex_radius { " << agNodeRadius << " } def" << end_line;

  if (agTextCentered)
   agStream << "/text_y_shift_2 { " << (-double(lcTextSize)/4) << " } def" << end_line;
  else agStream << "/text_y_shift_2 { " << (double(lcTextSize)+2) << " } def" << end_line;

  agStream << "/Times-Roman findfont " << double(lcTextSize) << " scalefont setfont" << end_line;

  agStream << end_line;
  open(lcHeaderFile,private_area::goEpsHeaderFileName2->data(),ios::in|ios::binary);
  copy(agStream,lcHeaderFile,false);
  close(lcHeaderFile);
  if (lcHeaderFile.fail()) send_error(erFileReading);
  agStream << "% DRAWING %=====================================================================";
  agStream << end_line;

  // Nodes Drawing //
  lcCurrentNode=agGraph.nodes().begin();
  lcLastNode=agGraph.nodes().end();

  while (lcCurrentNode!=lcLastNode) {
   lcNode=(*lcCurrentNode).second;
   lcPoint1=agLayout[lcNode->key()];
   agStream << "(";
   if (agNodeKey) agStream << lcNode->key();
   if (agNodeKey and agNodeData) agStream << " - ";
   if (agNodeData) agStream << lcNode->data().characters();
   agStream << ") ";

   if (agReversed) {
    if (agMirrored) agStream << (lcCorner.x - lcStep*lcPoint1.y);
    else agStream << (lcOffset + lcStep*lcPoint1.y);

    if (agFlipped) agStream << " " << (lcCorner.y - lcStep*lcPoint1.x);
    else agStream << " " << (lcOffset + lcStep*lcPoint1.x);
   }
   else {
    if (agMirrored) agStream << (lcCorner.x - lcStep*lcPoint1.x);
    else agStream << (lcOffset + lcStep*lcPoint1.x);

    if (agFlipped) agStream << " " << (lcCorner.y - lcStep*lcPoint1.y);
    else agStream << " " << (lcOffset + lcStep*lcPoint1.y);
   }

   agStream << " vertex" << end_line;
   lcCurrentNode++;
  }

  // Arcs Drawing //
  lcCurrentArc=agGraph.arcs().begin();
  lcLastArc=agGraph.arcs().end();

  while (lcCurrentArc!=lcLastArc) {
   lcArc=(*lcCurrentArc).second;
   lcPair.first=lcArc->sourceNode()->key();
   lcPair.second=lcArc->targetNode()->key();
   lcPoint1=agLayout[lcPair.first];
   lcPoint2=agLayout[lcPair.second];
   lcDx=lcStep*(tyReal(lcPoint2.x)-tyReal(lcPoint1.x));
   lcDy=lcStep*(tyReal(lcPoint2.y)-tyReal(lcPoint1.y));
   lcLength=std::sqrt(lcDx*lcDx+lcDy*lcDy);

   if (lcLength>0.0) {
    agStream << "(";
    if (agArcKey) agStream << lcArc->key();
    if (agArcKey and agArcData) agStream << " - ";
    if (agArcData) agStream << lcArc->data().characters();
    agStream << ") ";

    if (lcArcCounterX.count(lcPair)==0) {
     lcCoefficient1=0.5-0.5*lcArrowSize/lcLength;
     lcArcCounterX.insert(clArcCounterX::value_type(lcPair,1));
    }
    else {
     lcCounter=&lcArcCounterX[lcPair];

     if ((*lcCounter)%2==0)
      lcCoefficient1=0.5-0.5*lcArrowSize/lcLength
                     +0.5*(*lcCounter)*lcTextSize*agSpaceCoefficient/lcLength;
     else
      lcCoefficient1=0.5-lcArrowSize/lcLength
                     -0.5*(*lcCounter+1)*lcTextSize*agSpaceCoefficient/lcLength;

     (*lcCounter)++;
    }

    if (agReversed) {
     if (agMirrored) agStream << (lcCorner.x - lcStep*lcPoint1.y - lcDy*lcCoefficient1) << " ";
     else agStream << (lcOffset + lcStep*lcPoint1.y + lcDy*lcCoefficient1) << " ";

     if (agFlipped) agStream << (lcCorner.y - lcStep*lcPoint1.x - lcDx*lcCoefficient1) << " ";
     else agStream << (lcOffset + lcStep*lcPoint1.x + lcDx*lcCoefficient1) << " ";
    }
    else {
     if (agMirrored) agStream << (lcCorner.x - lcStep*lcPoint1.x - lcDx*lcCoefficient1) << " ";
     else agStream << (lcOffset + lcStep*lcPoint1.x + lcDx*lcCoefficient1) << " ";

     if (agFlipped) agStream << (lcCorner.y - lcStep*lcPoint1.y - lcDy*lcCoefficient1) << " ";
     else agStream << (lcOffset + lcStep*lcPoint1.y + lcDy*lcCoefficient1) << " ";
    }

    lcCoefficient1=agNodeRadius/lcLength;
    lcCoefficient2=(agNodeRadius+lcArrowSize)/lcLength;

    if (agReversed) {
     if (agMirrored) agStream << (lcCorner.x - lcStep*lcPoint1.y - lcDy*lcCoefficient1) << " ";
     else agStream << (lcOffset + lcStep*lcPoint1.y + lcDy*lcCoefficient1) << " ";

     if (agFlipped) agStream << (lcCorner.y - lcStep*lcPoint1.x - lcDx*lcCoefficient1) << " ";
     else agStream << (lcOffset + lcStep*lcPoint1.x + lcDx*lcCoefficient1) << " ";

     if (agMirrored) agStream << (lcCorner.x - lcStep*lcPoint2.y + lcDy*lcCoefficient2) << " ";
     else agStream << (lcOffset + lcStep*lcPoint2.y - lcDy*lcCoefficient2) << " ";

     if (agFlipped) agStream << (lcCorner.y - lcStep*lcPoint2.x + lcDx*lcCoefficient2);
     else agStream << (lcOffset + lcStep*lcPoint2.x - lcDx*lcCoefficient2);
    }
    else {
     if (agMirrored) agStream << (lcCorner.x - lcStep*lcPoint1.x - lcDx*lcCoefficient1) << " ";
     else agStream << (lcOffset + lcStep*lcPoint1.x + lcDx*lcCoefficient1) << " ";

     if (agFlipped) agStream << (lcCorner.y - lcStep*lcPoint1.y - lcDy*lcCoefficient1) << " ";
     else agStream << (lcOffset + lcStep*lcPoint1.y + lcDy*lcCoefficient1) << " ";

     if (agMirrored) agStream << (lcCorner.x - lcStep*lcPoint2.x + lcDx*lcCoefficient2) << " ";
     else agStream << (lcOffset + lcStep*lcPoint2.x - lcDx*lcCoefficient2) << " ";

     if (agFlipped) agStream << (lcCorner.y - lcStep*lcPoint2.y + lcDy*lcCoefficient2);
     else agStream << (lcOffset + lcStep*lcPoint2.y - lcDy*lcCoefficient2);
    }

    agStream << " 0.5 edge" << end_line;
   }

   lcCurrentArc++;
  }

  agStream << "showpage" << end_line << end_line;
  agStream << "% END %=========================================================================";
  agStream << end_line;
 }
}

namespace private_area {}

// X X X  Implementation //-------------------------------------------------------------------------
namespace {}

// I n i t i a l i z e r  Implementation //---------------------------------------------------------
namespace private_area {
 //--------------------------------------------------------------------------------------------Start
 property void clInitializer::start(void) {
  if (atCounter++ == 0) {
   try {
    #include <bpp/modules.hpp> /*NEED*/
    registerStop(this);
    environment::informInitialization(goModuleName);

    goGraphLayoutHeight = 100;
    goGraphLayoutWidth  = 100;

    goGraphLayoutEndFlag   = "<layout_end>";
    goGraphLayoutStartFlag = "<layout_start>";
    goNodeFlag             = "node";


    goEpsHeaderFileName1 = new_object(clString(environment::dataLocation()+fileNameSeparator()
                           +"program"+fileNameSeparator()+GRAPH_LAYOUT_DATA_FILE_1));

    goEpsHeaderFileName2 = new_object(clString(environment::dataLocation()+fileNameSeparator()
                           +"program"+fileNameSeparator()+GRAPH_LAYOUT_DATA_FILE_2));
   }

   initializer_catch;
  }
 }
 //---------------------------------------------------------------------------------------------Stop
 property void clInitializer::stop(void) {
  try {
   environment::informTermination(goModuleName);

   delete_object(goEpsHeaderFileName1);
   delete_object(goEpsHeaderFileName2);
  }

  initializer_catch;
 }
}

// End //-------------------------------------------------------------------------------------------
}