// constructive heuristics for orthogonal representation OR
void LongestPathCompaction::constructiveHeuristics(
	PlanRep &PG,
	OrthoRep &OR,
	const RoutingChannel<int> &rc,
	GridLayoutMapped &drawing)
{
	OGDF_ASSERT(OR.isOrientated());

	// x-coordinates of vertical segments
	CompactionConstraintGraph<int> Dx(OR, PG, odEast, rc.separation());
	Dx.insertVertexSizeArcs(PG, drawing.width(), rc);

	NodeArray<int> xDx(Dx.getGraph(), 0);
	computeCoords(Dx, xDx);

	// y-coordinates of horizontal segments
	CompactionConstraintGraph<int> Dy(OR, PG, odNorth, rc.separation());
	Dy.insertVertexSizeArcs(PG, drawing.height(), rc);

	NodeArray<int> yDy(Dy.getGraph(), 0);
	computeCoords(Dy, yDy);

	// final coordinates of vertices
	for(node v : PG.nodes) {
		drawing.x(v) = xDx[Dx.pathNodeOf(v)];
		drawing.y(v) = yDy[Dy.pathNodeOf(v)];
	}
}
// improvement heuristics for orthogonal drawing
void LongestPathCompaction::improvementHeuristics(
	PlanRep &PG,
	OrthoRep &OR,
	const RoutingChannel<int> &rc,
	GridLayoutMapped &drawing)
{
	OGDF_ASSERT(OR.isOrientated());

	int costs, lastCosts;
	int steps = 0, maxSteps = m_maxImprovementSteps;
	if (maxSteps == 0) maxSteps = numeric_limits<int>::max();

	// OPTIMIZATION POTENTIAL:
	// update constraint graphs "incrementally" by only re-inserting
	// visibility arcs
	costs = 0;
	do {
		lastCosts = costs;
		++steps;

		// x-coordinates of vertical segments
		CompactionConstraintGraph<int> Dx(OR, PG, odEast, rc.separation());
		Dx.insertVertexSizeArcs(PG, drawing.width(), rc);
		Dx.insertVisibilityArcs(PG, drawing.x(),drawing.y());

		NodeArray<int> xDx(Dx.getGraph(), 0);
		computeCoords(Dx, xDx);

		// final x-coordinates of vertices
		for(node v : PG.nodes) {
			drawing.x(v) = xDx[Dx.pathNodeOf(v)];
		}


		// y-coordinates of horizontal segments
		CompactionConstraintGraph<int> Dy(OR, PG, odNorth, rc.separation());
		Dy.insertVertexSizeArcs(PG, drawing.height(), rc);
		Dy.insertVisibilityArcs(PG, drawing.y(),drawing.x());

		NodeArray<int> yDy(Dy.getGraph(), 0);
		computeCoords(Dy, yDy);

		// final y-coordinates of vertices
		for(node v : PG.nodes) {
			drawing.y(v) = yDy[Dy.pathNodeOf(v)];
		}

		costs = Dx.computeTotalCosts(xDx) + Dy.computeTotalCosts(yDy);

	} while (steps < maxSteps && (steps == 1 || costs < lastCosts));
}
Exemple #3
0
void PlanRep::collapseVertices(const OrthoRep &OR, GridLayout &drawing)
{
	for (node v : nodes) {
		const OrthoRep::VertexInfoUML *vi = OR.cageInfo(v);

		if(vi == nullptr ||
			(typeOf(v) != Graph::highDegreeExpander &&
			typeOf(v) != Graph::lowDegreeExpander))
			continue;

		node vOrig = original(v);
		OGDF_ASSERT(vOrig != 0);

		node vCenter = newNode();
		m_vOrig[vCenter] = vOrig;
		m_vCopy[vOrig] = vCenter;
		m_vOrig[v] = nullptr;

		node lowerLeft  = vi->m_corner[odNorth]->theNode();
		node lowerRight = vi->m_corner[odWest ]->theNode();
		node upperLeft  = vi->m_corner[odEast ]->theNode();
		drawing.x(vCenter) = (drawing.x(lowerLeft)+drawing.x(lowerRight)) >> 1;
		drawing.y(vCenter) = (drawing.y(lowerLeft)+drawing.y(upperLeft )) >> 1;

		edge eOrig;
		forall_adj_edges(eOrig,vOrig) {
			if(eOrig->target() == vOrig) {
				node connect = m_eCopy[eOrig].back()->target();
				edge eNew = newEdge(connect,vCenter);
				m_eOrig[eNew] = eOrig;
				m_eIterator[eNew] = m_eCopy[eOrig].pushBack(eNew);

			} else {
				node connect = m_eCopy[eOrig].front()->source();
				edge eNew = newEdge(vCenter,connect);
				m_eOrig[eNew] = eOrig;
				m_eIterator[eNew] = m_eCopy[eOrig].pushFront(eNew);
			}
		}
	}
}
/**---------------------------------------------------
calling function taking the planar representation, the
external face (adjentry), the layout to be filled,
a list of non-planar edges, a list of inserted edges
and the original graph as input
*/
void ClusterOrthoLayout::call(ClusterPlanRep &PG,
	adjEntry adjExternal,
	Layout &drawing,
	List<NodePair>& npEdges,
	List<edge>& newEdges,
	Graph& originalGraph)
{
	// We don't care about UML hierarchies and therefore do not allow alignment
	OGDF_ASSERT(!m_align);

	// if we have only one vertex in PG ...
	if(PG.numberOfNodes() == 1) {
		node v1 = PG.firstNode();
		node vOrig = PG.original(v1);
		double w = PG.widthOrig(vOrig);
		double h = PG.heightOrig(vOrig);

		drawing.x(v1) = m_margin + w/2;
		drawing.y(v1) = m_margin + h/2;
		m_boundingBox = DPoint(w + 2*m_margin, h + 2*m_margin);
		return;
	}

	OGDF_ASSERT(PG.representsCombEmbedding())
	//-------------------------
	// insert cluster boundaries
	PG.ModelBoundaries();
	OGDF_ASSERT(PG.representsCombEmbedding())


	//--------------------------
	// insert non-planar edges
	CombinatorialEmbedding* CE = new CombinatorialEmbedding(PG);
	if (!npEdges.empty())
	{
		CPlanarEdgeInserter CEI;
		CEI.call(PG, *CE, originalGraph, npEdges, newEdges);
	}//if

	//------------------------------------------------------------
	// now we set the external face, currently to the largest face
	adjEntry extAdj = 0;
	int maximum = 0;
	edge e, eSucc;

	for(e = PG.firstEdge(); e; e = eSucc)
	{
		eSucc = e->succ();
		if ( PG.clusterOfEdge(e) == PG.getClusterGraph().rootCluster() )
		{
			int asSize = CE->rightFace(e->adjSource())->size();
			if ( asSize > maximum)
			{
				maximum = asSize;
				extAdj = e->adjSource();
			}
			int atSize = CE->rightFace(e->adjTarget())->size();
			if ( atSize > maximum)
			{
				maximum = atSize;
				extAdj = e->adjTarget();
			}

		}//if root edge

	}//for

	delete CE;

	//returns adjEntry in rootcluster
	adjExternal = extAdj;
	OGDF_ASSERT(adjExternal != 0);


	//----------------------------------------------------------
	//Compaction scaling: help node cages to pass by each other:
	//First, the layout is blown up and then shrunk again in several steps
	//We change the separation value and save the original value.
	double l_orsep = m_separation;
	if (m_useScalingCompaction)
	{
		double scaleFactor = double(int(1 << m_scalingSteps));
		m_separation = scaleFactor*m_separation; //reduce this step by step in compaction
	}//if scaling

	//***********************************
	// PHASE 1: determine orthogonal shape

	//-------------------------------------------------------
	// expand high-degree vertices and generalization mergers
	PG.expand();

	// get combinatorial embedding
	CombinatorialEmbedding E(PG);
	E.setExternalFace(E.rightFace(adjExternal));

	// orthogonal shape representation
	OrthoRep OR;

	ClusterOrthoShaper COF;

	//set some options
	COF.align(false); //cannot be used yet with clusters
	COF.traditional(m_orthoStyle > 0 ? false : true); //prefer 90/270 degree angles over 180/180
	//bend cost depends on cluster depths avoiding unnecessary "inner" bends
	COF.bendCostTopDown(ClusterOrthoShaper::topDownCost);

	// New Call
	//COF.call(PG,E,OR,2);
	// Original call without bend bounds(still valid)
	COF.call(PG, E, OR);

	String msg;
	OGDF_ASSERT(OR.check(msg))

	//******************************************************************
	// PHASE 2: construction of a feasible drawing of the expanded graph

	//---------------------------
	// expand low degree vertices
	PG.expandLowDegreeVertices(OR);

	OGDF_ASSERT(PG.representsCombEmbedding());

	//------------------
	// restore embedding
	E.computeFaces();
	E.setExternalFace(E.rightFace(adjExternal));

	OGDF_ASSERT(OR.check(msg))

	//----------
	//COMPACTION

	//--------------------------
	// apply constructive compaction heuristics
	OR.normalize();
	OR.dissect();

	OR.orientate(PG,m_preferedDir);

	OGDF_ASSERT(OR.check(msg))

	// compute cage information and routing channels
	OR.computeCageInfoUML(PG);
	//temporary grid layout
	GridLayoutMapped gridDrawing(PG,OR,m_separation,m_cOverhang,4);

	RoutingChannel<int> rcGrid(PG,gridDrawing.toGrid(m_separation),m_cOverhang);
	rcGrid.computeRoutingChannels(OR, m_align);

	node v;
	const OrthoRep::VertexInfoUML *pInfoExp;
	forall_nodes(v,PG) {
		pInfoExp = OR.cageInfo(v);

		if (pInfoExp) break;
	}
Exemple #5
0
void OrthoLayoutUML::call(PlanRepUML &PG,
	adjEntry adjExternal,
	Layout &drawing)
{
	// if we have only one vertex in PG ...
	if(PG.numberOfNodes() == 1) {
		node v1 = PG.firstNode();
		node vOrig = PG.original(v1);
		double w = PG.widthOrig(vOrig);
		double h = PG.heightOrig(vOrig);

		drawing.x(v1) = m_margin + w/2;
		drawing.y(v1) = m_margin + h/2;
		m_boundingBox = DPoint(w + 2*m_margin, h + 2*m_margin);
		return;
	}


	//classify brother-to-brother hierarchy edges to allow alignment
	if (m_align)
	{
		classifyEdges(PG, adjExternal);
	}//if align
	//compaction with scaling: help node cages to pass by each other
	double l_orsep = m_separation;
	if (m_useScalingCompaction)
	{
		m_scalingSteps = 6;
		double scaleFactor = double(int(1 << m_scalingSteps));
		m_separation = scaleFactor*m_separation; //reduce this step by step in compaction
	}//if scaling


	//***********************************
	// PHASE 1: determine orthogonal shape

	// expand high-degree vertices and generalization mergers
	PG.expand();

	//check preconditions, currently not necessary
	//assureDrawability(PG);

	// get combinatorial embedding
	CombinatorialEmbedding E(PG);
	E.setExternalFace(E.rightFace(adjExternal));

	// determine orthogonal shape
	OrthoRep OR;

	//OrthoFormerUML OF;
	OrthoShaper OFG;

	//set some options
	OFG.align(m_align);    //align brother objects on hierarchy levels
	OFG.traditional(m_orthoStyle > 0 ? false : true); //prefer 90/270 degree angles over 180/180

	// New Call
	OFG.setBendBound(m_bendBound);
	OFG.call(PG,E,OR);

	// remove face splitter
	edge e, eSucc;
	for(e = PG.firstEdge(); e; e = eSucc)
	{
		eSucc = e->succ();
		if(PG.faceSplitter(e)) {
			OR.angle(e->adjSource()->cyclicPred()) = 2;
			OR.angle(e->adjTarget()->cyclicPred()) = 2;
			PG.delEdge(e);
		}
	}

	//******************************************************************
	// PHASE 2: construction of a feasible drawing of the expanded graph

	// expand low degree vertices
	PG.expandLowDegreeVertices(OR);

	OGDF_ASSERT(PG.representsCombEmbedding());

	// restore embedding
	E.computeFaces();
	E.setExternalFace(E.rightFace(adjExternal));

	// apply constructive compaction heuristics

	OR.normalize();
	OR.dissect2(&PG); //OR.dissect();

	OR.orientate(PG,m_preferedDir);

	// compute cage information and routing channels
	OR.computeCageInfoUML(PG);

	// adjust value of cOverhang
	if(m_cOverhang < 0.05)
		m_cOverhang = 0.0;
	if(m_cOverhang > 0.5)
		m_cOverhang = 0.5;

	//temporary grid layout
	GridLayoutMapped gridDrawing(PG,OR,m_separation,m_cOverhang,2);

	RoutingChannel<int> rcGrid(PG,gridDrawing.toGrid(m_separation),m_cOverhang);
	rcGrid.computeRoutingChannels(OR, m_align);


	node v;
	const OrthoRep::VertexInfoUML *pInfoExp;
	forall_nodes(v, PG) {
		pInfoExp = OR.cageInfo(v);

		if (pInfoExp) break;
	}
Exemple #6
0
void PlanRep::writeGML(ostream &os, const OrthoRep &OR, const GridLayout &drawing)
{
	const Graph &G = *this;

	NodeArray<int> id(*this);
	int nextId = 0;

	os.setf(ios::showpoint);
	os.precision(10);

	os << "Creator \"ogdf::GraphAttributes::writeGML\"\n";
	os << "graph [\n";
	os << "  directed 1\n";

	for(node v : G.nodes) {
		os << "  node [\n";

		os << "    id " << (id[v] = nextId++) << "\n";

		os << "    label \"" << v->index() << "\"\n";

		os << "    graphics [\n";
		os << "      x " << ((double) drawing.x(v)) << "\n";
		os << "      y " << ((double) drawing.y(v)) << "\n";
		os << "      w " << 3.0 << "\n";
		os << "      h " << 3.0 << "\n";
		os << "      type \"rectangle\"\n";
		os << "      width 1.0\n";
		if (typeOf(v) == Graph::generalizationMerger) {
			os << "      type \"oval\"\n";
			os << "      fill \"#0000A0\"\n";
		}
		else if (typeOf(v) == Graph::generalizationExpander) {
			os << "      type \"oval\"\n";
			os << "      fill \"#00FF00\"\n";
		}
		else if (typeOf(v) == Graph::highDegreeExpander ||
			typeOf(v) == Graph::lowDegreeExpander)
			os << "      fill \"#FFFF00\"\n";
		else if (typeOf(v) == Graph::dummy)
			os << "      type \"oval\"\n";

		else if (v->degree() > 4)
			os << "      fill \"#FFFF00\"\n";

		else
			os << "      fill \"#000000\"\n";


		os << "    ]\n"; // graphics

		os << "  ]\n"; // node
	}

	for (node v : nodes)
	{
		if (expandAdj(v) != nullptr && (typeOf(v) == Graph::highDegreeExpander ||
			typeOf(v) == Graph::lowDegreeExpander))
		{
			node vOrig = original(v);
			const OrthoRep::VertexInfoUML &vi = *OR.cageInfo(v);
			node ll = vi.m_corner[odNorth]->theNode();
			node ur = vi.m_corner[odSouth]->theNode();

			os << "  node [\n";
			os << "    id " << nextId++ << "\n";

			if (m_pGraphAttributes->attributes() & GraphAttributes::nodeLabel) {
				os << "    label \"" << m_pGraphAttributes->label(vOrig) << "\"\n";
			}

			os << "    graphics [\n";
			os << "      x " << 0.5 * (drawing.x(ur) + drawing.x(ll)) << "\n";
			os << "      y " << 0.5 * (drawing.y(ur) + drawing.y(ll)) << "\n";
			os << "      w " << widthOrig(vOrig) << "\n";
			os << "      h " << heightOrig(vOrig) << "\n";
			os << "      type \"rectangle\"\n";
			os << "      width 1.0\n";
			os << "      fill \"#FFFF00\"\n";

			os << "    ]\n"; // graphics
			os << "  ]\n"; // node
		}
	}

	for(edge e : G.edges)
	{
		os << "  edge [\n";

		os << "    source " << id[e->source()] << "\n";
		os << "    target " << id[e->target()] << "\n";

		os << "    generalization " << typeOf(e) << "\n";

		os << "    graphics [\n";

		os << "      type \"line\"\n";

		if (typeOf(e) == Graph::generalization)
		{
			if (typeOf(e->target()) == Graph::generalizationExpander)
				os << "      arrow \"none\"\n";
			else
				os << "      arrow \"last\"\n";

			os << "      fill \"#FF0000\"\n";
			os << "      width 2.0\n";
		}
		else
		{
			if (typeOf(e->source()) == Graph::generalizationExpander ||
			    typeOf(e->source()) == Graph::generalizationMerger ||
			    typeOf(e->target()) == Graph::generalizationExpander ||
			    typeOf(e->target()) == Graph::generalizationMerger)
			{
				os << "      arrow \"none\"\n";
				os << "      fill \"#FF0000\"\n";
			}
			else if (original(e) == nullptr)
			{
				os << "      arrow \"none\"\n";
				os << "      fill \"#AFAFAF\"\n";
			}
			else
				os << "      arrow \"none\"\n";
			if (isBrother(e))
				os << "      fill \"#00AF0F\"\n";
			if (isHalfBrother(e))
				os << "      fill \"#0F00AF\"\n";
			os << "      width 1.0\n";
		}//else generalization

		os << "    ]\n"; // graphics

		os << "  ]\n"; // edge
	}

	os << "]\n"; // graph
}
Exemple #7
0
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