//================================================================================================== // 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 //------------------------------------------------------------------------------------------- } |
|