/* * calculate edge orthogonality criterium */ double EdgeOrthogonalityCriterium(Graph& G, GraphAttributes& GA) { double sum = 0; for (edge e : G.edges) { node s = e->source(); node t = e->target(); double s_x = GA.x(s); double s_y = GA.y(s); double t_x = GA.x(t); double t_y = GA.y(t); double angle = abs(atan2(s_y - t_y, t_x - s_x) * 180 / PI); Array<double> values = { angle, abs(90.0 - angle), abs(180.0 - angle) }; double *deviation = min_element(begin(values), end(values)); *deviation = *deviation / 45; sum += *deviation; } double Neo = 1.0 - ((1.0 / G.edges.size()) * sum); return Neo; }
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; } } }
//chooses the initial radius of the disk as half the maximum of width and height of //the initial layout or depending on the value of m_fineTune void DavidsonHarel::computeFirstRadius(const GraphAttributes &AG) { const Graph &G = AG.constGraph(); node vFirst = G.firstNode(); double minX = AG.x(vFirst); double minY = AG.y(vFirst); double maxX = minX; double maxY = minY; for(node v : G.nodes) { Math::updateMin(minX, AG.x(v)); Math::updateMax(maxX, AG.x(v)); Math::updateMin(minY, AG.y(v)); Math::updateMax(maxY, AG.y(v)); } // compute bounding box of current layout // make values nonzero double w = maxX-minX+1.0; double h = maxY-minY+1.0; double ratio = h/w; double W = sqrt(G.numberOfNodes() / ratio); m_diskRadius = W / 5.0;//allow to move by a significant part of current layout size Math::updateMax(m_diskRadius, max(maxX-minX, maxY-minY) / 5.0); //TODO: also use node sizes #if 0 double lengthSum(0.0); for(node v : m_G.nodes) { const DRectIntersection &i = shape(v); lengthSum += i.width(); lengthSum += i.width(); } lengthSum /= (2*m_G.numberOfNodes()); // lengthSum is now the average of all lengths and widths #endif //change the initial radius depending on the settings //this is legacy crap #if 0 double divo = 2.0; if (m_fineTune == tpCoarse) { m_diskRadius = 1000.0; divo = 0.5; } if (m_fineTune == tpFine) { m_diskRadius = 10.0; divo = 15.0; } #if 0 Math::updateMax(m_diskRadius, max(maxX-minX,maxY-minY)); #endif m_diskRadius = max(maxX-minX,maxY-minY); m_diskRadius /= divo; #endif }
void ComponentSplitterLayout::call(GraphAttributes &GA) { // Only do preparations and call if layout is valid if (m_secondaryLayout.valid()) { //first we split the graph into its components const Graph& G = GA.constGraph(); NodeArray<int> componentNumber(G); m_numberOfComponents = connectedComponents(G, componentNumber); if (m_numberOfComponents == 0) { return; } //std::vector< std::vector<node> > componentArray; //componentArray.resize(numComponents); //Array<GraphAttributes *> components(numComponents); // // intialize the array of lists of nodes contained in a CC nodesInCC.init(m_numberOfComponents); node v; forall_nodes(v,G) nodesInCC[componentNumber[v]].pushBack(v); // Create copies of the connected components and corresponding // GraphAttributes GraphCopy GC; GC.createEmpty(G); EdgeArray<edge> auxCopy(G); for (int i = 0; i < m_numberOfComponents; i++) { GC.initByNodes(nodesInCC[i],auxCopy); GraphAttributes cGA(GC); //copy information into copy GA forall_nodes(v, GC) { cGA.width(v) = GA.width(GC.original(v)); cGA.height(v) = GA.height(GC.original(v)); cGA.x(v) = GA.x(GC.original(v)); cGA.y(v) = GA.y(GC.original(v)); } m_secondaryLayout.get().call(cGA); //copy layout information back into GA forall_nodes(v, GC) { node w = GC.original(v); if (w != 0) GA.x(w) = cGA.x(v); GA.y(w) = cGA.y(v); } }
//chooses the initial radius of the disk as half the maximum of width and height of //the initial layout or depending on the value of m_fineTune void DavidsonHarel::computeFirstRadius(const GraphAttributes &AG) { const Graph &G = AG.constGraph(); node v = G.firstNode(); double minX = AG.x(v); double minY = AG.y(v); double maxX = minX; double maxY = minY; forall_nodes(v,G) { minX = min(minX,AG.x(v)); maxX = max(maxX,AG.x(v)); minY = min(minY,AG.y(v)); maxY = max(maxY,AG.y(v)); }
//the vertices with degree zero are placed below all other vertices on a horizontal // line centered with repect to the rest of the drawing void DavidsonHarel::placeIsolatedNodes(GraphAttributes &AG) const { double minX = 0.0; double minY = 0.0; double maxX = 0.0; if (!m_nonIsolatedNodes.empty()) { //compute a rectangle that includes all non-isolated vertices node vFirst = m_nonIsolatedNodes.front(); minX = AG.x(vFirst); minY = AG.y(vFirst); maxX = minX; double maxY = minY; for (node v : m_nonIsolatedNodes) { double xVal = AG.x(v); double yVal = AG.y(v); double halfHeight = AG.height(v) / 2.0; double halfWidth = AG.width(v) / 2.0; if (xVal - halfWidth < minX) minX = xVal - halfWidth; if (xVal + halfWidth > maxX) maxX = xVal + halfWidth; if (yVal - halfHeight < minY) minY = yVal - halfHeight; if (yVal + halfHeight > maxY) maxY = yVal + halfHeight; } } // compute the width and height of the largest isolated node List<node> isolated; const Graph &G = AG.constGraph(); double maxWidth = 0; double maxHeight = 0; for (node v : G.nodes) if (v->degree() == 0) { isolated.pushBack(v); if (AG.height(v) > maxHeight) maxHeight = AG.height(v); if (AG.width(v) > maxWidth) maxWidth = AG.width(v); } // The nodes are placed on a line in the middle under the non isolated vertices. // Each node gets a box sized 2 maxWidth. double boxWidth = 2.0*maxWidth; double commonYCoord = minY - (1.5*maxHeight); double XCenterOfDrawing = minX + ((maxX - minX) / 2.0); double startXCoord = XCenterOfDrawing - 0.5*(isolated.size()*boxWidth); double xcoord = startXCoord; for (node v : isolated) { AG.x(v) = xcoord; AG.y(v) = commonYCoord; xcoord += boxWidth; } }
void MultilevelGraph::importAttributesSimple(const GraphAttributes &GA) { OGDF_ASSERT(&(GA.constGraph()) == m_G); m_avgRadius = 0.0; for(node v : m_G->nodes) { double w = GA.width(v); double h = GA.height(v); if(w > 0 || h > 0) { m_radius[v] = sqrt(w*w + h*h) / 2.0f; } else { m_radius[v] = 1.0f; } m_avgRadius += m_radius[v]; m_GA->x(v) = GA.x(v); m_GA->y(v) = GA.y(v); m_GA->width(v) = GA.width(v); m_GA->height(v) = GA.height(v); } m_avgRadius /= m_G->numberOfNodes(); for(edge e : m_G->edges) { m_weight[e] = GA.doubleWeight(e); } }
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); }
static bool inline readAttribute( GraphAttributes &GA, node v, const NodeAttribute &attr, const std::string &value) { const long attrs = GA.attributes(); switch(attr) { case na_name: // Not really an attribute, handled elsewhere. break; case na_label: if(attrs & GraphAttributes::nodeLabel) { GA.label(v) = value; } break; case na_x: if(attrs & GraphAttributes::nodeGraphics) { std::istringstream is(value); is >> GA.x(v); } break; case na_y: if(attrs & GraphAttributes::nodeGraphics) { std::istringstream is(value); is >> GA.y(v); }
void PivotMDS::doPathLayout(GraphAttributes& GA, const node& v) { double xPos = 0; node prev = v; node cur = v; // since the given node is the beginning of the path just // use bfs and increment the x coordinate by the average // edge costs. do { GA.x(cur) = xPos; GA.y(cur) = 0; for(adjEntry adj : cur->adjEntries) { node w = adj->twinNode(); if (!(w == prev) || w == cur) { prev = cur; cur = w; if(m_hasEdgeCostsAttribute) { xPos+=GA.doubleWeight(adj->theEdge()); } else { xPos += m_edgeCosts; } break; } prev = cur; } } while (prev != cur); }
void BendPromotion(Graph& G, GraphAttributes& GA) { List<edge> edges; G.allEdges(edges); while (!edges.empty()) { edge e = edges.popFrontRet(); DPolyline bends_e = GA.bends(e); node s = e->source(); node t = e->target(); //check if an edge has bendpoints if (!bends_e.empty()) { while (!bends_e.empty()) { DPoint p = bends_e.front(); //insert new node node n = G.newNode(); GA.x(n) = p.m_x; GA.y(n) = p.m_y; edge e_ = G.newEdge(s, n); GA.arrowType(e_) = ogdf::EdgeArrow::None; GA.strokeColor(e_) = Color("#bababa"); s = n; bends_e.popFront(); } edge e_ = G.newEdge(s, t); GA.arrowType(e_) = ogdf::EdgeArrow::None; GA.strokeColor(e_) = Color("#bababa"); G.delEdge(e); } } }
// assumes, that the Graphs of MultilevelGraph and GA are the same, not copies! void MultilevelGraph::exportAttributesSimple(GraphAttributes &GA) const { OGDF_ASSERT(&(GA.constGraph()) == m_G); prepareGraphAttributes(GA); for(node v : m_G->nodes) { GA.x(v) = m_GA->x(v); GA.y(v) = m_GA->y(v); //TODO: Check what this w,h computation does double w = GA.width(v); double h = GA.height(v); if(w > 0 || h > 0) { double factor = m_radius[v] / sqrt(w*w + h*h) * 2.0f; w *= factor; h *= factor; } else { w = h = m_radius[v] * sqrt(2.0f); } GA.width(v) = w; GA.height(v) = h; GA.weight(v) = m_reverseNodeMergeWeight[v->index()]; } for(edge e : m_G->edges) { GA.doubleWeight(e) = m_weight[e]; } }
static inline bool readVizAttribute( GraphAttributes &GA, node v, const pugi::xml_node tag) { const long attrs = GA.attributes(); if(string(tag.name()) == "viz:position") { if(attrs & GraphAttributes::nodeGraphics) { pugi::xml_attribute xAttr = tag.attribute("x"); pugi::xml_attribute yAttr = tag.attribute("y"); pugi::xml_attribute zAttr = tag.attribute("z"); if(!xAttr || !yAttr) { GraphIO::logger.lout() << "Missing \"x\" or \"y\" in position tag." << std::endl; return false; } GA.x(v) = xAttr.as_int(); GA.y(v) = yAttr.as_int(); // z attribute is optional and avaliable only in \a threeD mode GA.y(v) = yAttr.as_int(); if (zAttr && (attrs & GraphAttributes::threeD)) { GA.z(v) = zAttr.as_int(); } } } else if(string(tag.name()) == "viz:size") { if(attrs & GraphAttributes::nodeGraphics) { pugi::xml_attribute valueAttr = tag.attribute("value"); if (!valueAttr) { GraphIO::logger.lout() << "\"size\" attribute is missing a value." << std::endl; return false; } double size = valueAttr.as_double(); GA.width(v) = size * LayoutStandards::defaultNodeWidth(); GA.height(v) = size * LayoutStandards::defaultNodeHeight(); } } else if(string(tag.name()) == "viz:shape") { if(attrs & GraphAttributes::nodeGraphics) { pugi::xml_attribute valueAttr = tag.attribute("value"); if(!valueAttr) { GraphIO::logger.lout() << "\"shape\" attribute is missing a value." << std::endl; return false; } GA.shape(v) = toShape(valueAttr.value()); } } else if(string(tag.name()) == "viz:color") { if(attrs & GraphAttributes::nodeStyle) { return readColor(GA.fillColor(v), tag); } } else { GraphIO::logger.lout() << "Incorrect tag: \"" << tag.name() << "\"." << std::endl; return false; } return true; }
bool GraphMLParser::readData( GraphAttributes &GA, const node &v, const XmlTagObject &nodeData) { XmlAttributeObject *keyId; nodeData.findXmlAttributeObjectByName("key", keyId); if(keyId == NULL) { cerr << "ERROR: Node data does not have a key.\n"; return false; } const long attrs = GA.attributes(); std::stringstream value(nodeData.getValue()); switch (graphml::toAttribute(m_attrName[keyId->getValue()])) { case graphml::a_nodeLabel: if(attrs & GraphAttributes::nodeLabel) { value >> GA.label(v); } break; case graphml::a_x: if(attrs & GraphAttributes::nodeGraphics) { value >> GA.x(v); }
double LayoutStatistics::edgeLengths( const GraphAttributes &ga, double *pMinLength, double *pMaxLength, double *pAvgLength, double *pStdDeviation, bool considerSelfLoops) { const Graph &G = ga.constGraph(); int m = G.numberOfEdges(); double totalLength = 0, minLength = numeric_limits<double>::max(), maxLength = -numeric_limits<double>::max(); EdgeArray<double> len(G); int nSelfLoops = 0; for(edge e : G.edges) { if(!considerSelfLoops && e->isSelfLoop()) { nSelfLoops++; continue; } const DPolyline &dpl = ga.bends(e); if(!dpl.empty()) { len[e] = dpl.length(); } else { DPoint pv = DPoint(ga.x(e->source()),ga.y(e->source())); DPoint pw = DPoint(ga.x(e->target()),ga.y(e->target())); len[e] = pv.distance(pw); } totalLength += len[e]; minLength = min(minLength, len[e]); maxLength = max(maxLength, len[e]); } m -= nSelfLoops; double avgEdgeLength = totalLength / m; if(pAvgLength) *pAvgLength = avgEdgeLength; if(pMinLength) *pMinLength = minLength; if(pMaxLength) *pMaxLength = maxLength; if(pStdDeviation) { double sum = 0; for(edge e : G.edges) { if(!considerSelfLoops && e->isSelfLoop()) continue; double d = len[e] - avgEdgeLength; sum += d*d; } *pStdDeviation = sqrt(sum / m); } return totalLength; }
void FastMultipoleMultilevelEmbedder::writeCurrentToGraphAttributes(GraphAttributes& GA) { for(node v : m_pCurrentGraph->nodes) { GA.x(v) = (*m_pCurrentNodeXPos)[v]; GA.y(v) = (*m_pCurrentNodeYPos)[v]; } }
//this is the main optimization routine with the loop that lowers the temperature //and the disk radius geometrically until the temperature is zero. For each //temperature, a certain number of new positions for a random vertex are tried void DavidsonHarel::call(GraphAttributes &AG) { initParameters(); m_shrinkingFactor = m_shrinkFactor; OGDF_ASSERT(!m_energyFunctions.empty()); const Graph &G = AG.constGraph(); //compute the list of vertices with degree greater than zero G.allNodes(m_nonIsolatedNodes); ListIterator<node> it,itSucc; for(it = m_nonIsolatedNodes.begin(); it.valid(); it = itSucc) { itSucc = it.succ(); if((*it)->degree() == 0) m_nonIsolatedNodes.del(it); } if(G.numberOfEdges() > 0) { //else only isolated nodes computeFirstRadius(AG); computeInitialEnergy(); if(m_numberOfIterations == 0) m_numberOfIterations = m_nonIsolatedNodes.size() * m_iterationMultiplier; //this is the main optimization loop while(m_temperature > 0) { //iteration loop for each temperature for(int ic = 1; ic <= m_numberOfIterations; ic ++) { DPoint newPos; //choose random vertex and new position for vertex node v = computeCandidateLayout(AG,newPos); //compute candidate energy and decide if new layout is chosen ListIterator<double> it2 = m_weightsOfEnergyFunctions.begin(); double newEnergy = 0.0; for(EnergyFunction *f : m_energyFunctions) { newEnergy += f->computeCandidateEnergy(v,newPos) * (*it2); ++it2; } OGDF_ASSERT(newEnergy >= 0.0); //this tests if the new layout is accepted. If this is the case, //all energy functions are informed that the new layout is accepted if(testEnergyValue(newEnergy)) { for(EnergyFunction *f : m_energyFunctions) f->candidateTaken(); AG.x(v) = newPos.m_x; AG.y(v) = newPos.m_y; m_energy = newEnergy; } } //lower the temperature and decrease the disk radius m_temperature = (int)floor(m_temperature*m_coolingFactor); m_diskRadius *= m_shrinkingFactor; } } //if there are zero degree vertices, they are placed using placeIsolatedNodes if(m_nonIsolatedNodes.size() != G.numberOfNodes()) placeIsolatedNodes(AG); }
static void compute_bounding_box(const GraphAttributes &A, double &xmin, double &ymin, double &xmax, double &ymax) { const Graph &G = A.constGraph(); if(G.numberOfNodes() == 0) { xmin = xmax = ymin = ymax = 0; return; } node v = G.firstNode(); xmin = xmax = A.x(v), ymin = ymax = A.y(v); forall_nodes(v, G) { double lw = (A.attributes() & GraphAttributes::nodeStyle) ? 0.5*A.strokeWidth(v) : 0.5; xmax = max(xmax, A.x(v) + A.width (v)/2 + lw); ymax = max(ymax, A.y(v) + A.height(v)/2 + lw); xmin = min(xmin, A.x(v) - A.width (v)/2 - lw); ymin = min(ymin, A.y(v) - A.height(v)/2 - lw); }
void BertaultLayout::call(GraphAttributes &AG) { const Graph &G = AG.constGraph(); if(G.numberOfNodes() == 0) return; if( (AG.attributes() & GraphAttributes::nodeGraphics) == 0 ) return; if( (AG.attributes() & GraphAttributes::edgeGraphics) != 0 ) AG.clearAllBends(); if(iter_no==0) iter_no=G.numberOfNodes()*10; if(req_length==0) { edge e; forall_edges(e,G) { node a=e->source(); node b=e->target(); req_length+=sqrt((AG.x(a)-AG.x(b))*(AG.x(a)-AG.x(b))+(AG.y(a)-AG.y(b))*(AG.y(a)-AG.y(b))); }
inline void FMMMLayout :: import_NodeAttributes(const Graph& G, GraphAttributes& GA, NodeArray<NodeAttributes>& A) { node v; DPoint position; forall_nodes(v,G) { position.m_x = GA.x(v); position.m_y = GA.y(v); A[v].set_NodeAttributes(GA.width(v),GA.height(v),position,NULL,NULL); }
void StressMinimization::copyLayout( const GraphAttributes& GA, NodeArray<double>& newX, NodeArray<double>& newY) { // copy the layout for(node v : GA.constGraph().nodes) { newX[v] = GA.x(v); newY[v] = GA.y(v); } }
static inline void writeAttributes( std::ostream &out, const GraphAttributes &GA, const node &v) { const long flags = GA.attributes(); out << "["; bool separator = false; // Wheter to put separator before attribute. if(flags & GraphAttributes::nodeId) { writeAttribute(out, separator, "id", GA.idNode(v)); } if(flags & GraphAttributes::nodeLabel) { writeAttribute(out, separator, "label", GA.label(v)); } if(flags & GraphAttributes::nodeTemplate) { writeAttribute(out, separator, "comment", GA.templateNode(v)); } if(flags & GraphAttributes::nodeGraphics) { writeAttribute(out, separator, "width", GA.width(v)); writeAttribute(out, separator, "height", GA.height(v)); writeAttribute(out, separator, "shape", dot::toString(GA.shape(v))); out << ", pos=\"" << GA.x(v) << "," << GA.y(v); if(flags & GraphAttributes::threeD) { out << "," << GA.z(v); } out << "\""; } if(flags & GraphAttributes::nodeStyle) { writeAttribute(out, separator, "color", GA.strokeColor(v)); writeAttribute(out, separator, "fillcolor", GA.fillColor(v)); writeAttribute(out, separator, "stroketype", toString(GA.strokeType(v))); writeAttribute(out, separator, "strokewidth", GA.strokeWidth(v)); writeAttribute(out, separator, "fillpattern", toString(GA.fillPattern(v))); } if(flags & GraphAttributes::nodeType) { writeAttribute(out, separator, "type", int(GA.type(v))); } if(flags & GraphAttributes::nodeWeight) { writeAttribute(out, separator, "weight", GA.weight(v)); } out << "]"; }
// create testGraph to test criteria imlementations void CreateGraph(Graph& G, GraphAttributes& GA) { // add nodes node zero = G.newNode(); node one = G.newNode(); node two = G.newNode(); node three = G.newNode(); node four = G.newNode(); // set node positions GA.x(zero) = 4 * NODE_WIDTH; GA.y(zero) = 0; GA.x(one) = 4 * NODE_WIDTH; GA.y(one) = 4 * NODE_HEIGHT; GA.x(two) = 0; GA.y(two) = 2 * NODE_HEIGHT; GA.x(three) = 4 * NODE_WIDTH; GA.y(three) = 8 * NODE_HEIGHT; GA.x(four) = 0; GA.y(four) = 8 * NODE_HEIGHT; // add edges edge zero_one = G.newEdge(zero, one); edge zero_three = G.newEdge(zero, three); edge zero_four = G.newEdge(zero, four); edge one_two = G.newEdge(one, two); edge one_three = G.newEdge(one, three); edge two_three = G.newEdge(two, three); DPolyline &p = GA.bends(zero_three); p.pushBack(DPoint(6 * NODE_WIDTH, 2 * NODE_HEIGHT)); p.pushBack(DPoint(6 * NODE_WIDTH, 6 * NODE_HEIGHT)); }
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; }
void GridLayoutModule::mapGridLayout(const Graph &G, GridLayout &gridLayout, GraphAttributes &AG) { // maximum width of columns and rows double maxWidth = 0; double yMax = 0; for(node v : G.nodes) { Math::updateMax<double>(maxWidth, AG.width(v)); Math::updateMax<double>(maxWidth, AG.height(v)); Math::updateMax<double>(yMax, gridLayout.y(v)); } maxWidth += m_separation; // set position of nodes for(node v : G.nodes) { AG.x(v) = gridLayout.x(v) * maxWidth; AG.y(v) = (yMax - gridLayout.y(v)) * maxWidth; } // transform bend points of edges for(edge e : G.edges) { IPolyline ipl = gridLayout.polyline(e); // Remove superfluous bendpoints node v = e->source(); while(!ipl.empty() && ipl.front() == IPoint(gridLayout.x(v), gridLayout.y(v))) { ipl.popFront(); } v = e->target(); while(!ipl.empty() && ipl.back() == IPoint(gridLayout.x(v), gridLayout.y(v))) { ipl.popBack(); } DPolyline &dpl = AG.bends(e); dpl.clear(); for (const IPoint &ip : ipl) { dpl.pushBack(DPoint(ip.m_x*maxWidth, (yMax-ip.m_y)*maxWidth)); } dpl.normalize(); } }
void RadialTreeLayout::ComputeCoordinates(GraphAttributes &AG) { const Graph &G = AG.constGraph(); //double mx = m_outerRadius + 0.5*m_connectedComponentDistance; //double my = mx; for(node v : G.nodes) { double r = m_radius[m_level[v]]; double alpha = m_angle[v]; AG.x(v) = r * cos(alpha); AG.y(v) = r * sin(alpha); } AG.clearAllBends(); }
//chooses random vertex and a new random position for it on a circle with radius m_diskRadius //around its previous position node DavidsonHarel::computeCandidateLayout( const GraphAttributes &AG, DPoint &newPos) const { int randomPos = randomNumber(0,m_nonIsolatedNodes.size()-1); node v = *(m_nonIsolatedNodes.get(randomPos)); double oldx = AG.x(v); double oldy = AG.y(v); double randomAngle = randNum() * 2.0 * Math::pi; newPos.m_y = oldy+sin(randomAngle)*m_diskRadius; newPos.m_x = oldx+cos(randomAngle)*m_diskRadius; #ifdef OGDF_DEBUG double dist = sqrt((newPos.m_x - oldx)*(newPos.m_x - oldx)+(newPos.m_y-oldy)*(newPos.m_y-oldy)); OGDF_ASSERT(dist > 0.99 * m_diskRadius && dist < 1.01 * m_diskRadius); #endif return v; }
/* * calculate node orthogonality criterium * * TODO: calculate value '2 * NODE_SIZE' from: * GCD of the set of vertical and horizontal (pixel) differences between all geometrically adjacent nodes. */ double NodeOrthogonalityCriterium(Graph& G, GraphAttributes& GA) { double width = 0, height = 0; for (node n : G.nodes) { double nodeWidth = GA.x(n) / (2 * NODE_WIDTH); double nodeHeight = GA.y(n) / (2 * NODE_HEIGHT); if (nodeWidth > width) width = nodeWidth; if (nodeHeight > height) height = nodeHeight; } double A = (1 + width)*(1 + height); double Nno = G.nodes.size() / A; return Nno; }
void TutteLayout::call(GraphAttributes &AG) { const Graph &G = AG.constGraph(); List<node> fixedNodes; List<DPoint> positions; double diam = sqrt((m_bbox.width()) * (m_bbox.width()) + (m_bbox.height()) * (m_bbox.height())); // handle graphs with less than two nodes switch (G.numberOfNodes()) { case 0: return; case 1: node v = G.firstNode(); DPoint center(0.5 * m_bbox.width(),0.5 * m_bbox.height()); center = center + m_bbox.p1(); AG.x(v) = center.m_x; AG.y(v) = center.m_y; return; } // increase radius to have no overlap on the outer circle node v = G.firstNode(); double r = diam/2.8284271; int n = G.numberOfNodes(); double nodeDiam = 2.0*sqrt((AG.width(v)) * (AG.width(v)) + (AG.height(v)) * (AG.height(v))); if(r<nodeDiam/(2*sin(2*Math::pi/n))) { r=nodeDiam/(2*sin(2*Math::pi/n)); m_bbox = DRect (0.0, 0.0, 2*r, 2*r); } setFixedNodes(G,fixedNodes,positions,r); doCall(AG,fixedNodes,positions); }
static inline void writeAttributes( std::ostream &out, const GraphAttributes &GA, const node &v) { const long flags = GA.attributes(); out << "["; bool separator = false; // Wheter to put separator before attribute. if(flags & GraphAttributes::nodeId) { writeAttribute(out, separator, "id", GA.idNode(v)); } if(flags & GraphAttributes::nodeLabel) { writeAttribute(out, separator, "label", GA.label(v)); } if(flags & GraphAttributes::nodeTemplate) { writeAttribute(out, separator, "comment", GA.templateNode(v)); } if(flags & GraphAttributes::nodeGraphics) { writeAttribute(out, separator, "width", GA.width(v)); writeAttribute(out, separator, "height", GA.height(v)); writeAttribute(out, separator, "shape", dot::toString(GA.shape(v))); out << ", pos=\"" << GA.x(v) << "," << GA.y(v); if(flags & GraphAttributes::threeD) { out << "," << GA.z(v); } out << "\""; } if(flags & GraphAttributes::nodeStyle) { writeAttribute(out, separator, "color", GA.strokeColor(v)); writeAttribute(out, separator, "fillcolor", GA.fillColor(v)); } // NOTE: Node type is weird and (probably) cannot be mapped to DOT. // NOTE: Node weight is not supported. out << "]"; }