6 * $Date: 2012-07-07 23:10:08 +0200 (Sa, 07. Jul 2012) $
7 ***************************************************************/
10 * \brief Implements planar orthogonal drawing algorithm for
13 * \author Carsten Gutwenger, Sebastian Leipert, Karsten Klein
16 * This file is part of the Open Graph Drawing Framework (OGDF).
20 * See README.txt in the root directory of the OGDF installation for details.
23 * This program is free software; you can redistribute it and/or
24 * modify it under the terms of the GNU General Public License
25 * Version 2 or 3 as published by the Free Software Foundation;
26 * see the file LICENSE.txt included in the packaging of this file
30 * This program is distributed in the hope that it will be useful,
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 * GNU General Public License for more details.
36 * You should have received a copy of the GNU General Public
37 * License along with this program; if not, write to the Free
38 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
39 * Boston, MA 02110-1301, USA.
41 * \see http://www.gnu.org/copyleft/gpl.html
42 ***************************************************************/
45 #include <ogdf/cluster/ClusterOrthoLayout.h>
46 #include <ogdf/cluster/CconnectClusterPlanarEmbed.h>
49 #include <ogdf/orthogonal/LongestPathCompaction.h>
50 #include <ogdf/orthogonal/FlowCompaction.h>
51 #include <ogdf/orthogonal/EdgeRouter.h>
52 #include <ogdf/internal/orthogonal/RoutingChannel.h>
53 #include <ogdf/orthogonal/MinimumEdgeDistances.h>
54 #include <ogdf/cluster/ClusterOrthoShaper.h>
60 ClusterOrthoLayout::ClusterOrthoLayout()
62 //drawing object distances
66 //preferred hierarchy direction is odNorth, but we use odSouth since gml's are flipped!
67 m_preferedDir
= odSouth
;
72 //align hierarchy nodes on same level
74 //scale layout while improving it during compaction
75 m_useScalingCompaction
= false;
78 m_orthoStyle
= 0; //traditional 0, progressive 1
82 /**--------------------------------------
83 calling function without non-planar edges
85 void ClusterOrthoLayout::call(ClusterPlanRep
&PG
,
89 List
<NodePair
> npEdges
; //is empty
90 List
<edge
> newEdges
; //is empty
92 call(PG
, adjExternal
, drawing
, npEdges
, newEdges
, G
);
95 /**---------------------------------------------------
96 calling function taking the planar representation, the
97 external face (adjentry), the layout to be filled,
98 a list of non-planar edges, a list of inserted edges
99 and the original graph as input
101 void ClusterOrthoLayout::call(ClusterPlanRep
&PG
,
102 adjEntry adjExternal
,
104 List
<NodePair
>& npEdges
,
105 List
<edge
>& newEdges
,
106 Graph
& originalGraph
)
108 // We don't care about UML hierarchies and therefore do not allow alignment
109 OGDF_ASSERT(!m_align
);
111 // if we have only one vertex in PG ...
112 if(PG
.numberOfNodes() == 1) {
113 node v1
= PG
.firstNode();
114 node vOrig
= PG
.original(v1
);
115 double w
= PG
.widthOrig(vOrig
);
116 double h
= PG
.heightOrig(vOrig
);
118 drawing
.x(v1
) = m_margin
+ w
/2;
119 drawing
.y(v1
) = m_margin
+ h
/2;
120 m_boundingBox
= DPoint(w
+ 2*m_margin
, h
+ 2*m_margin
);
124 OGDF_ASSERT(PG
.representsCombEmbedding())
125 //-------------------------
126 // insert cluster boundaries
127 PG
.ModelBoundaries();
128 OGDF_ASSERT(PG
.representsCombEmbedding())
131 //--------------------------
132 // insert non-planar edges
133 CombinatorialEmbedding
* CE
= new CombinatorialEmbedding(PG
);
134 if (!npEdges
.empty())
136 CPlanarEdgeInserter CEI
;
137 CEI
.call(PG
, *CE
, originalGraph
, npEdges
, newEdges
);
140 //------------------------------------------------------------
141 // now we set the external face, currently to the largest face
146 for(e
= PG
.firstEdge(); e
; e
= eSucc
)
149 if ( PG
.clusterOfEdge(e
) == PG
.getClusterGraph().rootCluster() )
151 int asSize
= CE
->rightFace(e
->adjSource())->size();
152 if ( asSize
> maximum
)
155 extAdj
= e
->adjSource();
157 int atSize
= CE
->rightFace(e
->adjTarget())->size();
158 if ( atSize
> maximum
)
161 extAdj
= e
->adjTarget();
170 //returns adjEntry in rootcluster
171 adjExternal
= extAdj
;
172 OGDF_ASSERT(adjExternal
!= 0);
175 //----------------------------------------------------------
176 //Compaction scaling: help node cages to pass by each other:
177 //First, the layout is blown up and then shrunk again in several steps
178 //We change the separation value and save the original value.
179 double l_orsep
= m_separation
;
180 if (m_useScalingCompaction
)
182 double scaleFactor
= double(int(1 << m_scalingSteps
));
183 m_separation
= scaleFactor
*m_separation
; //reduce this step by step in compaction
186 //***********************************
187 // PHASE 1: determine orthogonal shape
189 //-------------------------------------------------------
190 // expand high-degree vertices and generalization mergers
193 // get combinatorial embedding
194 CombinatorialEmbedding
E(PG
);
195 E
.setExternalFace(E
.rightFace(adjExternal
));
197 // orthogonal shape representation
200 ClusterOrthoShaper COF
;
203 COF
.align(false); //cannot be used yet with clusters
204 COF
.traditional(m_orthoStyle
> 0 ? false : true); //prefer 90/270 degree angles over 180/180
205 //bend cost depends on cluster depths avoiding unnecessary "inner" bends
206 COF
.bendCostTopDown(ClusterOrthoShaper::topDownCost
);
209 //COF.call(PG,E,OR,2);
210 // Original call without bend bounds(still valid)
214 OGDF_ASSERT(OR
.check(msg
))
216 //******************************************************************
217 // PHASE 2: construction of a feasible drawing of the expanded graph
219 //---------------------------
220 // expand low degree vertices
221 PG
.expandLowDegreeVertices(OR
);
223 OGDF_ASSERT(PG
.representsCombEmbedding());
228 E
.setExternalFace(E
.rightFace(adjExternal
));
230 OGDF_ASSERT(OR
.check(msg
))
235 //--------------------------
236 // apply constructive compaction heuristics
240 OR
.orientate(PG
,m_preferedDir
);
242 OGDF_ASSERT(OR
.check(msg
))
244 // compute cage information and routing channels
245 OR
.computeCageInfoUML(PG
);
246 //temporary grid layout
247 GridLayoutMapped
gridDrawing(PG
,OR
,m_separation
,m_cOverhang
,4);
249 RoutingChannel
<int> rcGrid(PG
,gridDrawing
.toGrid(m_separation
),m_cOverhang
);
250 rcGrid
.computeRoutingChannels(OR
, m_align
);
253 const OrthoRep::VertexInfoUML
*pInfoExp
;
255 pInfoExp
= OR
.cageInfo(v
);
260 FlowCompaction
fca(0,m_costGen
,m_costAssoc
);
261 fca
.constructiveHeuristics(PG
,OR
,rcGrid
,gridDrawing
);
263 OR
.undissect(m_align
);
265 if (!m_align
) {OGDF_ASSERT(OR
.check(msg
))}
267 //--------------------------
268 //apply improvement compaction heuristics
269 // call flow compaction on grid
270 FlowCompaction
fc(0,m_costGen
,m_costAssoc
);
272 fc
.scalingSteps(m_scalingSteps
);
274 fc
.improvementHeuristics(PG
,OR
,rcGrid
,gridDrawing
);
276 if (m_align
) OR
.undissect(false);
278 //**************************
279 // PHASE 3: routing of edges
281 OGDF_ASSERT(OR
.check(msg
) == true);
284 MinimumEdgeDistances
<int> minDistGrid(PG
, gridDrawing
.toGrid(m_separation
));
285 //router.setOrSep(int(gridDrawing.toGrid(l_orsep))); //scaling test
286 router
.call(PG
,OR
,gridDrawing
,E
,rcGrid
,minDistGrid
, gridDrawing
.width(),
287 gridDrawing
.height(), m_align
);
289 OGDF_ASSERT(OR
.check(msg
) == true);
291 OR
.orientate(pInfoExp
->m_corner
[odNorth
],odNorth
);
293 //*******************************************************
294 // PHASE 4: apply improvement compaction heuristics again
296 // call flow compaction on grid
297 fc
.improvementHeuristics(PG
, OR
, minDistGrid
, gridDrawing
, int(gridDrawing
.toGrid(l_orsep
)));
300 gridDrawing
.remap(drawing
);
304 //--------------------------
305 // collapse all expanded vertices by introducing a new node in the center
306 // of each cage representing the original vertex
307 PG
.collapseVertices(OR
,drawing
);
309 // finally set the bounding box
310 computeBoundingBox(PG
,drawing
);
312 //set the separation again to the input value
313 m_separation
= l_orsep
;
318 // compute bounding box and move final drawing such that it is 0 aligned
319 // respecting margins
320 void ClusterOrthoLayout::computeBoundingBox(
321 const ClusterPlanRep
&PG
,
324 double minX
, maxX
, minY
, maxY
;
326 minX
= maxX
= drawing
.x(PG
.firstNode());
327 minY
= maxY
= drawing
.y(PG
.firstNode());
332 double x
= drawing
.x(v
);
333 if (x
< minX
) minX
= x
;
334 if (x
> maxX
) maxX
= x
;
336 double y
= drawing
.y(v
);
337 if (y
< minY
) minY
= y
;
338 if (y
> maxY
) maxY
= y
;
341 double deltaX
= m_margin
- minX
;
342 double deltaY
= m_margin
- minY
;
346 drawing
.x(v
) += deltaX
;
347 drawing
.y(v
) += deltaY
;
350 m_boundingBox
= DPoint(maxX
+deltaX
+m_margin
, maxY
+deltaY
+m_margin
);
354 } // end namespace ogdf