void SimpleEmbedder::call(Graph& G, adjEntry& adjExternal) { OGDF_ASSERT(isPlanar(G)); //---------------------------------------------------------- // // determine embedding of G // // We currently compute any embedding and choose the maximal face // as external face // if we use FixedEmbeddingInserterOld, we have to re-use the computed // embedding, otherwise crossing nodes can turn into "touching points" // of edges (alternatively, we could compute a new embedding and // finally "remove" such unnecessary crossings). adjExternal = nullptr; if(!G.representsCombEmbedding()) planarEmbed(G); if (G.numberOfEdges() > 0) { CombinatorialEmbedding E(G); //face fExternal = E.maximalFace(); face fExternal = findBestExternalFace(G, E); adjExternal = fExternal->firstAdj(); } }
// embeds constraint graph such that all sources and sinks lie in a common // face void CompactionConstraintGraphBase::embed() { NodeArray<bool> onExternal(*this,false); const CombinatorialEmbedding &E = *m_pOR; face fExternal = E.externalFace(); for(adjEntry adj : fExternal->entries) onExternal[m_pathNode[adj->theNode()]] = true; // compute lists of sources and sinks SList<node> sources, sinks; for(node v : nodes) { if (onExternal[v]) { if (v->indeg() == 0) sources.pushBack(v); if (v->outdeg() == 0) sinks.pushBack(v); } } // determine super source and super sink node s,t; if (sources.size() > 1) { s = newNode(); for (node v : sources) newEdge(s,v); } else s = sources.front(); if (sinks.size() > 1) { t = newNode(); for (node v : sinks) newEdge(v,t); } else t = sinks.front(); edge st = newEdge(s,t); bool isPlanar = planarEmbed(*this); if (!isPlanar) OGDF_THROW(AlgorithmFailureException); delEdge(st); if (sources.size() > 1) delNode(s); if (sinks.size() > 1) delNode(t); }
void SchnyderLayout::doCall( const Graph &G, adjEntry adjExternal, GridLayout &gridLayout, IPoint &boundingBox, bool fixEmbedding) { // check for double edges & self loops OGDF_ASSERT(isSimple(G)); // handle special case of graphs with less than 3 nodes if (G.numberOfNodes() < 3) { node v1, v2; switch (G.numberOfNodes()) { case 0: boundingBox = IPoint(0, 0); return; case 1: v1 = G.firstNode(); gridLayout.x(v1) = gridLayout.y(v1) = 0; boundingBox = IPoint(0, 0); return; case 2: v1 = G.firstNode(); v2 = G.lastNode(); gridLayout.x(v1) = gridLayout.y(v1) = gridLayout.y(v2) = 0; gridLayout.x(v2) = 1; boundingBox = IPoint(1, 0); return; } } // make a copy for triangulation GraphCopy GC(G); // embed if (!fixEmbedding) { if (planarEmbed(GC) == false) { OGDF_THROW_PARAM(PreconditionViolatedException, pvcPlanar); } } triangulate(GC); schnyderEmbedding(GC, gridLayout, adjExternal); }
// sets the positions of the nodes in a largest face of G in the form // of a regular k-gon. The corresponding nodes and their positions are // stored in nodes and pos, respectively. void TutteLayout::setFixedNodes( const Graph &G, List<node>& nodes, List<DPoint>& pos, double radius) { // compute faces of a copy of G GraphCopy GC(G); // compute a planar embedding if \a G is planar if(isPlanar(G)) planarEmbed(GC); //FIXME this stuff above seems wrong!! CombinatorialEmbedding E(GC); E.computeFaces(); // search for largest face face maxFace = E.maximalFace(); // delete possible old entries in nodes and pos nodes.clear(); pos.clear(); // set nodes and pos NodeArray<bool> addMe(GC,true); List<node> maxNodes; for(adjEntry adj : maxFace->entries) { maxNodes.pushBack(adj->theNode()); } for(node w : maxNodes) { if(addMe[w]) { nodes.pushBack(w); addMe[w] = false; } } double step = 2.0 * Math::pi / (double)(nodes.size()); double alpha = 0.0; for(int i = 0; i < nodes.size(); ++i) { pos.pushBack(DPoint(radius * cos(alpha), radius * sin(alpha))); alpha += step; } }
void VarEdgeInserterDynCore::ExpandedGraph::expand(node v, node vPred, node vSucc) { m_exp.clear(); while (!m_nodesG.empty()) m_GtoExp[m_nodesG.popBackRet()] = nullptr; edge eInS = nullptr; if (vPred != nullptr) { eInS = m_BC.dynamicSPQRForest().virtualEdge(vPred, v); m_eS = insertEdge(eInS->source(), eInS->target(), nullptr); } edge eOutS = nullptr; if (vSucc != nullptr) { eOutS = m_BC.dynamicSPQRForest().virtualEdge(vSucc, v); m_eT = insertEdge(eOutS->source(), eOutS->target(), nullptr); } expandSkeleton(v, eInS, eOutS); planarEmbed(m_exp); m_E.init(m_exp); }
void FPPLayout::doCall( const Graph &G, adjEntry adjExternal, GridLayout &gridLayout, IPoint &boundingBox, bool fixEmbedding) { // check for double edges & self loops OGDF_ASSERT(isSimple(G)); // handle special case of graphs with less than 3 nodes if (G.numberOfNodes() < 3) { node v1, v2; switch (G.numberOfNodes()) { case 0: boundingBox = IPoint(0, 0); return; case 1: v1 = G.firstNode(); gridLayout.x(v1) = gridLayout.y(v1) = 0; boundingBox = IPoint(0, 0); return; case 2: v1 = G.firstNode(); v2 = G.lastNode(); gridLayout.x(v1) = gridLayout.y(v1) = gridLayout.y(v2) = 0; gridLayout.x(v2) = 1; boundingBox = IPoint(1, 0); return; } } // make a copy for triangulation GraphCopy GC(G); // embed if (!fixEmbedding) { if (planarEmbed(GC) == false) { OGDF_THROW_PARAM(PreconditionViolatedException, pvcPlanar); } } triangulate(GC); // get edges for outer face (triangle) adjEntry e_12; if (adjExternal != 0) { edge eG = adjExternal->theEdge(); edge eGC = GC.copy(eG); e_12 = (adjExternal == eG->adjSource()) ? eGC->adjSource() : eGC->adjTarget(); } else { e_12 = GC.firstEdge()->adjSource(); } adjEntry e_2n = e_12->faceCycleSucc(); NodeArray<int> num(GC); NodeArray<adjEntry> e_wp(GC); // List of predecessors on circle C_k NodeArray<adjEntry> e_wq(GC); // List of successors on circle C_k computeOrder(GC, num , e_wp, e_wq, e_12, e_2n, e_2n->faceCycleSucc()); computeCoordinates(GC, boundingBox, gridLayout, num, e_wp, e_wq); }
//-------------------------------------------------------------------- // actual algorithm call //-------------------------------------------------------------------- Module::ReturnType VarEdgeInserterDynCore::call( const Array<edge> &origEdges, RemoveReinsertType rrPost, double percentMostCrossed) { double T; usedTime(T); Module::ReturnType retValue = Module::retFeasible; m_runsPostprocessing = 0; if (origEdges.size() == 0) return Module::retOptimal; // nothing to do SListPure<edge> currentOrigEdges; if (rrPost == rrIncremental) { for (edge e : m_pr.edges) currentOrigEdges.pushBack(m_pr.original(e)); // insertion of edges for (int i = origEdges.low(); i <= origEdges.high(); ++i) { edge eOrig = origEdges[i]; storeTypeOfCurrentEdge(eOrig); m_pBC = createBCandSPQRtrees(); SList<adjEntry> eip; insert(eOrig, eip); m_pr.insertEdgePath(eOrig, eip); delete m_pBC; currentOrigEdges.pushBack(eOrig); bool improved; do { ++m_runsPostprocessing; improved = false; for (edge eOrigRR : currentOrigEdges) { int pathLength = (m_pCost != nullptr) ? costCrossed(eOrigRR) : (m_pr.chain(eOrigRR).size() - 1); if (pathLength == 0) continue; // cannot improve m_pr.removeEdgePath(eOrigRR); storeTypeOfCurrentEdge(eOrigRR); m_pBC = createBCandSPQRtrees(); SList<adjEntry> eip; insert(eOrigRR, eip); m_pr.insertEdgePath(eOrigRR, eip); delete m_pBC; int newPathLength = (m_pCost != nullptr) ? costCrossed(eOrigRR) : (m_pr.chain(eOrigRR).size() - 1); OGDF_ASSERT(newPathLength <= pathLength); if (newPathLength < pathLength) improved = true; } } while (improved); } } else { // insertion of edges m_pBC = createBCandSPQRtrees(); for (int i = origEdges.low(); i <= origEdges.high(); ++i) { edge eOrig = origEdges[i]; storeTypeOfCurrentEdge(eOrig); SList<adjEntry> eip; insert(eOrig, eip); m_pBC->insertEdgePath(eOrig, eip); } delete m_pBC; // postprocessing (remove-reinsert heuristc) const int m = m_pr.original().numberOfEdges(); SListPure<edge> rrEdges; switch (rrPost) { case rrAll: case rrMostCrossed: for (int i = m_pr.startEdge(); i < m_pr.stopEdge(); ++i) rrEdges.pushBack(m_pr.e(i)); break; case rrInserted: for (int i = origEdges.low(); i <= origEdges.high(); ++i) rrEdges.pushBack(origEdges[i]); break; case rrNone: case rrIncremental: case rrIncInserted: break; } // marks the end of the interval of rrEdges over which we iterate // initially set to invalid iterator which means all edges SListConstIterator<edge> itStop; bool improved; do { // abort postprocessing if time limit reached if (m_timeLimit >= 0 && m_timeLimit <= usedTime(T)) { retValue = Module::retTimeoutFeasible; break; } ++m_runsPostprocessing; improved = false; if (rrPost == rrMostCrossed) { VEICrossingsBucket bucket(&m_pr); rrEdges.bucketSort(bucket); const int num = int(0.01 * percentMostCrossed * m); itStop = rrEdges.get(num); } SListConstIterator<edge> it; for (it = rrEdges.begin(); it != itStop; ++it) { edge eOrig = *it; int pathLength = (m_pCost != nullptr) ? costCrossed(eOrig) : (m_pr.chain(eOrig).size() - 1); if (pathLength == 0) continue; // cannot improve m_pr.removeEdgePath(eOrig); storeTypeOfCurrentEdge(eOrig); m_pBC = createBCandSPQRtrees(); SList<adjEntry> eip; insert(eOrig, eip); m_pr.insertEdgePath(eOrig, eip); delete m_pBC; // we cannot find a shortest path that is longer than before! int newPathLength = (m_pCost != nullptr) ? costCrossed(eOrig) : (m_pr.chain(eOrig).size() - 1); OGDF_ASSERT(newPathLength <= pathLength); if (newPathLength < pathLength) improved = true; } } while (improved); } #ifdef OGDF_DEBUG bool isPlanar = #endif planarEmbed(m_pr); OGDF_ASSERT(isPlanar); m_pr.removePseudoCrossings(); OGDF_ASSERT(m_pr.representsCombEmbedding()); return retValue; }
//----------------------------------------------------------------------------- // call function: compute an UML layout for graph umlGraph //----------------------------------------------------------------------------- void PlanarizationLayoutUML::call(UMLGraph ¨Graph) { m_nCrossings = 0; if(umlGraph.constGraph().empty()) return; // check necessary preconditions preProcess(umlGraph); //--------------------------------------------------- // preprocessing: insert a merger for generalizations umlGraph.insertGenMergers(); PlanRepUML pr(umlGraph); const int numCC = pr.numberOfCCs(); // (width,height) of the layout of each connected component Array<DPoint> boundingBox(numCC); // alignment section (should not be here, because planarlayout should // not know about the meaning of layouter options and should not cope // with them), move later // we have to distinguish between cc's with and without generalizations // if the alignment option is set int l_layoutOptions = m_planarLayouter.get().getOptions(); bool l_align = ((l_layoutOptions & umlOpAlign) > 0); //end alignment section //------------------------------------------ //now planarize CCs and apply drawing module for(int i = 0; i < numCC; ++i) { //--------------------------------------- // 1. crossing minimization //--------------------------------------- // alignment: check wether gens exist, special treatment is necessary bool l_gensExist = false; // set this for all CC's, start with first gen, //this setting can be mixed among CC's without problems EdgeArray<Graph::EdgeType> savedType(pr); EdgeArray<Graph::EdgeType> savedOrigType(pr.original()); //for deleted copies EdgeArray<int> costOrig(pr.original(), 1); //edgearray for reinserter call: which edge may never be crossed? EdgeArray<bool> noCrossingEdge(pr.original(), false); edge e; forall_edges(e,pr) { edge eOrig = pr.original(e); if (pr.typeOf(e) == Graph::generalization) { if (l_align) l_gensExist = true; OGDF_ASSERT(!eOrig || !(noCrossingEdge[eOrig])); // high cost to allow alignment without crossings if (l_align && ( (eOrig && (pr.typeOf(e->target()) == Graph::generalizationMerger)) || pr.alignUpward(e->adjSource()) )) costOrig[eOrig] = 10; } } int cr; m_crossMin.get().call(pr, i, cr, &costOrig); m_nCrossings += cr; //--------------------------------------- // 2. embed resulting planar graph //--------------------------------------- // We currently compute any embedding and choose the maximal face as external face // if we use FixedEmbeddingInserter, we have to re-use the computed // embedding, otherwise crossing nodes can turn into "touching points" // of edges (alternatively, we could compute a new embedding and // finally "remove" such unnecessary crossings). if(!pr.representsCombEmbedding()) planarEmbed(pr); adjEntry adjExternal = 0; if(pr.numberOfEdges() > 0) { CombinatorialEmbedding E(pr); face fExternal = findBestExternalFace(pr,E); adjExternal = fExternal->firstAdj(); } //--------------------------------------------------------- // 3. compute layout of planarized representation //--------------------------------------------------------- Layout drawing(pr); // distinguish between CC's with/without generalizations // this changes the input layout modules options! if (l_gensExist) m_planarLayouter.get().setOptions(l_layoutOptions); else m_planarLayouter.get().setOptions((l_layoutOptions & ~umlOpAlign)); // call the Layouter for the CC's UMLGraph m_planarLayouter.get().call(pr, adjExternal, drawing); // copy layout into umlGraph // Later, we move nodes and edges in each connected component, such // that no two overlap. for(int j = pr.startNode(); j < pr.stopNode(); ++j) { node vG = pr.v(j); umlGraph.x(vG) = drawing.x(pr.copy(vG)); umlGraph.y(vG) = drawing.y(pr.copy(vG)); adjEntry adj; forall_adj(adj,vG) { if ((adj->index() & 1) == 0) continue; edge eG = adj->theEdge(); drawing.computePolylineClear(pr,eG,umlGraph.bends(eG)); } } // the width/height of the layout has been computed by the planar // layout algorithm; required as input to packing algorithm boundingBox[i] = m_planarLayouter.get().getBoundingBox(); }//for cc's