void VarEdgeInserterDynUMLCore::BCandSPQRtreesUML::insertEdgePath( edge eOrig, const SList<adjEntry>& crossedEdges) { SList<edge> ti; SList<node> tj; for (adjEntry adj : crossedEdges) { ti.pushBack(adj->theEdge()); tj.pushBack(adj->theEdge()->target()); } m_pr.insertEdgePath(eOrig, crossedEdges); Graph::EdgeType typeOfEOrig = m_pr.typeOrig(eOrig); int costOfEOrig = m_costOrig ? eOrig ? (*m_costOrig)[eOrig] : 0 : 1; node v = m_pr.copy(eOrig->source()); SListConstIterator<edge> it = ti.begin(); SListConstIterator<node> jt = tj.begin(); SListConstIterator<adjEntry> kt; for (kt = crossedEdges.begin(); it.valid(); ++it, ++jt, ++kt) { edge e = *it; node u = e->target(); adjEntry a; for (a = u->firstAdj(); a->theEdge()->target() != *jt; a = a->succ()) ; edge f = a->theEdge(); m_dynamicSPQRForest.updateInsertedNode(e, f); e = m_dynamicSPQRForest.rep(e); f = m_dynamicSPQRForest.rep(f); m_typeOf[f] = m_typeOf[e]; m_cost[f] = m_cost[e]; for (a = u->firstAdj(); a->theEdge()->source() != v; a = a->succ()); f = a->theEdge(); m_dynamicSPQRForest.updateInsertedEdge(f); f = m_dynamicSPQRForest.rep(f); m_typeOf[f] = typeOfEOrig; m_cost[f] = costOfEOrig; v = u; } node u = m_pr.copy(eOrig->target()); adjEntry a; for (a = v->firstAdj(); a->theEdge()->target() != u; a = a->succ()) ; edge f = a->theEdge(); m_dynamicSPQRForest.updateInsertedEdge(f); f = m_dynamicSPQRForest.rep(f); m_typeOf[f] = typeOfEOrig; m_cost[f] = costOfEOrig; }
node FaceSinkGraph::dfsStAugmentation( node v, // current node node parent, // its parent Graph &G, // original graph (not const) SList<edge> &augmentedEdges) // list of augmented edges { bool isFace = (m_originalFace[v] != nullptr); node vf = (parent != nullptr) ? m_originalNode[parent] : nullptr; // since we already know that T is a tree we can omit the visited array for(adjEntry adj : v->adjEntries) { node w = adj->twinNode(); if (w == parent) continue; if (isFace) { if (vf == nullptr) { vf = G.newNode(); } edge ew = G.newEdge(m_originalNode[w],vf); augmentedEdges.pushBack(ew); } dfsStAugmentation(w,v,G,augmentedEdges); } return vf; }
int main(void){ SList<int> mylist; mylist.pushFront(10); mylist.pushFront(20); mylist.pushBack(30); for(auto it=mylist.begin();it!=mylist.end();++it){ cout << *it << endl; } return 0; }
// // createSkeleton: creates a skeleton graph // DynamicSkeleton& DynamicSPQRTree::createSkeleton(node vT) const { DynamicSkeleton& S = *OGDF_NEW DynamicSkeleton(this, vT); SList<node> inMapV; for (edge eH : m_tNode_hEdges[vT]) { node sH = eH->source(); node tH = eH->target(); edge& eM = m_skelEdge[eH]; node& sM = m_mapV[sH]; node& tM = m_mapV[tH]; if (!sM) { sM = S.m_M.newNode(); S.m_origNode[sM] = sH; inMapV.pushBack(sH); } if (!tM) { tM = S.m_M.newNode(); S.m_origNode[tM] = tH; inMapV.pushBack(tH); } eM = S.m_M.newEdge(sM, tM); S.m_origEdge[eM] = eH; } while (!inMapV.empty()) m_mapV[inMapV.popFrontRet()] = nullptr; S.m_referenceEdge = m_tNode_hRefEdge[vT]; if (S.m_referenceEdge) S.m_referenceEdge = m_skelEdge[S.m_referenceEdge]; m_sk[vT] = &S; return S; }
// builds list of possible external faces (all faces in tree T containing // the single source s) by a dfs traversal of T void FaceSinkGraph::gatherExternalFaces( node v, // current node node parent, // its parent SList<face> &externalFaces) // returns list of possible external faces { if (m_containsSource[v]) externalFaces.pushBack(m_originalFace[v]); // since we already know that T is a tree we can omit the visited array for(adjEntry adj : v->adjEntries) { node w = adj->twinNode(); if (w != parent) gatherExternalFaces(w,v,externalFaces); } }
// original variant of st-augmentation // Inserts also new nodes representing faces into G. void FaceSinkGraph::stAugmentation( node h, // node corresponding to external face Graph &G, // original graph (not const) SList<node> &augmentedNodes, // list of augmented nodes SList<edge> &augmentedEdges) // list of augmented edges { SListPure<node> roots; for(node v : nodes) { node vOrig = m_originalNode[v]; if (vOrig != nullptr && vOrig->indeg() > 0 && vOrig->outdeg() > 0) roots.pushBack(v); } node vh = dfsStAugmentation(h,nullptr,G,augmentedNodes,augmentedEdges); SListConstIterator<node> it; for(it = roots.begin(); it.valid(); ++it) dfsStAugmentation(*it,nullptr,G,augmentedNodes,augmentedEdges); augmentedEdges.pushBack(G.newEdge(m_source,vh)); }
void VarEdgeInserterDynCore::insert(edge eOrig, SList<adjEntry>& eip) { eip.clear(); node s = m_pr.copy(eOrig->source()); node t = m_pr.copy(eOrig->target()); // find path from s to t in BC-tree // call of blockInsert() is done when we have found the path // if no path is found, s and t are in different connected components // and thus an empty edge insertion path is correct! DynamicSPQRForest& dSPQRF = m_pBC->dynamicSPQRForest(); SList<node>& path = dSPQRF.findPath(s, t); if (!path.empty()) { SListIterator<node> it = path.begin(); node repS = dSPQRF.repVertex(s, *it); for (SListIterator<node> jt = it; it.valid(); ++it) { node repT = (++jt).valid() ? dSPQRF.cutVertex(*jt, *it) : dSPQRF.repVertex(t, *it); // less than 3 nodes requires no crossings (cannot build SPQR-tree // for a graph with less than 3 nodes!) if (dSPQRF.numberOfNodes(*it) > 3) { List<adjEntry> L; blockInsert(repS, repT, L); // call biconnected case // transform crossed edges to edges in G for (adjEntry kt : L) { edge e = kt->theEdge(); eip.pushBack(e->adjSource() == kt ? dSPQRF.original(e)->adjSource() : dSPQRF.original(e)->adjTarget()); } } if (jt.valid()) repS = dSPQRF.cutVertex(*it, *jt); } } delete &path; }
void UpwardPlanarSubgraphSimple::call(GraphCopy &GC, List<edge> &delEdges) { const Graph &G = GC.original(); delEdges.clear(); // We construct an auxiliary graph H which represents the current upward // planar subgraph. Graph H; NodeArray<node> mapToH(G,nullptr); NodeArray<node> mapToG(H,nullptr); for(node v : G.nodes) mapToG[ mapToH[v] = H.newNode() ] = v; // We currently support only single-source acyclic digraphs ... node s; hasSingleSource(G,s); OGDF_ASSERT(s != 0); OGDF_ASSERT(isAcyclic(G)); // We start with a spanning tree of G rooted at the single source. NodeArray<bool> visitedNode(G,false); SListPure<edge> treeEdges; dfsBuildSpanningTree(s,treeEdges,visitedNode); // Mark all edges in the spanning tree so they can be skipped in the // loop below and add (copies of) them to H. EdgeArray<bool> visitedEdge(G,false); SListConstIterator<edge> it; for(it = treeEdges.begin(); it.valid(); ++it) { edge eG = *it; visitedEdge[eG] = true; H.newEdge(mapToH[eG->source()],mapToH[eG->target()]); } // Add subsequently the remaining edges to H and test if the resulting // graph is still upward planar. If not, remove the edge again from H // and add it to delEdges. SList<Tuple2<node,node> > augmented; GraphCopySimple graphAcyclicTest(G); for(edge eG : G.edges) { // already treated ? if(visitedEdge[eG] == true) continue; // insert edge into H edge eH = H.newEdge(mapToH[eG->source()],mapToH[eG->target()]); node superSink; SList<edge> augmentedEdges; if (UpwardPlanarity::upwardPlanarAugment_singleSource(H,superSink,augmentedEdges) == false) { // if H is no longer upward planar, remove eG from subgraph H.delEdge(eH); delEdges.pushBack(eG); } else { // add augmented edges as node-pair to tmpAugmented and remove // all augmented edges from H again SList<Tuple2<node,node> > tmpAugmented; SListConstIterator<edge> it; for(it = augmentedEdges.begin(); it.valid(); ++it) { node v = mapToG[(*it)->source()]; node w = mapToG[(*it)->target()]; if (v && w) tmpAugmented.pushBack(Tuple2<node,node>(v,w)); H.delEdge(*it); } if (mapToG[superSink] == nullptr) H.delNode(superSink); //**************************************************************** // The following is a simple workaround to assure the following // property of the upward planar subgraph: // The st-augmented upward planar subgraph plus the edges not // in the subgraph must be acyclic. (This is a special property // of the embedding, not the augmentation.) // The upward-planar embedding function gives us ANY upward-planar // embedding. We check if the property above holds with this // embedding. If it doesn't, we have actually no idea if another // embedding would do. // The better solution would be to incorporate the acyclicity // property into the upward-planarity test, but this is compicated. //**************************************************************** // test if original graph plus augmented edges is still acyclic if(checkAcyclic(graphAcyclicTest,tmpAugmented) == true) { augmented = tmpAugmented; } else { // if not, remove eG from subgraph H.delEdge(eH); delEdges.pushBack(eG); } } } // remove edges not in the subgraph from GC ListConstIterator<edge> itE; for(itE = delEdges.begin(); itE.valid(); ++itE) GC.delEdge(GC.copy(*itE)); // add augmented edges to GC SListConstIterator<Tuple2<node,node> > itP; for(itP = augmented.begin(); itP.valid(); ++itP) { node v = (*itP).x1(); node w = (*itP).x2(); GC.newEdge(GC.copy(v),GC.copy(w)); } // add super sink to GC node sGC = nullptr; SList<node> sinks; for(node v : GC.nodes) { if(v->indeg() == 0) sGC = v; if(v->outdeg() == 0) sinks.pushBack(v); } node superSinkGC = GC.newNode(); SListConstIterator<node> itV; for(itV = sinks.begin(); itV.valid(); ++itV) GC.newEdge(*itV,superSinkGC); // add st-edge to GC, so that we now have a planar st-digraph GC.newEdge(sGC,superSinkGC); OGDF_ASSERT(isAcyclic(GC)); OGDF_ASSERT(isPlanar(GC)); }
void DynamicSPQRForest::createSPQR (node vB) const { Graph GC; NodeArray<node> origNode(GC,0); EdgeArray<edge> origEdge(GC,0); SListConstIterator<edge> iH; for (iH=m_bNode_hEdges[vB].begin(); iH.valid(); ++iH) m_htogc[(*iH)->source()] = m_htogc[(*iH)->target()] = 0; for (iH=m_bNode_hEdges[vB].begin(); iH.valid(); ++iH) { edge eH = *iH; node sH = eH->source(); node tH = eH->target(); node& sGC = m_htogc[sH]; node& tGC = m_htogc[tH]; if (!sGC) { sGC = GC.newNode(); origNode[sGC] = sH; } if (!tGC) { tGC = GC.newNode(); origNode[tGC] = tH; } origEdge[GC.newEdge(sGC,tGC)] = eH; } TricComp tricComp(GC); const GraphCopySimple& GCC = *tricComp.m_pGC; EdgeArray<node> partnerNode(GCC,0); EdgeArray<edge> partnerEdge(GCC,0); for (int i=0; i<tricComp.m_numComp; ++i) { const TricComp::CompStruct &C = tricComp.m_component[i]; if (C.m_edges.empty()) continue; node vT = m_T.newNode(); m_tNode_owner[vT] = vT; switch(C.m_type) { case TricComp::bond: m_tNode_type[vT] = PComp; m_bNode_numP[vB]++; break; case TricComp::polygon: m_tNode_type[vT] = SComp; m_bNode_numS[vB]++; break; case TricComp::triconnected: m_tNode_type[vT] = RComp; m_bNode_numR[vB]++; break; } for (ListConstIterator<edge> iGCC=C.m_edges.begin(); iGCC.valid(); ++iGCC) { edge eGCC = *iGCC; edge eH = GCC.original(eGCC); if (eH) eH = origEdge[eH]; else { node uH = origNode[GCC.original(eGCC->source())]; node vH = origNode[GCC.original(eGCC->target())]; eH = m_H.newEdge(uH,vH); if (!partnerNode[eGCC]) { partnerNode[eGCC] = vT; partnerEdge[eGCC] = eH; } else { m_T.newEdge(partnerNode[eGCC],vT); m_hEdge_twinEdge[eH] = partnerEdge[eGCC]; m_hEdge_twinEdge[partnerEdge[eGCC]] = eH; } } m_hEdge_position[eH] = m_tNode_hEdges[vT].pushBack(eH); m_hEdge_tNode[eH] = vT; } } m_bNode_SPQR[vB] = m_hEdge_tNode[origEdge[GC.firstEdge()]]; m_tNode_hRefEdge[m_bNode_SPQR[vB]] = 0; SList<node> lT; lT.pushBack(m_bNode_SPQR[vB]); lT.pushBack(0); while (!lT.empty()) { node vT = lT.popFrontRet(); node wT = lT.popFrontRet(); for (ListConstIterator<edge> iH=m_tNode_hEdges[vT].begin(); iH.valid(); ++iH) { edge eH = *iH; edge fH = m_hEdge_twinEdge[eH]; if (!fH) continue; node uT = m_hEdge_tNode[fH]; if (uT==wT) m_tNode_hRefEdge[vT] = eH; else { lT.pushBack(uT); lT.pushBack(vT); } } } }
edge DynamicSPQRTree::updateInsertedEdge(edge eG) { SList<node> marked; node sH = m_gNode_hNode[eG->source()]; node tH = m_gNode_hNode[eG->target()]; for (adjEntry aH : sH->adjEdges) { edge fH = aH->theEdge(); node vT = spqrproper(fH); if (fH->opposite(sH) == tH) { if (m_tNode_type[vT] == PComp) { DynamicSPQRForest::updateInsertedEdge(eG); if (m_sk[vT]) { edge eH = m_gEdge_hEdge[eG]; edge fM = m_skelEdge[fH]; node sM = fM->source(); node tM = fM->target(); if (eH->source() == m_sk[vT]->m_origNode[tM]) { node uM = sM; sM = tM; tM = uM; } m_skelEdge[eH] = m_sk[vT]->getGraph().newEdge(sM, tM); m_sk[vT]->m_origEdge[m_skelEdge[eH]] = eH; } return eG; } else if (!m_hEdge_twinEdge[fH]) { DynamicSPQRForest::updateInsertedEdge(eG); if (m_sk[vT]) { edge gH = m_hEdge_twinEdge[m_tNode_hEdges[m_hEdge_tNode[fH]].front()]; m_skelEdge[gH] = m_skelEdge[fH]; m_sk[vT]->m_origEdge[m_skelEdge[gH]] = gH; } return eG; } else { m_tNode_isMarked[vT] = true; marked.pushBack(vT); } } else { m_tNode_isMarked[vT] = true; marked.pushBack(vT); } } int count = 0; node found[2]; for (adjEntry aH : tH->adjEdges) { edge fH = aH->theEdge(); node vT = spqrproper(fH); if (!m_tNode_isMarked[vT]) continue; found[count++] = vT; m_tNode_isMarked[vT] = false; } while (!marked.empty()) m_tNode_isMarked[marked.popFrontRet()] = false; if (count == 0) { node rT; SList<node>& pT = findPathSPQR(sH, tH, rT); for (node vT : pT) { if (m_sk[vT]) { delete m_sk[vT]; m_sk[vT] = nullptr; } } delete &pT; } else if (count == 1) { node vT = found[0]; if (m_sk[vT]) { delete m_sk[vT]; m_sk[vT] = nullptr; } } return DynamicSPQRForest::updateInsertedEdge(eG); }
void GEMLayout::call(GraphAttributes &AG) { const Graph &G = AG.constGraph(); if(G.empty()) return; // all edges straight-line AG.clearAllBends(); GraphCopy GC; GC.createEmpty(G); // compute connected component of G NodeArray<int> component(G); int numCC = connectedComponents(G,component); // intialize the array of lists of nodes contained in a CC Array<List<node> > nodesInCC(numCC); for(node v : G.nodes) nodesInCC[component[v]].pushBack(v); EdgeArray<edge> auxCopy(G); Array<DPoint> boundingBox(numCC); int i; for(i = 0; i < numCC; ++i) { GC.initByNodes(nodesInCC[i],auxCopy); GraphCopyAttributes AGC(GC,AG); for(node vCopy : GC.nodes) { node vOrig = GC.original(vCopy); AGC.x(vCopy) = AG.x(vOrig); AGC.y(vCopy) = AG.y(vOrig); } SList<node> permutation; // initialize node data m_impulseX.init(GC,0); m_impulseY.init(GC,0); m_skewGauge.init(GC,0); m_localTemperature.init(GC,m_initialTemperature); // initialize other data m_globalTemperature = m_initialTemperature; m_barycenterX = 0; m_barycenterY = 0; for(node v : GC.nodes) { m_barycenterX += weight(v) * AGC.x(v); m_barycenterY += weight(v) * AGC.y(v); } m_cos = cos(m_oscillationAngle / 2.0); m_sin = sin(Math::pi / 2 + m_rotationAngle / 2.0); // main loop int counter = m_numberOfRounds; while(OGDF_GEOM_ET.greater(m_globalTemperature,m_minimalTemperature) && counter--) { // choose nodes by random permutations if(permutation.empty()) { for(node v : GC.nodes) permutation.pushBack(v); permutation.permute(m_rng); } node v = permutation.popFrontRet(); // compute the impulse of node v computeImpulse(GC,AGC,v); // update node v updateNode(GC,AGC,v); } node vFirst = GC.firstNode(); double minX = AGC.x(vFirst), maxX = AGC.x(vFirst), minY = AGC.y(vFirst), maxY = AGC.y(vFirst); for(node vCopy : GC.nodes) { node v = GC.original(vCopy); AG.x(v) = AGC.x(vCopy); AG.y(v) = AGC.y(vCopy); if(AG.x(v)-AG.width (v)/2 < minX) minX = AG.x(v)-AG.width(v) /2; if(AG.x(v)+AG.width (v)/2 > maxX) maxX = AG.x(v)+AG.width(v) /2; if(AG.y(v)-AG.height(v)/2 < minY) minY = AG.y(v)-AG.height(v)/2; if(AG.y(v)+AG.height(v)/2 > maxY) maxY = AG.y(v)+AG.height(v)/2; } minX -= m_minDistCC; minY -= m_minDistCC; for(node vCopy : GC.nodes) { node v = GC.original(vCopy); AG.x(v) -= minX; AG.y(v) -= minY; } boundingBox[i] = DPoint(maxX - minX, maxY - minY); } Array<DPoint> offset(numCC); TileToRowsCCPacker packer; packer.call(boundingBox,offset,m_pageRatio); // The arrangement is given by offset to the origin of the coordinate // system. We still have to shift each node and edge by the offset // of its connected component. for(i = 0; i < numCC; ++i) { const List<node> &nodes = nodesInCC[i]; const double dx = offset[i].m_x; const double dy = offset[i].m_y; // iterate over all nodes in ith CC ListConstIterator<node> it; for(node v : nodes) { AG.x(v) += dx; AG.y(v) += dy; } } // free node data m_impulseX.init(); m_impulseY.init(); m_skewGauge.init(); m_localTemperature.init(); }
void PlanRep::expandLowDegreeVertices(OrthoRep &OR) { for(node v : nodes) { if (!(isVertex(v)) || expandAdj(v) != nullptr) continue; SList<edge> adjEdges; SListPure<Tuple2<node,int> > expander; node u = v; bool firstTime = true; setExpandedNode(v, v); for(adjEntry adj : v->adjEdges) { adjEdges.pushBack(adj->theEdge()); if(!firstTime) u = newNode(); setExpandedNode(u, v); typeOf(u) = Graph::lowDegreeExpander; expander.pushBack(Tuple2<node,int>(u,OR.angle(adj))); firstTime = false; } SListConstIterator<Tuple2<node,int>> itn = expander.begin().succ(); for (SListConstIterator<edge> it = adjEdges.begin().succ(); it.valid(); ++it) { // Did we allocate enough dummy nodes? OGDF_ASSERT(itn.valid()); if ((*it)->source() == v) moveSource(*it,(*itn).x1()); else moveTarget(*it,(*itn).x1()); ++itn; } adjEntry adjPrev = v->firstAdj(); itn = expander.begin(); int nBends = (*itn).x2(); for (++itn; itn.valid(); ++itn) { edge e = newEdge(adjPrev,(*itn).x1()->firstAdj()); OR.bend(e->adjSource()).set(convexBend,nBends); OR.bend(e->adjTarget()).set(reflexBend,nBends); OR.angle(adjPrev) = 1; OR.angle(e->adjSource()) = 2; OR.angle(e->adjTarget()) = 1; nBends = (*itn).x2(); typeOf(e) = association; //??? setExpansionEdge(e, 2); adjPrev = (*itn).x1()->firstAdj(); } edge e = newEdge(adjPrev,v->lastAdj()); typeOf(e) = association; //??? setExpansionEdge(e, 2); expandAdj(v) = e->adjSource(); OR.bend(e->adjSource()).set(convexBend,nBends); OR.bend(e->adjTarget()).set(reflexBend,nBends); OR.angle(adjPrev) = 1; OR.angle(e->adjSource()) = 2; OR.angle(e->adjTarget()) = 1; } }//expandlowdegreevertices
void PlanRep::expand(bool lowDegreeExpand) { for(node v : nodes) { // Replace vertices with high degree by cages and // replace degree 4 vertices with two generalizations // adjacent in the embedding list by a cage. if ((v->degree() > 4) && (typeOf(v) != Graph::dummy) && !lowDegreeExpand) { edge e; //Set the type of the node v. It remains in the graph // as one of the nodes of the expanded face. typeOf(v) = Graph::highDegreeExpander; // Scan the list of edges of v to find the adjacent edges of v // according to the planar embedding. All except one edge // will get a new adjacent node SList<edge> adjEdges; {forall_adj_edges(e,v) adjEdges.pushBack(e); } //The first edge remains at v. remove it from the list. e = adjEdges.popFrontRet(); // Create the list of high degree expanders // We need degree(v)-1 of them to construct a face. // and set expanded Node to v setExpandedNode(v, v); SListPure<node> expander; for (int i = 0; i < v->degree()-1; i++) { node u = newNode(); typeOf(u) = Graph::highDegreeExpander; setExpandedNode(u, v); expander.pushBack(u); } // We move the target node of each ingoing generalization of v to a new // node stored in expander. // Note that, for each such edge e, the target node of the original // edge is then different from the original of the target node of e // (the latter is 0 because u is a new (dummy) node) SListConstIterator<node> itn; NodeArray<adjEntry> ar(*this); itn = expander.begin(); for (edge ei : adjEdges) { // Did we allocate enough dummy nodes? OGDF_ASSERT(itn.valid()); if (ei->source() == v) moveSource(ei,*itn); else moveTarget(ei,*itn); ar[*itn] = (*itn)->firstAdj(); ++itn; } ar[v] = v->firstAdj(); // Now introduce the circular list of new edges // forming the border of the merge face. Keep the embedding. adjEntry adjPrev = v->firstAdj(); // cout <<endl << "INTRODUCING CIRCULAR EDGES" << endl; for (node n : expander) { // cout << adjPrev << " " << (*itn)->firstAdj() << endl; e = Graph::newEdge(adjPrev,n->firstAdj()); setExpansionEdge(e, 2);//can be removed if edgetypes work properly setExpansion(e); setAssociation(e); typeOf(e) = association; //??? if (!expandAdj(v)) expandAdj(v) = e->adjSource(); adjPrev = n->firstAdj(); } e = newEdge(adjPrev,v->lastAdj()); typeOf(e) = association; //??? setExpansionEdge(e, 2);//can be removed if edgetypes work properly setAssociation(e); }//highdegree // Replace all vertices with degree > 2 by cages. else if (v->degree() >= 2 && typeOf(v) != Graph::dummy && lowDegreeExpand) { edge e; //Set the type of the node v. It remains in the graph // as one of the nodes of the expanded face. typeOf(v) = Graph::lowDegreeExpander; //high?? // Scan the list of edges of v to find the adjacent edges of v // according to the planar embedding. All except one edge // will get a new adjacent node SList<edge> adjEdges; {forall_adj_edges(e,v) adjEdges.pushBack(e); } //The first edge remains at v. remove it from the list. // Check if it is a generalization. e = adjEdges.popFrontRet(); // Create the list of high degree expanders // We need degree(v)-1 of them to construct a face. // and set expanded Node to v setExpandedNode(v, v); SListPure<node> expander; for (int i = 0; i < v->degree()-1; i++) { node u = newNode(); typeOf(u) = Graph::highDegreeExpander; setExpandedNode(u, v); expander.pushBack(u); } // We move the target node of each ingoing generalization of v to a new // node stored in expander. // Note that, for each such edge e, the target node of the original // edge is then different from the original of the target node of e // (the latter is 0 because u is a new (dummy) node) NodeArray<adjEntry> ar(*this); SListConstIterator<node> itn = expander.begin(); for (edge ei : adjEdges) { // Did we allocate enough dummy nodes? OGDF_ASSERT(itn.valid()); if (ei->source() == v) moveSource(ei,*itn); else moveTarget(ei,*itn); ar[*itn] = (*itn)->firstAdj(); ++itn; } ar[v] = v->firstAdj(); // Now introduce the circular list of new edges // forming the border of the merge face. Keep the embedding. adjEntry adjPrev = v->firstAdj(); for (node n : expander) { e = newEdge(adjPrev,n->firstAdj()); if (!expandAdj(v)) expandAdj(v) = e->adjSource(); typeOf(e) = association; //??? setExpansionEdge(e, 2); //new types setAssociation(e); //should be dummy type? setExpansion(e); adjPrev = n->firstAdj(); } e = newEdge(adjPrev,v->lastAdj()); typeOf(e) = association; //??? setExpansionEdge(e, 2); } } }//expand