bool FeasibleUpwardPlanarSubgraph::constructMergeGraph( GraphCopy &M, adjEntry adj_orig, const List<edge> &orig_edges) { CombinatorialEmbedding Beta(M); //set ext. face of Beta adjEntry ext_adj = M.copy(adj_orig->theEdge())->adjSource(); Beta.setExternalFace(Beta.rightFace(ext_adj)); FaceSinkGraph fsg(Beta, M.copy(adj_orig->theNode())); SList<node> aug_nodes; SList<edge> aug_edges; SList<face> fList; fsg.possibleExternalFaces(fList); // use this method to call the methode checkForest() node v_ext = fsg.faceNodeOf(Beta.externalFace()); OGDF_ASSERT(v_ext != 0); fsg.stAugmentation(v_ext, M, aug_nodes, aug_edges); //add the deleted edges for(edge eOrig: orig_edges) { node a = M.copy(eOrig->source()); node b = M.copy(eOrig->target()); M.newEdge(a, b); } return (isAcyclic(M)); }
UpwardPlanRep::UpwardPlanRep(const GraphCopy &GC, ogdf::adjEntry adj_ext) : GraphCopy(GC), isAugmented(false), t_hat(nullptr), extFaceHandle(nullptr), crossings(0) { OGDF_ASSERT(adj_ext != nullptr); OGDF_ASSERT(hasSingleSource(*this)); m_isSourceArc.init(*this, false); m_isSinkArc.init(*this, false); hasSingleSource(*this, s_hat); m_Gamma.init(*this); //compute the ext. face; node v = copy(GC.original(adj_ext->theNode())); extFaceHandle = copy(GC.original(adj_ext->theEdge()))->adjSource(); if (extFaceHandle->theNode() != v) extFaceHandle = extFaceHandle->twin(); m_Gamma.setExternalFace(m_Gamma.rightFace(extFaceHandle)); for(adjEntry adj : s_hat->adjEntries) m_isSourceArc[adj->theEdge()] = true; computeSinkSwitches(); }
void SubgraphUpwardPlanarizer::constructComponentGraphs(BCTree &BC, NodeArray<GraphCopy> &biComps) { NodeArray<int> constructed(BC.originalGraph(), -1); const Graph &bcTree = BC.bcTree(); int i = 0; // comp. number for(node v : bcTree.nodes) { if (BC.typeOfBNode(v) == BCTree::CComp) continue; const SList<edge> &edges_comp = BC.hEdges(v); //bicomp edges List<edge> edges_orig; for(edge e : edges_comp) edges_orig.pushBack(BC.original(e)); GraphCopy GC; GC.createEmpty(BC.originalGraph()); // construct i-th component graph for(edge eOrig : edges_orig) { node srcOrig = eOrig->source(); node tgtOrig = eOrig->target(); if (constructed[srcOrig] != i) { constructed[srcOrig] = i; GC.newNode(srcOrig); } if (constructed[tgtOrig] != i) { constructed[tgtOrig] = i; GC.newNode(tgtOrig); } GC.newEdge(eOrig); } biComps[v] = GC; i++; } }
//use the layout information in the umlgraph to find nodes in //unconnected active parts of a CC that can be connected without //crossings in the given embedding void PlanRepInc::getExtAdjs(List<adjEntry> & /* extAdjs */) { //in order not to change the current CC initialization, //we construct a copy of the active parts (one by one) //and use the layout information to compute a external //face for that part. An (original) adjEntry on this face //is then inserted into the extAdjs list. //derive the unconnected parts by a run through the current //copy //compute connected component of current CC NodeArray<int> component(*this); int numPartialCC = connectedComponents(*this, component); EdgeArray<edge> copyEdge;//copy edges in partial CC copy //now we compute a copy for every CC //initialize an array of lists of nodes contained in a CC Array<List<node> > nodesInPartialCC; nodesInPartialCC.init(numPartialCC); for(node v : nodes) nodesInPartialCC[component[v]].pushBack(v); int i = 0; for (i = 0; i < numPartialCC; i++) { List<node> &theNodes = nodesInPartialCC[i]; GraphCopy GC; GC.createEmpty(*this); GC.initByNodes(theNodes, copyEdge); //now we derive an outer face of GC by using the //layout information on it's original //TODO: Insert the bend points into the copy //CombinatorialEmbedding E(GC); //run through the faces and compute angles to //derive outer face //we dont care about the original structure of //the graph, i.e., if crossings are inserted aso //we only take the given partial CC and its layout //adjEntry extAdj = getExtAdj(GC, E); //for(node v : GC.nodes) //{ // //} }//for }//getextadj
void Layout::computePolyline(GraphCopy &GC, edge eOrig, DPolyline &dpl) const { dpl.clear(); const List<edge> &edgePath = GC.chain(eOrig); // The corresponding edge path in the copy must contain at least 1 edge! OGDF_ASSERT(edgePath.size() >= 1); // iterate over all edges in the corresponding edge path in the copy bool firstTime = true; for (edge e : edgePath) { node v = e->source(); // append point of source node of e ... if (!firstTime) dpl.pushBack(DPoint(m_x[v], m_y[v])); else firstTime = false; // ... and polyline of e const DPolyline &segment = m_bends[e]; for (const DPoint &dp : segment) dpl.pushBack(dp); } }
Module::ReturnType FeasibleUpwardPlanarSubgraph::call( Graph &G, GraphCopy &FUPS, adjEntry &extFaceHandle, List<edge> &delEdges, bool multisources, int runs) { #ifdef OGDF_DEBUG OGDF_ASSERT(!UpwardPlanarity::isUpwardPlanar_singleSource(G)); #endif delEdges.clear(); //current fups, its embedding and the removed edges GraphCopy FUPS_cur; List<edge> delEdges_cur; call(G, FUPS, extFaceHandle, delEdges, multisources); for (int i = 1; i < runs; ++i) { adjEntry extFaceHandle_cur; call(G, FUPS_cur, extFaceHandle_cur, delEdges_cur, multisources); // use new result?? if (delEdges_cur.size() < delEdges.size()) { FUPS = FUPS_cur; extFaceHandle = FUPS.copy(FUPS_cur.original(extFaceHandle_cur->theEdge()))->adjSource(); delEdges = delEdges_cur; } } return Module::retFeasible; }
void UpwardPlanarSubgraphModule::callAndDelete( GraphCopy &GC, List<edge> &delOrigEdges) { List<edge> delEdges; call(GC, delEdges); ListConstIterator<edge> it; for(it = delEdges.begin(); it.valid(); ++it) { edge eCopy = *it; delOrigEdges.pushBack(GC.original(eCopy)); GC.delEdge(eCopy); } }
void FUPSSimple::getSpanTree(GraphCopy &GC, List<edge> &delEdges, bool random) { if (GC.numberOfNodes() == 1) return; // nothing to do node s; hasSingleSource(GC, s); NodeArray<bool> visited(GC, false); EdgeArray<bool> isTreeEdge(GC,false); List<node> toDo; //mark the incident edges e1..e_i of super source s and the incident edges of the target node of the edge e1.._e_i as tree edge. visited[s] = true; for(adjEntry adj : s->adjEdges) { isTreeEdge[adj] = true; visited[adj->theEdge()->target()]; for(adjEntry adjTmp : adj->theEdge()->target()->adjEdges) { isTreeEdge[adjTmp] = true; node tgt = adjTmp->theEdge()->target(); if (!visited[tgt]) { toDo.pushBack(tgt); visited[tgt] = true; } } } //traversing with dfs for(node start : toDo) { for(adjEntry adj : start->adjEdges) { node v = adj->theEdge()->target(); if (!visited[v]) dfs_visit(GC, adj->theEdge(), visited, isTreeEdge, random); } } // delete all non tree edgesEdges to obtain a span tree List<edge> l; for(edge e : GC.edges) { if (!isTreeEdge[e]) l.pushBack(e); } while (!l.empty()) { edge e = l.popFrontRet(); delEdges.pushBack(GC.original(e)); GC.delEdge(e); } }
void FeasibleUpwardPlanarSubgraph::getSpanTree(GraphCopy &GC, List<edge> &delEdges, bool random, bool multisource) { delEdges.clear(); if (GC.numberOfNodes() == 1) return; // nothing to do node s; hasSingleSource(GC, s); NodeArray<bool> visited(GC, false); EdgeArray<bool> isTreeEdge(GC,false); List<node> toDo; // the original graph is a multisource graph. The sources are connected with the super source s. // so do not delete the incident edges of s if (multisource){ // put all incident edges of the source to treeEdges for(adjEntry adj : s->adjEdges) { isTreeEdge[adj->theEdge()] = true; visited[adj->theEdge()->target()]; toDo.pushBack(adj->theEdge()->target()); } } else toDo.pushBack(s); //traversing with dfs for(node start : toDo) { for(adjEntry adj : start->adjEdges) { node v = adj->theEdge()->target(); if (!visited[v]) dfs_visit(GC, adj->theEdge(), visited, isTreeEdge, random); } } // delete all non tree edgesEdges to obtain a span tree List<edge> l; for(edge e : GC.edges) { if (!isTreeEdge[e]) l.pushBack(e); } while (!l.empty()) { edge e = l.popFrontRet(); delEdges.pushBack(GC.original(e)); GC.delEdge(e); } }
void GEMLayout::updateNode(GraphCopy &G, GraphCopyAttributes &AG,node v) { //const Graph &G = AG.constGraph(); int n = G.numberOfNodes(); double impulseLength; impulseLength = length(m_newImpulseX,m_newImpulseY); if(OGDF_GEOM_ET.greater(impulseLength,0.0)) { // scale impulse by node temperature m_newImpulseX *= m_localTemperature[v] / impulseLength; m_newImpulseY *= m_localTemperature[v] / impulseLength; // move node AG.x(v) += m_newImpulseX; AG.y(v) += m_newImpulseY; // adjust barycenter m_barycenterX += weight(v) * m_newImpulseX; m_barycenterY += weight(v) * m_newImpulseY; impulseLength = length(m_newImpulseX,m_newImpulseY) * length(m_impulseX[v],m_impulseY[v]); if(OGDF_GEOM_ET.greater(impulseLength,0.0)) { m_globalTemperature -= m_localTemperature[v] / n; // compute sine and cosine of angle between old and new impulse double sinBeta,cosBeta; sinBeta = (m_newImpulseX * m_impulseX[v] - m_newImpulseY * m_impulseY[v]) / impulseLength; cosBeta = (m_newImpulseX * m_impulseX[v] + m_newImpulseY * m_impulseY[v]) / impulseLength; // check for rotation if(OGDF_GEOM_ET.greater(sinBeta,m_sin)) m_skewGauge[v] += m_rotationSensitivity; // check for oscillation if(OGDF_GEOM_ET.greater(length(cosBeta),m_cos)) m_localTemperature[v] *= (1 + cosBeta * m_oscillationSensitivity); // cool down according to skew gauge m_localTemperature[v] *= (1.0 - length(m_skewGauge[v])); if(OGDF_GEOM_ET.geq(m_localTemperature[v],m_initialTemperature)) m_localTemperature[v] = m_initialTemperature; // adjust global temperature m_globalTemperature += m_localTemperature[v] / n; } // save impulse m_impulseX[v] = m_newImpulseX; m_impulseY[v] = m_newImpulseY; } }
Module::ReturnType FeasibleUpwardPlanarSubgraph::call( const Graph &G, GraphCopy &FUPS, adjEntry &extFaceHandle, List<edge> &delEdges, bool multisources) { FUPS = GraphCopy(G); delEdges.clear(); node s_orig; hasSingleSource(G, s_orig); List<edge> nonTreeEdges_orig; getSpanTree(FUPS, nonTreeEdges_orig, true, multisources); CombinatorialEmbedding Gamma(FUPS); nonTreeEdges_orig.permute(); // random order //insert nonTreeEdges while (!nonTreeEdges_orig.empty()) { // make identical copy GC of Fups //and insert e_orig in GC GraphCopy GC = FUPS; edge e_orig = nonTreeEdges_orig.popFrontRet(); //node a = GC.copy(e_orig->source()); //node b = GC.copy(e_orig->target()); GC.newEdge(e_orig); if (UpwardPlanarity::upwardPlanarEmbed_singleSource(GC)) { //upward embedded the fups and check feasibility CombinatorialEmbedding Beta(GC); //choose a arbitrary feasibel ext. face FaceSinkGraph fsg(Beta, GC.copy(s_orig)); SList<face> ext_faces; fsg.possibleExternalFaces(ext_faces); OGDF_ASSERT(!ext_faces.empty()); Beta.setExternalFace(ext_faces.front()); GraphCopy M = GC; // use a identical copy of GC to constrcut the merge graph of GC adjEntry extFaceHandle_cur = getAdjEntry(Beta, GC.copy(s_orig), Beta.externalFace()); adjEntry adj_orig = GC.original(extFaceHandle_cur->theEdge())->adjSource(); if (constructMergeGraph(M, adj_orig, nonTreeEdges_orig)) { FUPS = GC; extFaceHandle = FUPS.copy(GC.original(extFaceHandle_cur->theEdge()))->adjSource(); continue; } else { //Beta is not feasible delEdges.pushBack(e_orig); } } else { // not ok, GC is not feasible delEdges.pushBack(e_orig); } } return Module::retFeasible; }
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); } }
void GEMLayout::computeImpulse(GraphCopy &G, GraphCopyAttributes &AG,node v) { //const Graph &G = AG.constGraph(); int n = G.numberOfNodes(); double deltaX,deltaY,delta,deltaSqu; double desiredLength,desiredSqu; // add double node radius to desired edge length desiredLength = m_desiredLength + length(AG.getHeight(v),AG.getWidth(v)); desiredSqu = desiredLength * desiredLength; // compute attraction to center of gravity m_newImpulseX = (m_barycenterX / n - AG.x(v)) * m_gravitationalConstant; m_newImpulseY = (m_barycenterY / n - AG.y(v)) * m_gravitationalConstant; // disturb randomly int maxIntDisturbance = (int)(m_maximalDisturbance * 10000); std::uniform_int_distribution<> dist(-maxIntDisturbance,maxIntDisturbance); m_newImpulseX += (dist(m_rng) / 10000.0); m_newImpulseY += (dist(m_rng) / 10000.0); // compute repulsive forces for(node u : G.nodes) if(u != v ) { deltaX = AG.x(v) - AG.x(u); deltaY = AG.y(v) - AG.y(u); delta = length(deltaX,deltaY); if(OGDF_GEOM_ET.greater(delta,0.0)) { deltaSqu = delta * delta; m_newImpulseX += deltaX * desiredSqu / deltaSqu; m_newImpulseY += deltaY * desiredSqu / deltaSqu; } } // compute attractive forces for(adjEntry adj : v->adjEntries) { node u = adj->twinNode(); deltaX = AG.x(v) - AG.x(u); deltaY = AG.y(v) - AG.y(u); delta = length(deltaX,deltaY); if(m_attractionFormula == 1) { m_newImpulseX -= deltaX * delta / (desiredLength * weight(v)); m_newImpulseY -= deltaY * delta / (desiredLength * weight(v)); } else { deltaSqu = delta * delta; m_newImpulseX -= deltaX * deltaSqu / (desiredSqu * weight(v)); m_newImpulseY -= deltaY * deltaSqu / (desiredSqu * weight(v)); } } }
Module::ReturnType PlanarSubgraphModule::callAndDelete( GraphCopy &PG, const List<edge> &preferedEdges, List<edge> &delOrigEdges, bool preferedImplyPlanar) { List<edge> delEdges; ReturnType retValue = call(PG, preferedEdges, delEdges, preferedImplyPlanar); if(isSolution(retValue)) { ListConstIterator<edge> it; for(it = delEdges.begin(); it.valid(); ++it) { edge eCopy = *it; delOrigEdges.pushBack(PG.original(eCopy)); PG.delCopy(eCopy); } } return retValue; }
void GraphCopy::initGC(const GraphCopy &GC, NodeArray<node> &vCopy, EdgeArray<edge> &eCopy) { createEmpty(*GC.m_pGraph); for(node v : GC.nodes) m_vOrig[vCopy[v]] = GC.original(v); for(edge e : GC.edges) m_eOrig[eCopy[e]] = GC.original(e); for (node v : nodes) { node w = m_vOrig[v]; if (w != nullptr) m_vCopy[w] = v; } for(edge e : m_pGraph->edges) { ListConstIterator<edge> it; for (edge ei : GC.m_eCopy[e]) m_eIterator[eCopy[ei]] = m_eCopy[e].pushBack(eCopy[ei]); } }
void FPPLayout::computeCoordinates(const GraphCopy &G, IPoint &boundingBox, GridLayout &gridLayout, NodeArray<int> &num, NodeArray<adjEntry> &e_wp, NodeArray<adjEntry> &e_wq) { NodeArray<int> &x = gridLayout.x(); NodeArray<int> &y = gridLayout.y(); const int n = G.numberOfNodes(); NodeArray<int> x_rel(G); NodeArray<node> upper(G); NodeArray<node> next(G); Array<node, int> v(1, n); node w, vk, wp, wq; int k, xq, dx; forall_nodes(w, G) { v[num[w]] = (node) w; }
void FeasibleUpwardPlanarSubgraph::getSpanTree(GraphCopy &GC, List<edge> &delEdges, bool random, bool multisource) { delEdges.clear(); if (GC.numberOfNodes() == 1) return; // nothing to do node s; hasSingleSource(GC, s); NodeArray<bool> visited(GC, false); EdgeArray<bool> isTreeEdge(GC,false); List<node> toDo; // the original graph is a multisource graph. The sources are connected with the super source s. // so do not delete the incident edges of s if (multisource){ // put all incident edges of the source to treeEdges adjEntry adj; forall_adj(adj, s) { isTreeEdge[adj->theEdge()] = true; visited[adj->theEdge()->target()]; toDo.pushBack(adj->theEdge()->target()); } }
void ExpandedGraph2::constructDual(node s, node t, GraphCopy &GC, const EdgeArray<bool> *forbiddenEdgeOrig) { m_dual.clear(); FaceArray<node> faceNode(m_E); // constructs nodes (for faces in exp) face f; forall_faces(f,m_E) { faceNode[f] = m_dual.newNode(); } // construct dual edges (for primal edges in exp) node v; forall_nodes(v,m_exp) { adjEntry adj; forall_adj(adj,v) { // cannot cross edges that does not correspond to real edges adjEntry adjG = m_expToG[adj]; if(adjG == 0) continue; // Do not insert edges into dual if crossing the original edge // is forbidden if(forbiddenEdgeOrig && (*forbiddenEdgeOrig)[GC.original(m_BC.dynamicSPQRForest().original(m_expToG[adj]->theEdge()))] == true) continue; node vLeft = faceNode[m_E.leftFace (adj)]; node vRight = faceNode[m_E.rightFace(adj)]; m_primalEdge[m_dual.newEdge(vLeft,vRight)] = adj; }
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)); }
/* * Construct the realiszer and the Tree T * rValues = realizer value * T = Tree */ void SchnyderLayout::realizer( GraphCopy& G, const List<node>& L, node a, node b, node c, EdgeArray<int>& rValues, GraphCopy& T) { int i = 0; edge e; NodeArray<int> ord(G, 0); // ordering: b,c,L,a ord[b] = i++; ord[c] = i++; for(node v : L) { ord[v] = i++; // enumerate V(G) } ord[a] = i++; // remove all edges (they will be re-added later with different orientation) while (T.numberOfEdges() > 0) { e = T.firstEdge(); T.delEdge(e); } for(node v : L) { node u = T.copy(G.original(v)); // u is copy of v in T adjEntry adj = nullptr; for(adjEntry adjRun : v->adjEdges) { if (ord[adjRun->twinNode()] > ord[v]) { adj = adjRun; break; } } adjEntry adj1 = adj; while (ord[adj1->twinNode()] > ord[v]) { adj1 = adj1->cyclicSucc(); } e = T.newEdge(T.copy(G.original(adj1->twinNode())), u); rValues[e] = 2; adjEntry adj2 = adj; while (ord[adj2->twinNode()] > ord[v]) { adj2 = adj2->cyclicPred(); } e = T.newEdge(T.copy(G.original(adj2->twinNode())), u); rValues[e] = 3; for (adj = adj1->cyclicSucc(); adj != adj2; adj = adj->cyclicSucc()) { e = T.newEdge(u, T.copy(G.original(adj->twinNode()))); rValues[e] = 1; } } // special treatement of a,b,c node a_in_T = T.copy(G.original(a)); node b_in_T = T.copy(G.original(b)); node c_in_T = T.copy(G.original(c)); // all edges to node a get realizer value 1 for(adjEntry adj : a->adjEdges) { e = T.newEdge(a_in_T, T.copy(G.original(adj->twinNode()))); rValues[e] = 1; } // rest of outer triangle (reciprocal linked, realizer values 2 and 3) e = T.newEdge(b_in_T, a_in_T); rValues[e] = 2; e = T.newEdge(b_in_T, c_in_T); rValues[e] = 2; e = T.newEdge(c_in_T, a_in_T); rValues[e] = 3; e = T.newEdge(c_in_T, b_in_T); rValues[e] = 3; }
void GEMLayout::call(GraphAttributes &AG) { const Graph &G = AG.constGraph(); if(G.empty()) return; OGDF_ASSERT(m_numberOfRounds >= 0); OGDF_ASSERT(DIsGreaterEqual(m_minimalTemperature,0)); OGDF_ASSERT(DIsGreaterEqual(m_initialTemperature,m_minimalTemperature)); OGDF_ASSERT(DIsGreaterEqual(m_gravitationalConstant,0)); OGDF_ASSERT(DIsGreaterEqual(m_desiredLength,0)); OGDF_ASSERT(DIsGreaterEqual(m_maximalDisturbance,0)); OGDF_ASSERT(DIsGreaterEqual(m_rotationAngle,0)); OGDF_ASSERT(DIsLessEqual(m_rotationAngle,pi / 2)); OGDF_ASSERT(DIsGreaterEqual(m_oscillationAngle,0)); OGDF_ASSERT(DIsLessEqual(m_oscillationAngle,pi / 2)); OGDF_ASSERT(DIsGreaterEqual(m_rotationSensitivity,0)); OGDF_ASSERT(DIsLessEqual(m_rotationSensitivity,1)); OGDF_ASSERT(DIsGreaterEqual(m_oscillationSensitivity,0)); OGDF_ASSERT(DIsLessEqual(m_oscillationSensitivity,1)); OGDF_ASSERT(m_attractionFormula == 1 || m_attractionFormula == 2); // 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); node v; forall_nodes(v,G) 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); node vCopy; forall_nodes(vCopy, GC) { node vOrig = GC.original(vCopy); AGC.x(vCopy) = AG.x(vOrig); AGC.y(vCopy) = AG.y(vOrig); } SList<node> permutation; node v; // 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; forall_nodes(v,GC) { m_barycenterX += weight(v) * AGC.x(v); m_barycenterY += weight(v) * AGC.y(v); }
//outputs the set of feasible solutions void ClusterPlanarity::writeFeasible(const char *filename, CP_MasterBase &master, Master::STATUS &status) { const ClusterGraph& CG = *(master.getClusterGraph()); const Graph& G = CG.constGraph(); //first compute the nodepairs that are potential candidates to connect //chunks in a cluster //potential connection edges NodeArray< NodeArray<bool> > potConn(G); for(node v : G.nodes) { potConn[v].init(G, false); } //we perform a bottom up cluster tree traversal List< cluster > clist; getBottomUpClusterList(CG.rootCluster(), clist); //could use postordertraversal instead List< nodePair > connPairs; //holds all connection node pairs //counts the number of potential connectivity edges //int potCount = 0; //equal to number of true values in potConn //we run through the clusters and check connected components //we consider all possible edges connecting CCs in a cluster, //even if they may be connected by edges in a child cluster //(to get the set of all feasible solutions) for(cluster c : clist) { //we compute the subgraph induced by vertices in c GraphCopy gcopy; gcopy.createEmpty(G); List<node> clusterNodes; //would be more efficient if we would just merge the childrens' vertices //and add c's c->getClusterNodes(clusterNodes); NodeArray<bool> activeNodes(G, false); //true for all cluster nodes EdgeArray<edge> copyEdge(G); //holds the edge copy for(node v : clusterNodes) activeNodes[v] = true; gcopy.initByActiveNodes(clusterNodes, activeNodes, copyEdge); //gcopy now represents the cluster induced subgraph //we compute the connected components and store all nodepairs //that connect two of them NodeArray<int> component(gcopy); connectedComponents(gcopy, component); //now we run over all vertices and compare the component //number of adjacent vertices. If they differ, we found a //potential connection edge. We do not care if we find them twice. for(node v : gcopy.nodes) { for(node w : gcopy.nodes) { if (component[v] != component[w]) { cout <<"Indizes: "<<v->index()<<":"<<w->index()<<"\n"; node vg = gcopy.original(v); node wg = gcopy.original(w); bool newConn = !((vg->index() < wg->index()) ? potConn[vg][wg] : potConn[wg][vg]); if (newConn) { nodePair np; np.v1 = vg; np.v2 = wg; connPairs.pushBack(np); if (vg->index() < wg->index()) potConn[vg][wg] = true; else potConn[wg][vg] = true; } } }//nodes }//nodes } cout << "Number of potential connection edges: "<< connPairs.size()<<"\n"; //we run through our candidates and save them in an array //that can be used for dynamic graph updates int i = 0; connStruct *cons = new connStruct[connPairs.size()]; for(const nodePair &np : connPairs) { connStruct cs; cs.connected = false; cs.v1 = np.v1; cs.v2 = np.v2; cs.e = nullptr; cons[i] = cs; i++; } //------------------------------------------------------------------------- // WARNING: this is extremely slow for graphs with a large number of cluster // chunks now we test all possible connection edge combinations for c-planarity Graph G2; NodeArray<node> origNodes(CG.constGraph()); ClusterArray<cluster> origCluster(CG); EdgeArray<edge> origEdges(CG.constGraph()); ClusterGraph testCopy(CG, G2, origCluster, origNodes, origEdges); ofstream os(filename); // Output dimension of the LP (number of variables) os << "DIM = " << connPairs.size() << "\n"; os << "COMMENT\n"; switch (status) { case Master::Optimal: os << "Optimal \n\n"; break; case Master::Error: os << "Error \n\n"; break; default: os << "unknown \n\n"; } for (i = 0; i < connPairs.size(); i++) { os << "Var " << i << ": " << origNodes[cons[i].v1]->index() << "->" << origNodes[cons[i].v2] << "\n"; } os << "CONV_SECTION\n"; #ifdef writefeasiblegraphs int j = 0; //debug #endif if (connPairs.size() > 0) while (true) { //we create the next test configuration by incrementing the edge selection array //we create the corresponding graph dynamically on the fly i = 0; while ( (i < connPairs.size()) && (cons[i].connected == true) ) { cons[i].connected = false; OGDF_ASSERT(cons[i].e != 0); G2.delEdge(cons[i].e); i++; }//while if (i >= connPairs.size()) break; //cout<<"v1graph: "<<&(*(cons[i].v1->graphOf()))<<"\n"; //cout<<"origNodesgraph: "<<&(*(origNodes.graphOf()))<<"\n"; cons[i].connected = true; //i.e., (false) will never be a feasible solution cons[i].e = G2.newEdge(origNodes[cons[i].v1], origNodes[cons[i].v2]); //and test it for c-planarity CconnectClusterPlanar CCCP; bool cplanar = CCCP.call(testCopy); //c-planar graphs define a feasible solution if (cplanar) { #ifdef OGDF_DEBUG cout << "Feasible solution found\n"; #endif for (int j = 0; j < connPairs.size(); j++) { char ch = (cons[j].connected ? '1' : '0'); cout << ch; os << ch << " "; } cout << "\n"; os << "\n"; #ifdef writefeasiblegraphs string fn = "cGraph"; fn += to_string(j++) + ".gml"; GraphIO::writeGML(testCopy, fn); #endif } }//while counting delete[] cons; os << "\nEND" <<"\n"; os.close(); //return; os.open(getIeqFileName()); os << "DIM = " << m_numVars << "\n"; // Output the status as a comment os << "COMMENT\n"; switch (status) { case Master::Optimal: os << "Optimal \n\n"; break; case Master::Error: os << "Error \n\n"; break; default: os << "unknown \n\n"; } // In case 0 is not a valid solution, some PORTA functions need //a valid solution in the ieq file os << "VALID\n"; os << "\nLOWER_BOUNDS\n"; for (i = 0; i < m_numVars; i++) os << "0 "; os << "\n"; os << "\nHIGHER_BOUNDS\n"; for (i = 0; i < m_numVars; i++) os << "1 "; os << "\n"; os << "\nINEQUALITIES_SECTION\n"; //we first read the standard constraint that are written //into a text file by the optimization master ifstream isf(master.getStdConstraintsFileName()); if (!isf) { cerr << "Could not open optimization master's standard constraint file\n"; os << "#No standard constraints read\n"; } else { char* fileLine = new char[maxConLength()]; while (isf.getline(fileLine, maxConLength())) { //skip commment lines if (fileLine[0] == '#') continue; int count = 1; std::istringstream iss(fileLine); char d; bool rhs = false; while (iss >> d) { if ( rhs || ( (d == '<') || (d == '>') || (d == '=') ) ) { os << d; rhs = true; } else { if (d != '0') { os <<"+"<< d <<"x"<<count; } count++; } }//while chars os <<"\n"; } delete[] fileLine; }//ifstream //now we read the cut pools from the master if (master.useDefaultCutPool()) { os << "#No cut constraints read from master\n"; //StandardPool<Constraint, Variable> *connCon = master.cutPool(); } else { StandardPool<Constraint, Variable> *connCon = master.getCutConnPool(); StandardPool<Constraint, Variable> *kuraCon = master.getCutKuraPool(); StandardPool<Variable, Constraint> *stdVar = master.varPool(); OGDF_ASSERT(connCon != 0); OGDF_ASSERT(kuraCon != 0); cout << connCon->number() << " Constraints im MasterConnpool \n"; cout << kuraCon->number() << " Constraints im MasterKurapool \n"; cout << connCon->size() << " Größe ConnPool"<<"\n"; outputCons(os, connCon, stdVar); outputCons(os, kuraCon, stdVar); }//else os << "\nEND" <<"\n"; os.close(); cout << "Cutting is set: "<<master.cutting()<<"\n"; //cout <<"Bounds for the variables:\n"; //Sub &theSub = *(master.firstSub()); //for ( i = 0; i < theSub.nVar(); i++) //{ // cout << i << ": " << theSub.lBound(i) << " - " << theSub.uBound(i) << "\n"; //} /*// OLD CRAP cout << "Constraints: \n"; StandardPool< Constraint, Variable > *spool = master.conPool(); StandardPool< Constraint, Variable > *cpool = master.cutPool(); cout << spool->size() << " Constraints im Masterpool \n"; cout << cpool->size() << " Constraints im Mastercutpool \n"; cout << "ConPool Constraints \n"; for ( i = 0; i < spool->size(); i++) { PoolSlot< Constraint, Variable > * sloty = spool->slot(i); Constraint *mycon = sloty->conVar(); switch (mycon->sense()->sense()) { case CSense::Less: cout << "<" << "\n"; break; case CSense::Greater: cout << ">" << "\n"; break; case CSense::Equal: cout << "=" << "\n"; break; default: cout << "Inequality sense doesn't make any sense \n"; break; }//switch } cout << "CutPool Constraints \n"; for ( i = 0; i < cpool->size(); i++) { PoolSlot< Constraint, Variable > * sloty = cpool->slot(i); Constraint *mycon = sloty->conVar(); switch (mycon->sense()->sense()) { case CSense::Less: cout << "<" << "\n"; break; case CSense::Greater: cout << ">" << "\n"; break; case CSense::Equal: cout << "=" << "\n"; break; default: cout << "Inequality sense doesn't make any sense \n"; break; }//switch } */ /* for ( i = 0; i < theSub.nCon(); i++) { Constraint &theCon = *(theSub.constraint(i)); for ( i = 0; i < theSub.nVar(); i++) { double c = theCon.coeff(theSub.variable(i)); if (c != 0) cout << c; else cout << " "; } switch (theCon.sense()->sense()) { case CSense::Less: cout << "<" << "\n"; break; case CSense::Greater: cout << ">" << "\n"; break; case CSense::Equal: cout << "=" << "\n"; break; default: cout << "doesn't make any sense \n"; break; }//switch float fl; while(!(std::cin >> fl)) { std::cin.clear(); std::cin.ignore(numeric_limits<streamsize>::max(),'\n'); } }*/ }//writeportaieq
void SubgraphUpwardPlanarizer::merge( const GraphCopy &GC, UpwardPlanRep &UPR_res, const GraphCopy &block, UpwardPlanRep &UPR) { node startUPR = UPR.getSuperSource()->firstAdj()->theEdge()->target(); node startRes; node startG = GC.original(block.original(UPR.original(startUPR))); bool empty = UPR_res.empty(); if (empty) { OGDF_ASSERT(startG == 0); // contruct a node in UPR_res assocciated with startUPR startRes = UPR_res.newNode(); UPR_res.m_isSinkArc.init(UPR_res, false); UPR_res.m_isSourceArc.init(UPR_res, false); UPR_res.s_hat = startRes; } else { startRes = UPR_res.copy(startG); } OGDF_ASSERT(startRes != 0); // compute the adjEntry position (in UPR_res) of the cutvertex startRes adjEntry pos = nullptr; if (!empty) { adjEntry adj_ext = nullptr, adj_int = nullptr; for(adjEntry run : startRes->adjEntries) { if (UPR_res.getEmbedding().rightFace(run) == UPR_res.getEmbedding().externalFace()) { adj_ext = run; break; } if (run->theEdge()->source() == startRes) adj_int = run; } // cutvertex is a sink in UPR_res if (adj_ext == nullptr && adj_int == nullptr) { pos = UPR_res.sinkSwitchOf(startRes); } else { if (adj_ext == nullptr) pos = adj_int; else pos = adj_ext; } OGDF_ASSERT(pos != 0); } // construct for each node (except the two super sink and the super source) of UPR a associated of UPR to UPR_res NodeArray<node> nodeUPR2UPR_res(UPR, nullptr); nodeUPR2UPR_res[startUPR] = startRes; for(node v : UPR.nodes) { // allready constructed or is super sink or super source if (v == startUPR || v == UPR.getSuperSink() || v == UPR.getSuperSink()->firstAdj()->theEdge()->source() || v == UPR.getSuperSource()) continue; node vNew; if (UPR.original(v) != nullptr ) { node vG = GC.original(block.original((UPR.original(v)))); if (vG != nullptr) vNew = UPR_res.newNode(vG); else vNew = UPR_res.newNode(); //vG is the super source } else // crossing dummy, no original node vNew = UPR_res.newNode(); nodeUPR2UPR_res[v] = vNew; } //add edges of UPR to UPR_res EdgeArray<edge> edgeUPR2UPR_res(UPR, nullptr); for(edge e : block.edges) { if (e->source()->indeg()==0) // the artificial edge with the super source continue; List<edge> chains = UPR.chain(e); edge eG = nullptr, eGC = block.original(e); eG = GC.original(eGC); OGDF_ASSERT(!chains.empty()); //construct new edges in UPR_res for(edge eChain : chains) { node tgt = nodeUPR2UPR_res[eChain->target()]; node src = nodeUPR2UPR_res[eChain->source()]; edge eNew = UPR_res.newEdge(src, tgt); edgeUPR2UPR_res[eChain] = eNew; if (UPR.isSinkArc(UPR.copy(e))) UPR_res.m_isSinkArc[eNew] = true; if (UPR.isSourceArc(UPR.copy(e))) UPR_res.m_isSourceArc[eNew] = true; if (eG == nullptr) { // edge is associated with a sink arc UPR_res.m_eOrig[eNew] = nullptr; continue; } UPR_res.m_eOrig[eNew] = eG; if (chains.size() == 1) { // e is not split UPR_res.m_eCopy[eG].pushBack(eNew); UPR_res.m_eIterator[eNew] = UPR_res.m_eCopy[eG].begin(); break; } UPR_res.m_eCopy[eG].pushBack(eNew); UPR_res.m_eIterator[eNew] = UPR_res.m_eCopy[eG].rbegin(); } } ///* //* embed the new component in UPR_res with respect to the embedding of UPR //*/ // for the cut vertex if (!empty) { adjEntry run = UPR.getAdjEntry(UPR.getEmbedding(), startUPR, UPR.getEmbedding().externalFace()); run = run->cyclicSucc(); adjEntry adjStart = run; do { if (edgeUPR2UPR_res[run->theEdge()] != nullptr) { adjEntry adj_UPR_res = edgeUPR2UPR_res[run->theEdge()]->adjSource(); UPR_res.moveAdjAfter(adj_UPR_res, pos); pos = adj_UPR_res; } run = run->cyclicSucc(); } while(run != adjStart); } for(node v : UPR.nodes) { if (v == startUPR && !empty) continue; node v_UPR_res = nodeUPR2UPR_res[v]; List<adjEntry> adj_UPR, adj_UPR_res; v->allAdjEntries(adj_UPR); // convert adj_UPR of v to adj_UPR_res of v_UPR_res for(adjEntry adj : adj_UPR) { edge e_res = edgeUPR2UPR_res[adj->theEdge()]; if (e_res == nullptr) // associated edges in UPR_res continue; adjEntry adj_res = e_res->adjSource(); if (adj_res->theNode() != v_UPR_res) adj_res = adj_res->twin(); adj_UPR_res.pushBack(adj_res); } UPR_res.sort(v_UPR_res, adj_UPR_res); } /* //---------------------------------------------------debug if (!UPR_res.empty()) { GraphAttributes GA_UPR_res(UPR_res, GraphAttributes::nodeGraphics| GraphAttributes::edgeGraphics| GraphAttributes::nodeColor| GraphAttributes::edgeColor| GraphAttributes::nodeLabel| GraphAttributes::edgeLabel ); GA_UPR_res.setAllHeight(30.0); GA_UPR_res.setAllWidth(30.0); // label the nodes with their index for(node z : GA_UPR_res.constGraph().nodes) { GA_UPR_res.label(z) = to_string(z->index()); } GA_UPR_res.writeGML("c:/temp/UPR_res_tmp.gml"); cout << "UPR_res_tmp faces:"; UPR_res.outputFaces(UPR_res.getEmbedding()); } GraphAttributes GA_UPR(UPR, GraphAttributes::nodeGraphics| GraphAttributes::edgeGraphics| GraphAttributes::nodeColor| GraphAttributes::edgeColor| GraphAttributes::nodeLabel| GraphAttributes::edgeLabel ); GA_UPR.setAllHeight(30.0); GA_UPR.setAllWidth(30.0); // label the nodes with their index for(node z : GA_UPR.constGraph().nodes) { GA_UPR.label(z) = to_string(z->index()); } GA_UPR.writeGML("c:/temp/UPR_tmp.gml"); cout << "UPR_tmp faces:"; UPR.outputFaces(UPR.getEmbedding()); //end -----------------------------------------------debug */ // update UPR_res UPR_res.initMe(); }
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); int numberOfComponents = connectedComponents(G, componentNumber); if (numberOfComponents == 0) { return; } // intialize the array of lists of nodes contained in a CC Array<List<node> > nodesInCC(numberOfComponents); for(node v : G.nodes) 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 < numberOfComponents; i++) { GC.initByNodes(nodesInCC[i],auxCopy); GraphAttributes cGA(GC, GA.attributes()); //copy information into copy GA for(node v : GC.nodes) { 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)); } // copy information on edges if (GA.attributes() & GraphAttributes::edgeDoubleWeight) { for (edge e : GC.edges) { cGA.doubleWeight(e) = GA.doubleWeight(GC.original(e)); } } m_secondaryLayout.get().call(cGA); //copy layout information back into GA for(node v : GC.nodes) { node w = GC.original(v); if (w != nullptr) { GA.x(w) = cGA.x(v); GA.y(w) = cGA.y(v); if (GA.attributes() & GraphAttributes::threeD) { GA.z(w) = cGA.z(v); } } } } // rotate component drawings and call the packer reassembleDrawings(GA, nodesInCC); }//if valid }
void SchnyderLayout::schnyderEmbedding( GraphCopy& GC, GridLayout &gridLayout, adjEntry adjExternal) { NodeArray<int> &xcoord = gridLayout.x(); NodeArray<int> &ycoord = gridLayout.y(); node v; List<node> L; // (un)contraction order GraphCopy T = GraphCopy(GC); // the realizer tree (reverse direction of edges!!!) EdgeArray<int> rValues(T); // the realizer values // choose outer face a,b,c adjEntry adja; if (adjExternal != 0) { edge eG = adjExternal->theEdge(); edge eGC = GC.copy(eG); adja = (adjExternal == eG->adjSource()) ? eGC->adjSource() : eGC->adjTarget(); } else { adja = GC.firstEdge()->adjSource(); } adjEntry adjb = adja->faceCyclePred(); adjEntry adjc = adjb->faceCyclePred(); node a = adja->theNode(); node b = adjb->theNode(); node c = adjc->theNode(); node a_in_T = T.copy(GC.original(a)); node b_in_T = T.copy(GC.original(b)); node c_in_T = T.copy(GC.original(c)); contract(GC, a, b, c, L); realizer(GC, L, a, b, c, rValues, T); NodeArray<int> t1(T); NodeArray<int> t2(T); NodeArray<int> val(T, 1); NodeArray<int> P1(T); NodeArray<int> P3(T); NodeArray<int> v1(T); NodeArray<int> v2(T); subtreeSizes(rValues, 1, a_in_T, t1); subtreeSizes(rValues, 2, b_in_T, t2); prefixSum(rValues, 1, a_in_T, val, P1); prefixSum(rValues, 3, c_in_T, val, P3); // now Pi = depth of all nodes in Tree T(i) (depth[root] = 1) prefixSum(rValues, 2, b_in_T, t1, v1); // special treatment for a v1[a_in_T] = t1[a_in_T]; /* * v1[v] now is the sum of the * "count of nodes in t1" minus the "subtree size for node x" * for every node x on a path from b to v in t2 */ prefixSum(rValues, 3, c_in_T, t1, val); // special treatment for a val[a_in_T] = t1[a_in_T]; /* * val[v] now is the sum of the * "count of nodes in t1" minus the "subtree size for node x" * for every node x on a path from c to v in t3 */ // r1[v]=v1[v]+val[v]-t1[v] is the number of nodes in region 1 from v forall_nodes(v, T) { // calc v1' v1[v] += val[v] - t1[v] - P3[v]; }
void FPPLayout::computeOrder( const GraphCopy &G, NodeArray<int> &num, NodeArray<adjEntry> &e_wp, NodeArray<adjEntry> &e_wq, adjEntry e_12, adjEntry e_2n, adjEntry e_n1) { NodeArray<int> num_diag(G, 0); // number of chords // link[v] = Iterator in possible, that points to v (if diag[v] = 0 and outer[v] = TRUE) NodeArray<ListIterator<node> > link(G, 0); // outer[v] = TRUE <=> v is a node of the actual outer face NodeArray<bool> outer(G, false); // List of all nodes v with outer[v] = TRUE and diag[v] = 0 List<node> possible; // nodes of the outer triangle (v_1,v_2,v_n) node v_1 = e_12->theNode(); node v_2 = e_2n->theNode(); node v_n = e_n1->theNode(); node v_k, wp, wq, u; adjEntry e, e2; int k; // initialization: beginn with outer face (v_1,v_2,v_n) // v_n is the only possible node num[v_1] = 1; num[v_2] = 2; outer[v_1] = true; outer[v_2] = true; outer[v_n] = true; link[v_n] = possible.pushBack(v_n); e_wq[v_1] = e_n1->twin(); e_wp[v_2] = e_2n; e_wq[v_n] = e_2n->twin(); e_wp[v_n] = e_n1; // select next v_k and delete it for (k = G.numberOfNodes(); k >= 3; k--) { v_k = possible.popFrontRet(); // select arbitrary node from possible as v_k num[v_k] = k; // predecessor wp and successor wq from vk in C_k (actual outer face) wq = (e_wq [v_k])->twinNode(); wp = (e_wp [v_k])->twinNode(); // v_k not in C_k-1 anymore outer[v_k] = false; // shortfall of a chord? if (e_wq[wp]->cyclicSucc()->twinNode() == wq) { // wp, wq is the only successor of vk in G_k // wp, wq loose a chord if (--num_diag[wp] == 0) { link[wp] = possible.pushBack(wp); } if (--num_diag[wq] == 0) { link[wq] = possible.pushBack(wq); } } // update or initialize e_wq, e_wp e_wq[wp] = e_wq[wp]->cyclicSucc(); e_wp[wq] = e_wp[wq]->cyclicPred(); e = e_wq[wp]; for (u = e->twinNode(); u != wq; u = e->twinNode()) { outer[u] = true; e_wp[u] = e->twin(); e = e_wq[u] = e_wp[u]->cyclicSucc()->cyclicSucc(); // search for new chords for (e2 = e_wp[u]->cyclicPred(); e2 != e_wq[u]; e2 = e2->cyclicPred()) { node w = e2->twinNode(); if (outer[w] == true) { ++num_diag[u]; if (w != v_1 && w != v_2) if (++num_diag[w] == 1) possible.del(link[w]); } } if (num_diag[u] == 0) { link[u] = possible.pushBack(u); } } } }
//todo: is called only once, but could be sped up the same way as the co-conn check void MaxCPlanarMaster::clusterConnection(cluster c, GraphCopy &gc, double &upperBoundC) { // For better performance, a node array is used to indicate which nodes are contained // in the currently considered cluster. NodeArray<bool> vInC(gc,false); // First check, if the current cluster \a c is a leaf cluster. // If so, compute the number of edges that have at least to be added // to make the cluster induced graph connected. if (c->cCount()==0) { //cluster \a c is a leaf cluster GraphCopy *inducedC = new GraphCopy((const Graph&)gc); List<node> clusterNodes; c->getClusterNodes(clusterNodes); // \a clusterNodes now contains all (original) nodes of cluster \a c. for (node w : clusterNodes) { vInC[gc.copy(w)] = true; } // Delete all nodes from \a inducedC that do not belong to the cluster, // in order to obtain the cluster induced graph. node v = inducedC->firstNode(); while (v!=nullptr) { node w = v->succ(); if (!vInC[inducedC->original(v)]) inducedC->delNode(v); v = w; } // Determine number of connected components of cluster induced graph. //Todo: check could be skipped if (!isConnected(*inducedC)) { NodeArray<int> conC(*inducedC); int nCC = connectedComponents(*inducedC,conC); //at least #connected components - 1 edges have to be added. upperBoundC -= (nCC-1)*m_largestConnectionCoeff; } delete inducedC; // Cluster \a c is an "inner" cluster. Process all child clusters first. } else { //c->cCount is != 0, process all child clusters first for (cluster ci : c->children) { clusterConnection(ci, gc, upperBoundC); } // Create cluster induced graph. GraphCopy *inducedC = new GraphCopy((const Graph&)gc); List<node> clusterNodes; c->getClusterNodes(clusterNodes); //\a clusterNodes now contains all (original) nodes of cluster \a c. for (node w : clusterNodes) { vInC[gc.copy(w)] = true; } node v = inducedC->firstNode(); while (v!=nullptr) { node w = v->succ(); if (!vInC[inducedC->original(v)]) inducedC->delNode(v); v = w; } // Now collapse each child cluster to one node and determine #connected components of \a inducedC. List<node> oChildClusterNodes; List<node> cChildClusterNodes; for (cluster ci : c->children) { ci->getClusterNodes(oChildClusterNodes); // Compute corresponding nodes of graph \a inducedC. for (node u : oChildClusterNodes) { node copy = inducedC->copy(gc.copy(u)); cChildClusterNodes.pushBack(copy); } inducedC->collapse(cChildClusterNodes); oChildClusterNodes.clear(); cChildClusterNodes.clear(); } // Now, check \a inducedC for connectivity. if (!isConnected(*inducedC)) { NodeArray<int> conC(*inducedC); int nCC = connectedComponents(*inducedC,conC); //at least #connected components - 1 edges have to added. upperBoundC -= (nCC-1)*m_largestConnectionCoeff; } delete inducedC; } }//clusterConnection
bool FUPSSimple::constructMergeGraph(GraphCopy &M, adjEntry adj_orig, const List<edge> &orig_edges) { CombinatorialEmbedding Beta(M); //set ext. face of Beta adjEntry ext_adj = M.copy(adj_orig->theEdge())->adjSource(); Beta.setExternalFace(Beta.rightFace(ext_adj)); //*************************** debug ******************************** /* cout << endl << "FUPS : " << endl; for(face ff : Beta.faces) { cout << "face " << ff->index() << ": "; adjEntry adjNext = ff->firstAdj(); do { cout << adjNext->theEdge() << "; "; adjNext = adjNext->faceCycleSucc(); } while(adjNext != ff->firstAdj()); cout << endl; } if (Beta.externalFace() != 0) cout << "ext. face of the graph is: " << Beta.externalFace()->index() << endl; else cout << "no ext. face set." << endl; */ FaceSinkGraph fsg(Beta, M.copy(adj_orig->theNode())); SList<node> aug_nodes; SList<edge> aug_edges; SList<face> fList; fsg.possibleExternalFaces(fList); // use this method to call the methode checkForest() node v_ext = fsg.faceNodeOf(Beta.externalFace()); OGDF_ASSERT(v_ext != 0); fsg.stAugmentation(v_ext, M, aug_nodes, aug_edges); /* //------------------------------------debug GraphAttributes AG(M, GraphAttributes::nodeGraphics| GraphAttributes::edgeGraphics| GraphAttributes::nodeColor| GraphAttributes::edgeColor| GraphAttributes::nodeLabel| GraphAttributes::edgeLabel ); // label the nodes with their index for(node v : AG.constGraph().nodes) { AG.label(v) = to_string(v->index()); } AG.writeGML("c:/temp/MergeFUPS.gml"); */ OGDF_ASSERT(isStGraph(M)); //add the deleted edges for(edge eOrig : orig_edges) { node a = M.copy(eOrig->source()); node b = M.copy(eOrig->target()); M.newEdge(a, b); } return (isAcyclic(M)); }
void OptimalRanking::doCall( const Graph& G, NodeArray<int> &rank, EdgeArray<bool> &reversed, const EdgeArray<int> &length, const EdgeArray<int> &costOrig) { MinCostFlowReinelt<int> mcf; // construct min-cost flow problem 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); rank.init(G); for(int i = 0; i < numCC; ++i) { GC.initByNodes(nodesInCC[i], auxCopy); makeLoopFree(GC); for(edge e : GC.edges) if(reversed[GC.original(e)]) GC.reverseEdge(e); // special cases: if(GC.numberOfNodes() == 1) { rank[GC.original(GC.firstNode())] = 0; continue; } else if(GC.numberOfEdges() == 1) { edge e = GC.original(GC.firstEdge()); rank[e->source()] = 0; rank[e->target()] = length[e]; continue; } EdgeArray<int> lowerBound(GC,0); EdgeArray<int> upperBound(GC,mcf.infinity()); EdgeArray<int> cost(GC); NodeArray<int> supply(GC); for(edge e : GC.edges) cost[e] = -length[GC.original(e)]; for(node v : GC.nodes) { int s = 0; edge e; forall_adj_edges(e,v) { if(v == e->source()) s += costOrig[GC.original(e)]; else s -= costOrig[GC.original(e)]; } supply[v] = s; } OGDF_ASSERT(isAcyclic(GC) == true); // find min-cost flow EdgeArray<int> flow(GC); NodeArray<int> dual(GC); #ifdef OGDF_DEBUG bool feasible = #endif mcf.call(GC, lowerBound, upperBound, cost, supply, flow, dual); OGDF_ASSERT(feasible); for(node v : GC.nodes) rank[GC.original(v)] = dual[v]; } }
void SpringEmbedderFR::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); node v; forall_nodes(v,G) 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); node vCopy; forall_nodes(vCopy, GC) { node vOrig = GC.original(vCopy); AGC.x(vCopy) = AG.x(vOrig); AGC.y(vCopy) = AG.y(vOrig); } // original if (initialize(GC, AGC) == true) { for(int i = 1; i <= m_iterations; i++) mainStep(GC, AGC); } cleanup(); // end original node vFirst = GC.firstNode(); double minX = AGC.x(vFirst), maxX = AGC.x(vFirst), minY = AGC.y(vFirst), maxY = AGC.y(vFirst); forall_nodes(vCopy,GC) { 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; }