void MultilevelGraph::prepareGraphAttributes(GraphAttributes &GA) const { long additionalAttributes = 0; if (!GA.has(GraphAttributes::edgeDoubleWeight)) { additionalAttributes |= GraphAttributes::edgeDoubleWeight; } if (!GA.has(GraphAttributes::nodeWeight)) { additionalAttributes |= GraphAttributes::nodeWeight; } GA.addAttributes(additionalAttributes); }
void StressMinimization::call(GraphAttributes& GA) { const Graph& G = GA.constGraph(); // if the graph has at most one node nothing to do if (G.numberOfNodes() <= 1) { // make it exception save for(node v : G.nodes) { GA.x(v) = 0; GA.y(v) = 0; } return; } if (m_componentLayout && !isConnected(G)) { OGDF_THROW(PreconditionViolatedException); return; } NodeArray<NodeArray<double> > shortestPathMatrix(G); NodeArray<NodeArray<double> > weightMatrix(G); initMatrices(G, shortestPathMatrix, weightMatrix); // if the edge costs are defined by the attribute copy it to an array and // construct the proper shortest path matrix if (m_hasEdgeCostsAttribute) { if (!GA.has(GraphAttributes::edgeDoubleWeight)) { OGDF_THROW(PreconditionViolatedException); return; } m_avgEdgeCosts = dijkstra_SPAP(GA, shortestPathMatrix); // compute shortest path all pairs } else { m_avgEdgeCosts = m_edgeCosts; bfs_SPAP(G, shortestPathMatrix, m_edgeCosts); } call(GA, shortestPathMatrix, weightMatrix); }
void PivotMDS::pivotMDSLayout(GraphAttributes& GA) { const Graph& G = GA.constGraph(); bool use3D = GA.has(GraphAttributes::threeD) && DIMENSION_COUNT > 2; const int n = G.numberOfNodes(); // trivial cases if (n == 0) return; if (n == 1) { node v1 = G.firstNode(); GA.x(v1) = 0.0; GA.y(v1) = 0.0; if (use3D) GA.z(v1) = 0.0; return; } // check whether the graph is a path or not const node head = getRootedPath(G); if (head != nullptr) { doPathLayout(GA, head); } else { Array<Array<double> > pivDistMatrix; // compute the pivot matrix getPivotDistanceMatrix(GA, pivDistMatrix); // center the pivot matrix centerPivotmatrix(pivDistMatrix); // init the coordinate matrix Array<Array<double> > coord(DIMENSION_COUNT); for (int i = 0; i < coord.size(); i++) { coord[i].init(n); } // init the eigen values array Array<double> eVals(DIMENSION_COUNT); singularValueDecomposition(pivDistMatrix, coord, eVals); // compute the correct aspect ratio for (int i = 0; i < coord.size(); i++) { eVals[i] = sqrt(eVals[i]); for (int j = 0; j < n; j++) { coord[i][j] *= eVals[i]; } } // set the new positions to the graph int i = 0; for (node v : G.nodes) { GA.x(v) = coord[0][i]; GA.y(v) = coord[1][i]; if (use3D){ GA.z(v) = coord[2][i];//cout << coord[2][i] << "\n"; } ++i; } } }
void StressMinimization::minimizeStress( GraphAttributes& GA, NodeArray<NodeArray<double> >& shortestPathMatrix, NodeArray<NodeArray<double> >& weightMatrix) { const Graph& G = GA.constGraph(); int numberOfPerformedIterations = 0; double prevStress = numeric_limits<double>::max(); double curStress = numeric_limits<double>::max(); if (m_terminationCriterion == STRESS) { curStress = calcStress(GA, shortestPathMatrix, weightMatrix); } NodeArray<double> newX; NodeArray<double> newY; NodeArray<double> newZ; if (m_terminationCriterion == POSITION_DIFFERENCE) { newX.init(G); newY.init(G); if (GA.has(GraphAttributes::threeD)) newZ.init(G); } do { if (m_terminationCriterion == POSITION_DIFFERENCE) { if (GA.has(GraphAttributes::threeD)) copyLayout(GA, newX, newY, newZ); else copyLayout(GA, newX, newY); } nextIteration(GA, shortestPathMatrix, weightMatrix); if (m_terminationCriterion == STRESS) { prevStress = curStress; curStress = calcStress(GA, shortestPathMatrix, weightMatrix); } } while (!finished(GA, ++numberOfPerformedIterations, newX, newY, prevStress, curStress)); Logger::slout() << "Iteration count:\t" << numberOfPerformedIterations << "\tStress:\t" << calcStress(GA, shortestPathMatrix, weightMatrix) << endl; }
void PivotMDS::call(GraphAttributes& GA) { if (!isConnected(GA.constGraph())) { OGDF_THROW_PARAM(PreconditionViolatedException,pvcConnected); return; } if (m_hasEdgeCostsAttribute && !GA.has(GraphAttributes::edgeDoubleWeight)) { OGDF_THROW(PreconditionViolatedException); return; } pivotMDSLayout(GA); }
static void write_ogml_graph_edges(const GraphAttributes &A, ostream &os) { const Graph &G = A.constGraph(); for(edge e : G.edges) { GraphIO::indent(os,3) << "<edge id=\"e" << e->index() << "\">\n"; if (A.has(GraphAttributes::edgeLabel)) { GraphIO::indent(os,4) << "<label id=\"le" << e->index() << "\">\n"; GraphIO::indent(os,5) << "<content>" << formatLabel(A.label(e)) << "</content>\n"; GraphIO::indent(os,4) << "</label>\n"; } GraphIO::indent(os,4) << "<source idRef=\"n" << e->source()->index() << "\" />\n"; GraphIO::indent(os,4) << "<target idRef=\"n" << e->target()->index() << "\" />\n"; GraphIO::indent(os,3) << "</edge>\n"; } }
// write graph structure with attributes static void write_ogml_graph(const GraphAttributes &A, ostream &os) { const Graph &G = A.constGraph(); GraphIO::indent(os,2) << "<structure>\n"; for(node v : G.nodes) { GraphIO::indent(os,3) << "<node id=\"n" << v->index() << "\">\n"; if (A.has(GraphAttributes::nodeLabel)) { GraphIO::indent(os,4) << "<label id=\"ln" << v->index() << "\">\n"; GraphIO::indent(os,5) << "<content>" << formatLabel(A.label(v)) << "</content>\n"; GraphIO::indent(os,4) << "</label>\n"; } GraphIO::indent(os,3) << "</node>\n"; } write_ogml_graph_edges(A, os); GraphIO::indent(os,2) << "</structure>\n"; }
double StressMinimization::calcStress( const GraphAttributes& GA, NodeArray<NodeArray<double> >& shortestPathMatrix, NodeArray<NodeArray<double> >& weightMatrix) { double stress = 0; for (node v = GA.constGraph().firstNode(); v != nullptr; v = v->succ()) { for (node w = v->succ(); w != nullptr; w = w->succ()) { double xDiff = GA.x(v) - GA.x(w); double yDiff = GA.y(v) - GA.y(w); double zDiff = 0.0; if (GA.has(GraphAttributes::threeD)) { zDiff = GA.z(v) - GA.z(w); } double dist = sqrt(xDiff * xDiff + yDiff * yDiff + zDiff * zDiff); if (dist != 0) { stress += weightMatrix[v][w] * (shortestPathMatrix[v][w] - dist) * (shortestPathMatrix[v][w] - dist);// } } } return stress; }
static void write_ogml_layout_nodes_edges(const GraphAttributes &A, ostream &os) { const Graph &G = A.constGraph(); if (A.has(GraphAttributes::nodeGraphics | GraphAttributes::nodeStyle)) { for(node v : G.nodes) { GraphIO::indent(os,4) << "<nodeStyle idRef=\"n" << v->index() << "\">\n"; if(A.has(GraphAttributes::nodeGraphics)) { GraphIO::indent(os,5) << "<location x=\"" << A.x(v)-0.5*A.width(v) << "\" y=\""<< A.y(v)-0.5*A.height(v) << "\" />\n"; GraphIO::indent(os,5) << "<shape type=\""; switch (A.shape(v)) { case shRect: os << "rect"; break; case shRoundedRect: os << "roundedRect"; break; case shEllipse: os << "ellipse"; break; case shTriangle: os << "triangle"; break; case shPentagon: os << "pentagon"; break; case shHexagon: os << "hexagon"; break; case shOctagon: os << "octagon"; break; case shRhomb: os << "rhomb"; break; case shTrapeze: os << "trapeze"; break; case shParallelogram: os << "parallelogram"; break; case shInvTriangle: os << "invTriangle"; break; case shInvTrapeze: os << "invTrapeze"; break; case shInvParallelogram: os << "invParallelogram"; break; case shImage: os << "image"; break; } os << "\" width=\"" << A.width(v) << "\" height=\"" << A.height(v) << "\" />\n"; } if(A.has(GraphAttributes::nodeStyle)) { // fill-tag GraphIO::indent(os,5) << "<fill"; // color-attribute of fill-tag os << " color=\"" << A.fillColor(v) << "\""; // pattern- and patternColor-attribute of fill-tag (closing) os << " pattern=\"" << fillPatternToOGML(A.fillPattern(v)) << "\" patternColor=\"" << A.fillBgColor(v) << "\" />\n"; // line-tag GraphIO::indent(os,5) << "<line type=\"" << edgeStyleToOGML(A.strokeType(v)) << "\" width=\"" << A.strokeWidth(v) << "\"" << " color=\"" << A.strokeColor(v) << "\""; // closing fill-tag os << " />\n"; } GraphIO::indent(os,4) << "</nodeStyle>\n"; } } if (A.has(GraphAttributes::edgeGraphics | GraphAttributes::edgeStyle)) { int pointId = 0; for(edge e : G.edges) { GraphIO::indent(os,4) << "<edgeStyle idRef=\"e" << e->index() << "\">\n"; if(A.has(GraphAttributes::edgeStyle)) { GraphIO::indent(os,5) << "<line "; if (A.has(GraphAttributes::edgeStyle)) { os << "type=\"" << edgeStyleToOGML(A.strokeType(e)) << "\" width=\"" << A.strokeWidth(e) << "\" "; os << "color=\"" << A.strokeColor(e) << "\" />\n"; } else { os << " />\n"; } } // TODO review the handling of edge arrows if(A.has(GraphAttributes::edgeArrow)) { switch(A.arrowType(e)) { case eaNone: GraphIO::indent(os,5) << "<sourceStyle type=\"none\" color=\"#000000\" size=\"1\" />\n"; GraphIO::indent(os,5) << "<targetStyle type=\"none\" color=\"#000000\" size=\"1\" />\n"; break; case eaLast: GraphIO::indent(os,5) << "<sourceStyle type=\"none\" color=\"#000000\" size=\"1\" />\n"; GraphIO::indent(os,5) << "<targetStyle type=\"arrow\" color=\"#000000\" size=\"1\" />\n"; break; case eaFirst: GraphIO::indent(os,5) << "<sourceStyle type=\"arrow\" color=\"#000000\" size=\"1\" />\n"; GraphIO::indent(os,5) << "<targetStyle type=\"none\" color=\"#000000\" size=\"1\" />\n"; break; case eaBoth: GraphIO::indent(os,5) << "<sourceStyle type=\"arrow\" color=\"#000000\" size=\"1\" />\n"; GraphIO::indent(os,5) << "<targetStyle type=\"arrow\" color=\"#000000\" size=\"1\" />\n"; break; case eaUndefined: // do nothing break; default: // do nothing break; } } // handling of points // TODO: Revise for new OGML specification const DPolyline &dpl = A.bends(e); if (!dpl.empty()) { // handle source node v = e->source(); if(dpl.front().m_x < A.x(v) - A.width(v)/2 || dpl.front().m_x > A.x(v) + A.width(v)/2 || dpl.front().m_y < A.y(v) - A.height(v)/2 || dpl.front().m_y > A.y(v) + A.height(v)/2) { GraphIO::indent(os,5) << "<point id=\"p" << pointId++ << "\" x=\"" << A.x(e->source()) << "\" y=\"" << A.y(e->source()) << "\" />\n"; } // handle points for(const DPoint &dp : dpl) { GraphIO::indent(os,5) << "<point id=\"p" << pointId++ << "\" x=\"" << dp.m_x << "\" y=\"" << dp.m_y << "\" />\n"; } // handle target v = e->target(); if(dpl.back().m_x < A.x(v) - A.width(v)/2 || dpl.back().m_x > A.x(v) + A.width(v)/2 || dpl.back().m_y < A.y(v) - A.height(v)/2 || dpl.back().m_y > A.y(v) + A.height(v)/2) { GraphIO::indent(os,5) << "<point id=\"p" << pointId++ << "\" x=\"" << A.x(e->target()) << "\" y=\"" << A.y(e->target()) << "\" />\n"; } } GraphIO::indent(os,4) << "</edgeStyle>\n"; } } }
void StressMinimization::nextIteration( GraphAttributes& GA, NodeArray<NodeArray<double> >& shortestPathMatrix, NodeArray<NodeArray<double> >& weights) { const Graph& G = GA.constGraph(); for (node v : G.nodes) { double newXCoord = 0.0; double newYCoord = 0.0; double newZCoord = 0.0; double& currXCoord = GA.x(v); double& currYCoord = GA.y(v); double totalWeight = 0; for (node w : G.nodes) { if (v == w) { continue; } // calculate euclidean distance between both points double xDiff = currXCoord - GA.x(w); double yDiff = currYCoord - GA.y(w); double zDiff = (GA.has(GraphAttributes::threeD)) ? GA.z(v) - GA.z(w) : 0.0; double euclideanDist = sqrt(xDiff * xDiff + yDiff * yDiff + zDiff * zDiff); // get the weight double weight = weights[v][w]; // get the desired distance double desDistance = shortestPathMatrix[v][w]; // reset the voted x coordinate // if x is not fixed if (!m_fixXCoords) { double voteX = GA.x(w); if (euclideanDist != 0) { // calc the vote voteX += desDistance * (currXCoord - voteX) / euclideanDist; } // add the vote newXCoord += weight * voteX; } // reset the voted y coordinate // y is not fixed if (!m_fixYCoords) { double voteY = GA.y(w); if (euclideanDist != 0) { // calc the vote voteY += desDistance * (currYCoord - voteY) / euclideanDist; } newYCoord += weight * voteY; } if (GA.has(GraphAttributes::threeD)) { // reset the voted z coordinate // z is not fixed if (!m_fixZCoords) { double voteZ = GA.z(w); if (euclideanDist != 0) { // calc the vote voteZ += desDistance * (GA.z(v) - voteZ) / euclideanDist; } newZCoord += weight * voteZ; } } // sum up the weights totalWeight += weight; } // update the positions if (totalWeight != 0) { if (!m_fixXCoords) { currXCoord = newXCoord / totalWeight; } if (!m_fixYCoords) { currYCoord = newYCoord / totalWeight; } if (GA.has(GraphAttributes::threeD)) { if (!m_fixZCoords) { GA.z(v) = newZCoord / totalWeight; } } } } }
bool GmlParser::read(Graph &G, GraphAttributes &AG) { OGDF_ASSERT(&G == &(AG.constGraph())) G.clear(); int minId = m_mapToNode.low(); int maxId = m_mapToNode.high(); int notDefined = minId-1; //indicates not defined id key HashArray<string,Shape> strToShape(shRect); strToShape["rectangle"] = shRect; strToShape["rect"] = shRect; strToShape["roundedRect"] = shRoundedRect; strToShape["oval"] = shEllipse; strToShape["ellipse"] = shEllipse; strToShape["triangle"] = shTriangle; strToShape["pentagon"] = shPentagon; strToShape["hexagon"] = shHexagon; strToShape["octagon"] = shOctagon; strToShape["rhomb"] = shRhomb; strToShape["trapeze"] = shTrapeze; strToShape["parallelogram"] = shParallelogram; strToShape["invTriangle"] = shInvTriangle; strToShape["invTrapeze"] = shInvTrapeze; strToShape["invParallelogram"] = shInvParallelogram; strToShape["image"] = shImage; DPolyline bends; GmlObject *son = m_graphObject->m_pFirstSon; for(; son; son = son->m_pBrother) { switch(id(son)) { case nodePredefKey: { if (son->m_valueType != gmlListBegin) break; // set attributes to default values int vId = notDefined; double x = 0, y = 0, w = 0, h = 0; string label; string templ; string fill; // the fill color attribute string line; // the line color attribute string shape; //the shape type float lineWidth = 1.0f; //node line width int pattern = 1; //node brush pattern int stipple = 1; //line style pattern int weight = 0; // node weight // read all relevant attributes GmlObject *nodeSon = son->m_pFirstSon; for(; nodeSon; nodeSon = nodeSon->m_pBrother) { switch(id(nodeSon)) { case idPredefKey: if(nodeSon->m_valueType != gmlIntValue) break; vId = nodeSon->m_intValue; break; case graphicsPredefKey: { if (nodeSon->m_valueType != gmlListBegin) break; GmlObject *graphicsObject = nodeSon->m_pFirstSon; for(; graphicsObject; graphicsObject = graphicsObject->m_pBrother) { switch(id(graphicsObject)) { case xPredefKey: if(graphicsObject->m_valueType != gmlDoubleValue) break; x = graphicsObject->m_doubleValue; break; case yPredefKey: if(graphicsObject->m_valueType != gmlDoubleValue) break; y = graphicsObject->m_doubleValue; break; case wPredefKey: if(graphicsObject->m_valueType != gmlDoubleValue) break; w = graphicsObject->m_doubleValue; break; case hPredefKey: if(graphicsObject->m_valueType != gmlDoubleValue) break; h = graphicsObject->m_doubleValue; break; case fillPredefKey: if(graphicsObject->m_valueType != gmlStringValue) break; fill = graphicsObject->m_stringValue; break; case linePredefKey: if(graphicsObject->m_valueType != gmlStringValue) break; line = graphicsObject->m_stringValue; break; case lineWidthPredefKey: if(graphicsObject->m_valueType != gmlDoubleValue) break; lineWidth = (float)graphicsObject->m_doubleValue; break; case typePredefKey: if(graphicsObject->m_valueType != gmlStringValue) break; shape = graphicsObject->m_stringValue; break; case patternPredefKey: //fill style if(graphicsObject->m_valueType != gmlIntValue) break; pattern = graphicsObject->m_intValue; case stipplePredefKey: //line style if(graphicsObject->m_valueType != gmlIntValue) break; stipple = graphicsObject->m_intValue; } } break; } case templatePredefKey: if (nodeSon->m_valueType != gmlStringValue) break; templ = nodeSon->m_stringValue; break; case labelPredefKey: if (nodeSon->m_valueType != gmlStringValue) break; label = nodeSon->m_stringValue; break; case edgeWeightPredefKey: //sic! if (nodeSon->m_valueType != gmlIntValue) break; weight = nodeSon->m_intValue; break; } } // check if everything required is defined correctly if (vId == notDefined) { setError("node id not defined"); return false; } // create new node if necessary and assign attributes if (m_mapToNode[vId] == nullptr) m_mapToNode[vId] = G.newNode(); node v = m_mapToNode[vId]; if (AG.has(GraphAttributes::nodeGraphics)) { AG.x(v) = x; AG.y(v) = y; AG.width (v) = w; AG.height(v) = h; AG.shape(v) = strToShape[shape]; } if (AG.has(GraphAttributes::nodeLabel)) AG.label(m_mapToNode[vId]) = label; if (AG.has(GraphAttributes::nodeTemplate)) AG.templateNode(m_mapToNode[vId]) = templ; if (AG.has(GraphAttributes::nodeId)) AG.idNode(m_mapToNode[vId]) = vId; if (AG.has(GraphAttributes::nodeWeight)) AG.weight(m_mapToNode[vId]) = weight; if (AG.has(GraphAttributes::nodeStyle)) { AG.fillColor(m_mapToNode[vId]) = fill; AG.strokeColor(m_mapToNode[vId]) = line; AG.setFillPattern(m_mapToNode[vId], intToFillPattern(pattern)); AG.setStrokeType(m_mapToNode[vId], intToStrokeType(stipple)); AG.strokeWidth(m_mapToNode[vId]) = lineWidth; } }//node //Todo: line style set stipple value break; case edgePredefKey: { string arrow; // the arrow type attribute string fill; //the color fill attribute int stipple = 1; //the line style float lineWidth = 1.0f; double edgeWeight = 1.0; int subGraph = 0; //edgeSubGraphs attribute string label; // label attribute if (son->m_valueType != gmlListBegin) break; // set attributes to default values int sourceId = notDefined, targetId = notDefined; Graph::EdgeType umlType = Graph::association; // read all relevant attributes GmlObject *edgeSon = son->m_pFirstSon; for(; edgeSon; edgeSon = edgeSon->m_pBrother) { switch(id(edgeSon)) { case sourcePredefKey: if (edgeSon->m_valueType != gmlIntValue) break; sourceId = edgeSon->m_intValue; break; case targetPredefKey: if (edgeSon->m_valueType != gmlIntValue) break; targetId = edgeSon->m_intValue; break; case subGraphPredefKey: if (edgeSon->m_valueType != gmlIntValue) break; subGraph = edgeSon->m_intValue; break; case labelPredefKey: if (edgeSon->m_valueType != gmlStringValue) break; label = edgeSon->m_stringValue; break; case graphicsPredefKey: { if (edgeSon->m_valueType != gmlListBegin) break; GmlObject *graphicsObject = edgeSon->m_pFirstSon; for(; graphicsObject; graphicsObject = graphicsObject->m_pBrother) { if(id(graphicsObject) == LinePredefKey && graphicsObject->m_valueType == gmlListBegin) { readLineAttribute(graphicsObject->m_pFirstSon,bends); } if(id(graphicsObject) == arrowPredefKey && graphicsObject->m_valueType == gmlStringValue) arrow = graphicsObject->m_stringValue; if(id(graphicsObject) == fillPredefKey && graphicsObject->m_valueType == gmlStringValue) fill = graphicsObject->m_stringValue; if (id(graphicsObject) == stipplePredefKey && //line style graphicsObject->m_valueType == gmlIntValue) stipple = graphicsObject->m_intValue; if (id(graphicsObject) == lineWidthPredefKey && //line width graphicsObject->m_valueType == gmlDoubleValue) lineWidth = (float)graphicsObject->m_doubleValue; if (id(graphicsObject) == edgeWeightPredefKey && graphicsObject->m_valueType == gmlDoubleValue) edgeWeight = graphicsObject->m_doubleValue; }//for graphics } case generalizationPredefKey: if (edgeSon->m_valueType != gmlIntValue) break; umlType = (edgeSon->m_intValue == 0) ? Graph::association : Graph::generalization; break; } } // check if everything required is defined correctly if (sourceId == notDefined || targetId == notDefined) { setError("source or target id not defined"); return false; } else if (sourceId < minId || maxId < sourceId || targetId < minId || maxId < targetId) { setError("source or target id out of range"); return false; } // create adjacent nodes if necessary and new edge if (m_mapToNode[sourceId] == nullptr) m_mapToNode[sourceId] = G.newNode(); if (m_mapToNode[targetId] == nullptr) m_mapToNode[targetId] = G.newNode(); edge e = G.newEdge(m_mapToNode[sourceId],m_mapToNode[targetId]); if (AG.has(GraphAttributes::edgeGraphics)) AG.bends(e).conc(bends); if (AG.has(GraphAttributes::edgeType)) AG.type(e) = umlType; if(AG.has(GraphAttributes::edgeSubGraphs)) AG.subGraphBits(e) = subGraph; if (AG.has(GraphAttributes::edgeLabel)) AG.label(e) = label; if (AG.has(GraphAttributes::edgeArrow)) { if (arrow == "none") AG.arrowType(e) = eaNone; else if (arrow == "last") AG.arrowType(e) = eaLast; else if (arrow == "first") AG.arrowType(e) = eaFirst; else if (arrow == "both") AG.arrowType(e) = eaBoth; else AG.arrowType(e) = eaUndefined; } if (AG.has(GraphAttributes::edgeStyle)) { AG.strokeColor(e) = fill; AG.setStrokeType(e, intToStrokeType(stipple)); AG.strokeWidth(e) = lineWidth; } if (AG.has(GraphAttributes::edgeDoubleWeight)) AG.doubleWeight(e) = edgeWeight; break; } case directedPredefKey: { if(son->m_valueType != gmlIntValue) break; AG.setDirected(son->m_intValue > 0); break; } } } return true; }//read