/* * $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) << "source()->index() << "\"/>" << NEWLINE; os << ind(indentDepth) << "target()->index() << "\"/>" << NEWLINE; /* * TODO find solution for handling the edge types (app specific data?) * * os << "GENERALIZATION=\"" << (m_eType[e]==Graph::generalization?1:0) << "\">" << delimiter; */ 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