void MixedModelLayout::doCall( PlanRep &PG, adjEntry adjExternal, GridLayout &gridLayout, IPoint &boundingBox, bool fixEmbedding) { // handle graphs with less than 3 nodes node v1, v2; switch (PG.numberOfNodes()) { case 0: boundingBox = IPoint(0,0); return; case 1: v1 = PG.firstNode(); gridLayout.x(v1) = gridLayout.y(v1) = 0; boundingBox = IPoint(0,0); return; case 2: v1 = PG.firstNode(); v2 = v1->succ(); gridLayout.x(v1) = gridLayout.y(v1) = gridLayout.y(v2) = 0; gridLayout.x(v2) = 1; boundingBox = IPoint(1,0); return; } MixedModelBase mm(PG,gridLayout); if(fixEmbedding) { OGDF_ASSERT(PG.representsCombEmbedding()); PlanarAugmentationFix fixAugmenter; mm.computeOrder(fixAugmenter, 0, adjExternal, m_compOrder.get()); } else mm.computeOrder(m_augmenter.get(),&m_embedder.get(),0,m_compOrder.get()); mm.assignIopCoords(); mm.placeNodes(); mm.postprocessing1(); mm.setBends(); mm.postprocessing2(); m_crossingsBeautifier.get().call(PG,gridLayout); int xmin, ymin; gridLayout.computeBoundingBox(xmin,boundingBox.m_x,ymin,boundingBox.m_y); }
// // insert an arc for each edge with direction m_arcDir void CompactionConstraintGraphBase::insertBasicArcs(const PlanRep &PG) { const Graph &G = *m_pOR; for(node v : G.nodes) { node start = m_pathNode[v]; for(adjEntry adj : v->adjEdges) { if (m_pOR->direction(adj) == m_arcDir) { edge e = newEdge(start, m_pathNode[adj->theEdge()->opposite(v)]); m_edgeToBasicArc[adj] = e; m_cost[e] = m_edgeCost[PG.typeOf(adj->theEdge())]; //try to pull nodes up in hierarchies if ( (PG.typeOf(adj->theEdge()) == Graph::generalization) && (PG.typeOf(adj->theEdge()->target()) == Graph::generalizationExpander) && !(PG.isExpansionEdge(adj->theEdge())) ) { if (m_align) { //got to be higher than vertexarccost*doublebendfactor m_cost[e] = 4000*m_cost[e]; //use parameter later corresponding m_alignmentArc[e] = true; }//if align //to compconsgraph::doublebendfactor else m_cost[e] = 2*m_cost[e]; } //set generalization type if (verticalGen(adj->theEdge())) m_verticalArc[e] = true; //set onborder if (PG.isDegreeExpansionEdge(adj->theEdge())) { edge borderE = adj->theEdge(); node v1 = borderE->source(); node v2 = borderE->target(); m_border[e] = ((v1->degree()>2) && (v2->degree()>2) ? 2 : 1); } } } } }
face SimpleEmbedder::findBestExternalFace(const PlanRep& PG, const CombinatorialEmbedding& E) { FaceArray<int> weight(E); face f; forall_faces(f,E) weight[f] = f->size(); node v; forall_nodes(v,PG) { if(PG.typeOf(v) != Graph::generalizationMerger) continue; adjEntry adj; forall_adj(adj,v) { if(adj->theEdge()->source() == v) break; } OGDF_ASSERT(adj->theEdge()->source() == v); node w = adj->theEdge()->target(); bool isBase = true; adjEntry adj2; forall_adj(adj2, w) { edge e = adj2->theEdge(); if(e->target() != w && PG.typeOf(e) == Graph::generalization) { isBase = false; break; } } if(isBase == false) continue; face f1 = E.leftFace(adj); face f2 = E.rightFace(adj); weight[f1] += v->indeg(); if(f2 != f1) weight[f2] += v->indeg(); }
void IOPoints::restoreDeg1Nodes(PlanRep &PG, Stack<PlanRep::Deg1RestoreInfo> &S) { List<node> deg1s; PG.restoreDeg1Nodes(S,deg1s); ListConstIterator<node> it; for(it = deg1s.begin(); it.valid(); ++it) { adjEntry adj = (*it)->firstAdj(); m_mark[adj] = m_mark[adj->twin()] = true; } }
void SubgraphPlanarizer::CrossingStructure::restore(PlanRep &PG, int cc) { //PG.initCC(cc); Array<node> id2Node(0,m_numCrossings-1,0); SListPure<edge> edges; PG.allEdges(edges); for(SListConstIterator<edge> itE = edges.begin(); itE.valid(); ++itE) { edge ePG = *itE; edge e = PG.original(ePG); SListConstIterator<int> it; for(it = m_crossings[e].begin(); it.valid(); ++it) { node x = id2Node[*it]; edge ePGOld = ePG; ePG = PG.split(ePG); node y = ePG->source(); if(x == 0) { id2Node[*it] = y; } else { PG.moveTarget(ePGOld, x); PG.moveSource(ePG, x); PG.delNode(y); } } } }
DPoint Layout::computeBoundingBox(PlanRep &PG) const { double maxWidth = 0; double maxHeight = 0; // check rightmost and uppermost extension of all (original) nodes for(int i = PG.startNode(); i < PG.stopNode(); ++i) { node vG = PG.v(i); double maxX = x(PG.copy(vG)) + PG.widthOrig(vG)/2; if (maxX > maxWidth ) maxWidth = maxX; double maxY = y(PG.copy(vG)) + PG.heightOrig(vG)/2; if (maxY > maxHeight) maxHeight = maxY; // check polylines of all (original) edges for(adjEntry adj : vG->adjEntries) { if ((adj->index() & 1) == 0) continue; edge eG = adj->theEdge(); const List<edge> &path = PG.chain(eG); for(edge e : path) { // we have to check (only) all interior points, i.e., we can // omitt the first and last point since it lies in the box of // the source or target node. // This version checks also the first for simplicity of the loop. node v = e->source(); if (x(v) > maxWidth ) maxWidth = x(v); if (y(v) > maxHeight) maxHeight = y(v); const DPolyline &dpl = bends(e); for(const DPoint &dp : dpl) { if (dp.m_x > maxWidth ) maxWidth = dp.m_x; if (dp.m_y > maxHeight) maxHeight = dp.m_y; } } } } return DPoint(maxWidth,maxHeight); }
//--------------------------------------------------------- // actual call (called by all variations of call) // crossing of generalizations is forbidden if forbidCrossingGens = true // edge costs are obeyed if costOrig != 0 // Module::ReturnType FixedEmbeddingInserter::doCall( PlanRep &PG, const List<edge> &origEdges, bool forbidCrossingGens, const EdgeArray<int> *costOrig, const EdgeArray<bool> *forbiddenEdgeOrig, const EdgeArray<unsigned int> *edgeSubGraph) { double T; usedTime(T); ReturnType retValue = retFeasible; m_runsPostprocessing = 0; PG.embed(); OGDF_ASSERT(PG.representsCombEmbedding() == true); if (origEdges.size() == 0) return retOptimal; // nothing to do // initialization CombinatorialEmbedding E(PG); // embedding of PG m_dual.clear(); m_primalAdj.init(m_dual); m_nodeOf.init(E); // construct dual graph m_primalIsGen.init(m_dual,false); OGDF_ASSERT(forbidCrossingGens == false || forbiddenEdgeOrig == 0); if(forbidCrossingGens) constructDualForbidCrossingGens((const PlanRepUML&)PG,E); else constructDual(PG,E,forbiddenEdgeOrig); // m_delFaces and m_newFaces are used by removeEdge() // if we can't allocate memory for them, we throw an exception if (removeReinsert() != rrNone) { m_delFaces = new FaceSetSimple(E); if (m_delFaces == 0) OGDF_THROW(InsufficientMemoryException); m_newFaces = new FaceSetPure(E); if (m_newFaces == 0) { delete m_delFaces; OGDF_THROW(InsufficientMemoryException); } // no postprocessing -> no removeEdge() } else { m_delFaces = 0; m_newFaces = 0; } SListPure<edge> currentOrigEdges; if(removeReinsert() == rrIncremental) { edge e; forall_edges(e,PG) currentOrigEdges.pushBack(PG.original(e)); } // insertion of edges ListConstIterator<edge> it; for(it = origEdges.begin(); it.valid(); ++it) { edge eOrig = *it; int eSubGraph = 0; // edgeSubGraph-data of eOrig if(edgeSubGraph!=0) eSubGraph = (*edgeSubGraph)[eOrig]; SList<adjEntry> crossed; if(costOrig != 0) { findShortestPath(PG, E, *costOrig, PG.copy(eOrig->source()),PG.copy(eOrig->target()), forbidCrossingGens ? ((const PlanRepUML&)PG).typeOrig(eOrig) : Graph::association, crossed, edgeSubGraph, eSubGraph); } else { findShortestPath(E, PG.copy(eOrig->source()),PG.copy(eOrig->target()), forbidCrossingGens ? ((const PlanRepUML&)PG).typeOrig(eOrig) : Graph::association, crossed); } insertEdge(PG,E,eOrig,crossed,forbidCrossingGens,forbiddenEdgeOrig); if(removeReinsert() == rrIncremental) { currentOrigEdges.pushBack(eOrig); bool improved; do { ++m_runsPostprocessing; improved = false; SListConstIterator<edge> itRR; for(itRR = currentOrigEdges.begin(); itRR.valid(); ++itRR) { edge eOrigRR = *itRR; int pathLength; if(costOrig != 0) pathLength = costCrossed(eOrigRR,PG,*costOrig,edgeSubGraph); else pathLength = PG.chain(eOrigRR).size() - 1; if (pathLength == 0) continue; // cannot improve removeEdge(PG,E,eOrigRR,forbidCrossingGens,forbiddenEdgeOrig); // try to find a better insertion path SList<adjEntry> crossed; if(costOrig != 0) { int eSubGraph = 0; // edgeSubGraph-data of eOrig if(edgeSubGraph!=0) eSubGraph = (*edgeSubGraph)[eOrigRR]; findShortestPath(PG, E, *costOrig, PG.copy(eOrigRR->source()),PG.copy(eOrigRR->target()), forbidCrossingGens ? ((const PlanRepUML&)PG).typeOrig(eOrigRR) : Graph::association, crossed, edgeSubGraph, eSubGraph); } else { findShortestPath(E, PG.copy(eOrigRR->source()),PG.copy(eOrigRR->target()), forbidCrossingGens ? ((const PlanRepUML&)PG).typeOrig(eOrigRR) : Graph::association, crossed); } // re-insert edge (insertion path cannot be longer) insertEdge(PG,E,eOrigRR,crossed,forbidCrossingGens,forbiddenEdgeOrig); int newPathLength = (costOrig != 0) ? costCrossed(eOrigRR,PG,*costOrig,edgeSubGraph) : (PG.chain(eOrigRR).size() - 1); OGDF_ASSERT(newPathLength <= pathLength); if(newPathLength < pathLength) improved = true; } } while (improved); } } const Graph &G = PG.original(); if(removeReinsert() != rrIncremental) { // postprocessing (remove-reinsert heuristc) SListPure<edge> rrEdges; switch(removeReinsert()) { case rrAll: case rrMostCrossed: { const List<node> &origInCC = PG.nodesInCC(); ListConstIterator<node> itV; for(itV = origInCC.begin(); itV.valid(); ++itV) { node vG = *itV; adjEntry adj; forall_adj(adj,vG) { if ((adj->index() & 1) == 0) continue; edge eG = adj->theEdge(); rrEdges.pushBack(eG); } } } break; case rrInserted: for(ListConstIterator<edge> it = origEdges.begin(); it.valid(); ++it) rrEdges.pushBack(*it); break; case rrNone: case rrIncremental: 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 = retTimeoutFeasible; break; } ++m_runsPostprocessing; improved = false; if(removeReinsert() == rrMostCrossed) { FEICrossingsBucket bucket(&PG); rrEdges.bucketSort(bucket); const int num = int(0.01 * percentMostCrossed() * G.numberOfEdges()); itStop = rrEdges.get(num); } SListConstIterator<edge> it; for(it = rrEdges.begin(); it != itStop; ++it) { edge eOrig = *it; // remove only if crossings on edge; // in especially: forbidden edges are never handled by postprocessing // since there are no crossings on such edges int pathLength; if(costOrig != 0) pathLength = costCrossed(eOrig,PG,*costOrig,edgeSubGraph); else pathLength = PG.chain(eOrig).size() - 1; if (pathLength == 0) continue; // cannot improve removeEdge(PG,E,eOrig,forbidCrossingGens,forbiddenEdgeOrig); // try to find a better insertion path SList<adjEntry> crossed; if(costOrig != 0) { int eSubGraph = 0; // edgeSubGraph-data of eOrig if(edgeSubGraph!=0) eSubGraph = (*edgeSubGraph)[eOrig]; findShortestPath(PG, E, *costOrig, PG.copy(eOrig->source()),PG.copy(eOrig->target()), forbidCrossingGens ? ((const PlanRepUML&)PG).typeOrig(eOrig) : Graph::association, crossed, edgeSubGraph, eSubGraph); } else { findShortestPath(E, PG.copy(eOrig->source()),PG.copy(eOrig->target()), forbidCrossingGens ? ((const PlanRepUML&)PG).typeOrig(eOrig) : Graph::association, crossed); } // re-insert edge (insertion path cannot be longer) insertEdge(PG,E,eOrig,crossed,forbidCrossingGens,forbiddenEdgeOrig); int newPathLength = (costOrig != 0) ? costCrossed(eOrig,PG,*costOrig,edgeSubGraph) : (PG.chain(eOrig).size() - 1); OGDF_ASSERT(newPathLength <= pathLength); if(newPathLength < pathLength) improved = true; } } while(improved); // iterate as long as we improve }
int getBucket(const edge &e) { return -m_pPG->chain(e).size(); }
Module::ReturnType SubgraphPlanarizer::doCall( PlanRep &pr, int cc, const EdgeArray<int> *pCostOrig, const EdgeArray<bool> *pForbiddenOrig, const EdgeArray<__uint32> *pEdgeSubGraphs, int &crossingNumber) { OGDF_ASSERT(m_permutations >= 1); PlanarSubgraphModule &subgraph = m_subgraph.get(); EdgeInsertionModule &inserter = m_inserter.get(); int nThreads = min(m_maxThreads, m_permutations); __int64 startTime; System::usedRealTime(startTime); __int64 stopTime = (m_timeLimit >= 0) ? (startTime + __int64(1000.0*m_timeLimit)) : -1; // // Compute subgraph // if(m_setTimeout) subgraph.timeLimit(m_timeLimit); pr.initCC(cc); List<edge> delEdges; ReturnType retValue; if(pCostOrig) { EdgeArray<int> costPG(pr); edge e; forall_edges(e,pr) costPG[e] = (*pCostOrig)[pr.original(e)]; retValue = subgraph.call(pr, costPG, delEdges); } else retValue = subgraph.call(pr, delEdges); if(isSolution(retValue) == false) return retValue; const int m = delEdges.size(); if(m == 0) return retOptimal; // graph is planar for(ListIterator<edge> it = delEdges.begin(); it.valid(); ++it) *it = pr.original(*it); // // Permutation phase // int seed = rand(); #ifdef OGDF_HAVE_CPP11 minstd_rand rng(seed); #endif if(nThreads > 1) { // // Parallel implementation // ThreadMaster master( pr, cc, pCostOrig, pForbiddenOrig, pEdgeSubGraphs, delEdges, seed, m_permutations - nThreads, stopTime); Array<Worker *> thread(nThreads-1); for(int i = 0; i < nThreads-1; ++i) { thread[i] = new Worker(&master, inserter.clone()); thread[i]->start(); } #ifdef OGDF_HAVE_CPP11 doWorkHelper(master, inserter, rng); #else doWorkHelper(master, inserter); #endif for(int i = 0; i < nThreads-1; ++i) { thread[i]->join(); delete thread[i]; } master.restore(pr, crossingNumber); } else { // // Sequential implementation // PlanRepLight prl(pr); Array<edge> deletedEdges(m); int j = 0; for(ListIterator<edge> it = delEdges.begin(); it.valid(); ++it) deletedEdges[j++] = *it; bool foundSolution = false; CrossingStructure cs; for(int i = 1; i <= m_permutations; ++i) { int cr; bool ok = doSinglePermutation(prl, cc, pCostOrig, pForbiddenOrig, pEdgeSubGraphs, deletedEdges, inserter, #ifdef OGDF_HAVE_CPP11 rng, #endif cr); if(ok && (foundSolution == false || cr < cs.weightedCrossingNumber())) { foundSolution = true; cs.init(prl, cr); } if(stopTime >= 0 && System::realTime() >= stopTime) { if(foundSolution == false) return retTimeoutInfeasible; // not able to find a solution... break; } } cs.restore(pr,cc); // restore best solution in pr crossingNumber = cs.weightedCrossingNumber(); OGDF_ASSERT(isPlanar(pr) == true); } return retFeasible; }
Module::ReturnType SubgraphPlanarizer::doCall(PlanRep &PG, int cc, const EdgeArray<int> &cost, const EdgeArray<bool> &forbid, const EdgeArray<unsigned int> &subgraphs, int& crossingNumber) { OGDF_ASSERT(m_permutations >= 1); OGDF_ASSERT(!(useSubgraphs()) || useCost()); // ersetze durch exception handling double startTime; usedTime(startTime); if(m_setTimeout) m_subgraph.get().timeLimit(m_timeLimit); List<edge> deletedEdges; PG.initCC(cc); EdgeArray<int> costPG(PG); edge e; forall_edges(e,PG) costPG[e] = cost[PG.original(e)]; ReturnType retValue = m_subgraph.get().call(PG, costPG, deletedEdges); if(isSolution(retValue) == false) return retValue; for(ListIterator<edge> it = deletedEdges.begin(); it.valid(); ++it) *it = PG.original(*it); bool foundSolution = false; CrossingStructure cs; for(int i = 1; i <= m_permutations; ++i) { const int nG = PG.numberOfNodes(); for(ListConstIterator<edge> it = deletedEdges.begin(); it.valid(); ++it) PG.delCopy(PG.copy(*it)); deletedEdges.permute(); if(m_setTimeout) m_inserter.get().timeLimit( (m_timeLimit >= 0) ? max(0.0,m_timeLimit - usedTime(startTime)) : -1); ReturnType ret; if(useForbid()) { if(useCost()) { if(useSubgraphs()) ret = m_inserter.get().call(PG, cost, forbid, deletedEdges, subgraphs); else ret = m_inserter.get().call(PG, cost, forbid, deletedEdges); } else ret = m_inserter.get().call(PG, forbid, deletedEdges); } else { if(useCost()) { if(useSubgraphs()) ret = m_inserter.get().call(PG, cost, deletedEdges, subgraphs); else ret = m_inserter.get().call(PG, cost, deletedEdges); } else ret = m_inserter.get().call(PG, deletedEdges); } if(isSolution(ret) == false) continue; // no solution found, that's bad... if(!useCost()) crossingNumber = PG.numberOfNodes() - nG; else { crossingNumber = 0; node n; forall_nodes(n, PG) { if(PG.original(n) == 0) { // dummy found -> calc cost edge e1 = PG.original(n->firstAdj()->theEdge()); edge e2 = PG.original(n->lastAdj()->theEdge()); if(useSubgraphs()) { int subgraphCounter = 0; for(int i=0; i<32; i++) { if(((subgraphs[e1] & (1<<i))!=0) && ((subgraphs[e2] & (1<<i)) != 0)) subgraphCounter++; } crossingNumber += (subgraphCounter*cost[e1] * cost[e2]); } else crossingNumber += cost[e1] * cost[e2]; } } } if(i == 1 || crossingNumber < cs.weightedCrossingNumber()) { foundSolution = true; cs.init(PG, crossingNumber); } if(localLogMode() == LM_STATISTIC) { if(m_permutations <= 200 || i <= 10 || (i <= 30 && (i % 2) == 0) || (i > 30 && i <= 100 && (i % 5) == 0) || ((i % 10) == 0)) sout() << "\t" << cs.weightedCrossingNumber(); } PG.initCC(cc); if(m_timeLimit >= 0 && usedTime(startTime) >= m_timeLimit) { if(foundSolution == false) return retTimeoutInfeasible; // not able to find a solution... break; } } cs.restore(PG,cc); // restore best solution in PG crossingNumber = cs.weightedCrossingNumber(); PlanarModule pm; OGDF_ASSERT(pm.planarityTest(PG) == true); return retFeasible; }