/*
* $Revision: 2616 $
*
* last checkin:
* $Author: gutwenger $
* $Date: 2012-07-16 15:34:43 +0200 (Mo, 16. Jul 2012) $
***************************************************************/
/** \file
* \brief Implement class ClusterGraphAttributes
*
* \author Karsten Klein
*
* \par License:
* This file is part of the Open Graph Drawing Framework (OGDF).
*
* \par
* Copyright (C)
* See README.txt in the root directory of the OGDF installation for details.
*
* \par
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* Version 2 or 3 as published by the Free Software Foundation;
* see the file LICENSE.txt included in the packaging of this file
* for details.
*
* \par
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* \par
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
* \see http://www.gnu.org/copyleft/gpl.html
***************************************************************/
#include "ClusterGraphAttributes.h"
#include "ClusterArray.h"
#include "../fileformats/GmlParser.h"
#include "../fileformats/OgmlParser.h"
#include
namespace ogdf {
ClusterGraphAttributes::ClusterGraphAttributes(
ClusterGraph& cg,
long initAttributes)
: GraphAttributes(cg.getGraph(), initAttributes | edgeType | nodeType |
nodeGraphics | edgeGraphics), m_clusterTemplate(cg), m_pClusterGraph(&cg)
//we should initialize m__clusterinfo here
{
//should we always fill the cluster infos here?
}//constructor
//reinitialize graph
void ClusterGraphAttributes::init(ClusterGraph &cg, long initAttributes)
{
m_pClusterGraph = &cg;
m_clusterInfo.clear();
//need to initialize GraphAttributes with getGraph()
//we only use parameter initAttributes here in constrast
//to the initialization in the constructor
GraphAttributes::init(cg.getGraph(), initAttributes );
}
//
// calculates the bounding box of the graph including clusters
const DRect ClusterGraphAttributes::boundingBox() const
{
DRect bb = GraphAttributes::boundingBox();
double minx = bb.p1().m_x;
double miny = bb.p1().m_y;
double maxx = bb.p2().m_x;
double maxy = bb.p2().m_y;
cluster c;
forall_clusters(c,*m_pClusterGraph)
{
if(c == m_pClusterGraph->rootCluster())
continue;
double x1 = clusterXPos(c);
double y1 = clusterYPos(c);
double x2 = x1 + clusterWidth(c);
double y2 = y1 + clusterHeight(c);
if (x1 < minx) minx = x1;
if (x2 > maxx) maxx = x2;
if (y1 < miny) miny = y1;
if (y2 > maxy) maxy = y2;
}
return DRect(minx, miny, maxx, maxy);
}
void ClusterGraphAttributes::updateClusterPositions(double boundaryDist)
{
cluster c;
//run through children and nodes and update size accordingly
//we use width, height temporarily to store max values
forall_postOrderClusters(c,*m_pClusterGraph)
{
ListIterator nit = c->nBegin();
ListConstIterator cit = c->cBegin();
//Initialize with first element
if (nit.valid())
{
clusterXPos(c->index()) = m_x[*nit] - m_width[*nit]/2;
clusterYPos(c->index()) = m_y[*nit] - m_height[*nit]/2;
clusterWidth(c->index()) = m_x[*nit] + m_width[*nit]/2;
clusterHeight(c->index()) = m_y[*nit] + m_height[*nit]/2;
nit++;
}
else
{
if (cit.valid())
{
clusterXPos(c->index()) = clusterXPos(*cit);
clusterYPos(c->index()) = clusterYPos(*cit);
clusterWidth(c->index()) = clusterXPos(*cit) + clusterWidth(*cit);
clusterHeight(c->index()) = clusterYPos(*cit) + clusterHeight(*cit);
cit++;
}
else
{
clusterXPos(c->index()) = 0.0;
clusterYPos(c->index()) = 0.0;
clusterWidth(c->index()) = 1.0;
clusterHeight(c->index()) = 1.0;
}
}
//run through elements and update
while (nit.valid())
{
if (clusterXPos(c->index()) > m_x[*nit] - m_width[*nit]/2)
clusterXPos(c->index()) = m_x[*nit] - m_width[*nit]/2;
if (clusterYPos(c->index()) > m_y[*nit] - m_height[*nit]/2)
clusterYPos(c->index()) = m_y[*nit] - m_height[*nit]/2;
if (clusterWidth(c->index()) < m_x[*nit] + m_width[*nit]/2)
clusterWidth(c->index()) = m_x[*nit] + m_width[*nit]/2;
if (clusterHeight(c->index()) < m_y[*nit] + m_height[*nit]/2)
clusterHeight(c->index()) = m_y[*nit] + m_height[*nit]/2;
nit++;
}
while (cit.valid())
{
if (clusterXPos(c->index()) > clusterXPos((*cit)->index()))
clusterXPos(c->index()) = clusterXPos((*cit)->index());
if (clusterYPos(c->index()) > clusterYPos((*cit)->index()))
clusterYPos(c->index()) = clusterYPos((*cit)->index());
if (clusterWidth(c->index()) < clusterXPos((*cit)->index()) + clusterWidth((*cit)->index()))
clusterWidth(c->index()) = clusterXPos((*cit)->index()) + clusterWidth((*cit)->index());
if (clusterHeight(c->index()) < clusterYPos((*cit)->index()) + clusterHeight((*cit)->index()))
clusterHeight(c->index()) = clusterYPos((*cit)->index()) + clusterHeight((*cit)->index());
cit++;
}
clusterXPos(c->index()) -= boundaryDist;
clusterYPos(c->index()) -= boundaryDist;
clusterWidth(c->index()) = clusterWidth(c->index()) - clusterXPos(c->index()) + boundaryDist;
clusterHeight(c->index()) = clusterHeight(c->index()) - clusterYPos(c->index()) + boundaryDist;
}
}
void ClusterGraphAttributes::writeGML(const char *fileName)
{
ofstream os(fileName);
writeGML(os);
}
void ClusterGraphAttributes::writeGML(ostream &os)
{
NodeArray nId(*m_pGraph);
int nextId = 0;
os.setf(ios::showpoint);
GraphAttributes::writeGML(os);
// set index string for cluster entries
node v;
forall_nodes(v,*m_pGraph)
{
nId[v] = nextId++;
}
// output the cluster information
String indent = "\0";
nextId = 1;
writeGraphWinCluster(os, nId, nextId,
m_pClusterGraph->rootCluster(), indent);
}
// recursively write the cluster structure in GML
void ClusterGraphAttributes::writeCluster(
ostream &os,
NodeArray &nId,
ClusterArray & cId,
int &nextId,
cluster c,
String indent)
{
String newindent = indent;
newindent += " ";
os << indent << "cluster [\n";
os << indent << " id " << (cId[c] = nextId++) << "\n";
ListConstIterator it;
for (it = c->cBegin(); it.valid(); it++)
writeCluster(os,nId,cId,nextId,*it,newindent);
ListConstIterator itn;
for (itn = c->nBegin(); itn.valid(); itn++)
os << indent << " node " << nId[*itn] << "\n";
os << indent << "]\n"; // cluster
}
// recursively write the cluster structure in GraphWin GML
void ClusterGraphAttributes::writeGraphWinCluster(
ostream &os,
NodeArray &nId,
int &nextId,
cluster c,
String indent
)
{
String newindent = indent;
newindent += " ";
if (c == m_pClusterGraph->rootCluster())
os << indent << "rootcluster [\n";
else
{
os << indent << "cluster [\n";
os << indent << " id " << c->index() << "\n";
const String &templStr = m_clusterTemplate[c];
if(templStr.length() > 0) {
// GDE extension: Write cluster template and custom attribute
os << " template ";
writeLongString(os, templStr);
os << "\n";
os << " label ";
writeLongString(os, clusterLabel(c));
os << "\n";
} else {
os << indent << " label \"" << clusterLabel(c) << "\"\n";
}
os << indent << " graphics [\n";
double shiftPos;
shiftPos = clusterYPos(c->index());
os << indent << " x " << clusterXPos(c->index()) << "\n";
os << indent << " y " << shiftPos/*clusterYPos(c->index())*/ << "\n";
os << indent << " width " << clusterWidth(c->index()) << "\n";
os << indent << " height " << clusterHeight(c->index()) << "\n";
os << indent << " fill \"" << clusterFillColor(c->index()) << "\"\n";
os << indent << " pattern " << clusterFillPattern(c->index()) << "\n";
//border line styles
os << indent << " color \"" << clusterColor(c) << "\"\n";
os << indent << " lineWidth " << clusterLineWidth(c) << "\n";
//save space by defaulting
if (clusterLineStyle(c) != esSolid)
os << indent << " stipple " << clusterLineStyle(c) << "\n";
os << indent << " style \"rectangle\"\n";
os << indent << " ]\n"; //graphics
}
// write contained clusters
ListConstIterator it;
for (it = c->cBegin(); it.valid(); it++)
writeGraphWinCluster(os, nId, nextId, *it, newindent);
// write contained nodes
ListConstIterator itn;
for (itn = c->nBegin(); itn.valid(); itn++)
os << indent << "vertex \"" << nId[*itn] << "\"\n";
os << indent << "]\n"; // cluster
}
const char NEWLINE('\n'); // newline character
const char INDENTCHAR(' '); // indent character
const int INDENTSIZE(2); // indent size
class omani
{
int m_n;
ostream& (*m_f)(ostream&, int);
public:
omani(ostream& (*f)(ostream&, int), int n) : m_n(n), m_f(f) { }
friend ostream& operator<<(ostream& os, omani man) {
return man.m_f(os, man.m_n);
}
};
ostream& padN(ostream& os, int depth)
{
int n = INDENTSIZE * depth;
for( ; n > 0; --n)
os.put(INDENTCHAR);
return os;
}
omani ind(int depth)
{
return omani(&padN, depth);
}
ostream &ind(ostream &os, int depth)
{
int n = INDENTSIZE * depth;
for( ; n > 0; --n)
os.put(INDENTCHAR);
return os;
}
void ClusterGraphAttributes::writeOGML(const char *fileName)//, GraphConstraints &GC)
{
ofstream os(fileName);
writeOGML(os);//, GC);
}
void ClusterGraphAttributes::writeOGML(ostream & os) //, GraphConstraints & GC)
{
int labelId = 0; // new ID of current label
int pointId = 0; // new ID of current point
int indentDepth = 0; // main indent depth
int indentDepthS = 4; // indent depth for styles block
std::ostringstream osS; // string output stream for buffering the styles of the handled elements
std::ostringstream osC; // string output stream for buffering constraints
// CONFIGURING OUTPUT STREAMS
os.setf(ios::showpoint);
os.precision(10);
osS.setf(ios::showpoint);
osS.precision(10);
osC.setf(ios::showpoint);
osC.precision(10);
// XML DECLARATION AND OGML TAG
os << "" << NEWLINE; // Latin-1
// Simple version
os << "" << NEWLINE;
// Strict version
// os << "" << NEWLINE;
// WRITING GRAPH BLOCK
os << ind(++indentDepth) << "" << NEWLINE;
// WRITING STRUCTURE BLOCK
os << ind(++indentDepth) << "" << NEWLINE;
// recursive handling of clusters
writeClusterOGML(os, osS, labelId, m_pClusterGraph->rootCluster(), ++indentDepth, indentDepthS);
// handling of edges
edge e;
forall_edges(e, *m_pGraph)
{
// EDGE STRUCTURE
os << ind(indentDepth) << "index() << "\">" << NEWLINE;
++indentDepth;
// handling of label (if exists)
if (attributes() & edgeLabel) {
os << ind(indentDepth) << "" << NEWLINE;
}
os << ind(indentDepth) << "" << NEWLINE; // edge
// EDGE LAYOUT
if (attributes() & edgeGraphics || attributes() & edgeColor || attributes() & edgeStyle)
{
osS << ind(indentDepthS) << "index() << "\">" << NEWLINE;
// handling of style information
if(attributes() & edgeStyle || attributes() & edgeColor)
{
osS << ind(indentDepthS+1) << "" << NEWLINE;
} else {
osS << "/>" << NEWLINE;
}
}
// TODO review the handling of edge arrows
if(attributes() & edgeArrow)
{
++indentDepthS;
switch(arrowEdge(e)) {
case GraphAttributes::none:
osS << ind(indentDepthS) << "" << NEWLINE;
osS << ind(indentDepthS) << "" << NEWLINE;
break;
case GraphAttributes::last:
osS << ind(indentDepthS) << "" << NEWLINE;
osS << ind(indentDepthS) << "" << NEWLINE;
break;
case GraphAttributes::first:
osS << ind(indentDepthS) << "" << NEWLINE;
osS << ind(indentDepthS) << "" << NEWLINE;
break;
case GraphAttributes::both:
osS << ind(indentDepthS) << "" << NEWLINE;
osS << ind(indentDepthS) << "" << NEWLINE;
break;
case GraphAttributes::undefined:
// do nothing
break;
default:
// do nothing
break;
}
--indentDepthS;
}
// handling of points
const DPolyline &dpl = m_bends[e];
if (!dpl.empty()) {
++indentDepthS;
// handle source
node v = e->source();
if(dpl.front().m_x < m_x[v] - m_width[v]/2 ||
dpl.front().m_x > m_x[v] + m_width[v]/2 ||
dpl.front().m_y < m_y[v] - m_height[v]/2 ||
dpl.front().m_y > m_y[v] + m_height[v]/2) {
osS << ind(indentDepthS) << "source()] << "\" y=\"" << m_y[e->source()] << "\"/>" << NEWLINE;
}
// handle points
ListConstIterator it;
for(it = dpl.begin(); it.valid(); ++it) {
osS << ind(indentDepthS) << "" << NEWLINE;
}
// handle target
v = e->target();
if(dpl.back().m_x < m_x[v] - m_width[v]/2 ||
dpl.back().m_x > m_x[v] + m_width[v]/2 ||
dpl.back().m_y < m_y[v] - m_height[v]/2 ||
dpl.back().m_y > m_y[v] + m_height[v]/2) {
osS << ind(indentDepthS) << "target()] << "\" y=\"" << m_y[e->target()] << "\"/>" << NEWLINE;
}
--indentDepthS;
}
osS << ind(indentDepthS) << "" << NEWLINE;
}
}
--indentDepth;
os << ind(indentDepth) << "" << NEWLINE;
// WRITING LAYOUT BLOCK
os << ind(indentDepth) << "" << NEWLINE;
// WRITING STYLES
++indentDepth;
os << ind(indentDepth) << "" << NEWLINE;
os << osS.str();
os << ind(indentDepth) << "" << NEWLINE;
// 2.2.2) WRITING CONSTRAINTS
// No constraint handling so far in OGDF
/*
List * csList = GC.getConstraints();
ListConstIterator it;
int constID = 0;
if (csList->size() > 0) {
os << indent << "" << NEWLINE;
for (it = csList->begin(); it.valid(); ++it) {
(*it)->storeToOgml(constID++, os, indentDepth + indentDepthS + 1);
}
os << indent << "" << NEWLINE;
cout << "Constraints written...\n" << flush;
}
*/
--indentDepth;
os << ind(indentDepth) << "" << NEWLINE;
--indentDepth;
os << ind(indentDepth) << "" << NEWLINE;
os << "";
}
// recursively write the cluster structure in OGML
void ClusterGraphAttributes::writeClusterOGML(
ostream & os,
std::ostringstream & osS,
int & nextLabelId,
cluster clust,
int & indentDepth,
int indentDepthS)
{
// we handle all cluster except the root cluster
if (clust != m_pClusterGraph->rootCluster()) {
/* cluster structure infos */
os << ind(indentDepth) << "index() << "\">" << NEWLINE;
/*
* TODO What are cluster templates and how can/should they be handled?
*
const String &templStr = m_clusterTemplate[cluster];
if(templStr.length() > 0) {
// GDE extension: Write cluster template and custom attribute
os << "template ";
writeLongString(os, templStr);
os << "\n";
os << "label ";
writeLongString(os, clusterLabel(cluster));
os << "\n";
} else {
*/
++indentDepth;
os << ind(indentDepth) << "" << NEWLINE;
}
// we handle the contained nodes first
ListConstIterator itn;
for (itn = clust->nBegin(); itn.valid(); ++itn)
{
node v = *itn;
// node structure infos
os << ind(indentDepth) << "index() << "\">" << NEWLINE;
// handling of label (if exists)
if (attributes() & nodeLabel) {
os << ind(indentDepth+1) << "" << NEWLINE;
}
os << ind(indentDepth) << "" << NEWLINE;
// node layout infos
osS << ind(indentDepthS) << "index() << "\">" << NEWLINE;
++indentDepthS;
osS << ind(indentDepthS) << "" << NEWLINE;
osS << ind(indentDepthS) << "" << NEWLINE;
if(attributes() & nodeColor || attributes() & nodeStyle) {
// fill-tag
osS << ind(indentDepthS) << " 0) {
osS << " color=\"" << m_nodeColor[v] << "\"";
}
}
if (attributes() & nodeStyle) {
// pattern- and patternColor-attribute of fill-tag (closing)
osS << " pattern=\"" << brushPatternToOGML(m_nodePattern[v]) << "\" patternColor=\"#000000\"/>" << NEWLINE;
// line-tag
osS << ind(indentDepthS) << "" << NEWLINE;
} else {
// closing fill-tag
osS << "/>" << NEWLINE;
}
}
--indentDepthS;
osS << ind(indentDepthS) << "" << NEWLINE;
}//for clusters
// now we recursively handle the contained clusters
ListConstIterator it;
for (it = clust->cBegin(); it.valid(); ++it) {
writeClusterOGML(os, osS, nextLabelId, *it, indentDepth, indentDepthS);
}
// we handle all clusters except the root cluster
if (clust != m_pClusterGraph->rootCluster()) {
--indentDepth;
os << ind(indentDepth) << "" << NEWLINE;
// cluster layout infos
osS << ind(indentDepthS) << "index() << "\">" << NEWLINE;
++indentDepthS;
osS << ind(indentDepthS) << "" << NEWLINE;
osS << ind(indentDepthS) << "" << NEWLINE;
if(clusterFillColor(clust).length() > 0) {
osS << ind(indentDepthS) << "" << NEWLINE;
}
osS << ind(indentDepthS) << "" << NEWLINE;
--indentDepthS;
osS << ind(indentDepthS) << "" << NEWLINE;
}
}
//++++++++++++++++++++++++++++++++++++++++++++
//reading graph, attributes, cluster structure
bool ClusterGraphAttributes::readClusterGML(
const char* fileName,
ClusterGraph& CG,
Graph& G)
{
ifstream is(fileName);
if (!is)
return false; // couldn't open file
return readClusterGML(is, CG, G);
}
bool ClusterGraphAttributes::readClusterGML(
istream& is,
ClusterGraph& CG,
Graph& G)
{
bool result;
GmlParser gml(is);
if (gml.error())
return false;
result = gml.read(G,*this);
if (!result) return false;
return readClusterGraphGML(CG, G, gml);
}
//read Cluster Graph with Attributes, base graph G, from fileName
bool ClusterGraphAttributes::readClusterGraphGML(
const char* fileName,
ClusterGraph& CG,
Graph& G,
GmlParser& gml)
{
ifstream is(fileName);
if (!is)
return false; // couldn't open file
return readClusterGraphGML(CG, G, gml);
}
//read from input stream
bool ClusterGraphAttributes::readClusterGraphGML(
ClusterGraph& CG,
Graph& G,
GmlParser& gml)
{
return gml.readAttributedCluster(G, CG, *this);
}
// read Cluster Graph from OGML file
bool ClusterGraphAttributes::readClusterGraphOGML(
const char* fileName,
ClusterGraph& CG,
Graph& G)
{
ifstream is(fileName);
if (!is)
return false;
OgmlParser op;
return op.read(fileName, G, CG, *this);
};
ostream &operator<<(ostream &os, ogdf::cluster c)
{
if (c) os << c->index(); else os << "nil";
return os;
}
} // end namespace ogdf