# Controlling TreePlot (and GraphPlot) layout issue

A continuing battle with Mathematica involves graphics heavy with text labels. For example, the following expression renders so that the "Lifecycle properties..." node completely hides the arrow from the "NonFunctional" node. Any options to space these nodes apart?

TreePlot[{
"Software Engr" -> "Specification",
"Software Engr" -> "Implementation",
"Specification" -> "Functional",
"Specification" -> "NonFunctional",
"Functional" -> Style[ "What the system \n must do", Bold],
"NonFunctional" ->
Column[{ Style["Lifecycle properties:", Bold], "Interoperability",
"Usability", "Scalability", "Safety", "Expandability",
"Flexibility", "Robustness", "Modularity"}]
},
Top, "Software Engr",
VertexLabeling -> True,
PlotStyle -> {FontFamily -> "Helvetica", FontSize -> 14},
EdgeRenderingFunction -> ({GrayLevel[0.5], Arrow[#1, 0.2]} &)
]


A related issue is that although Export renders a PDF of this exactly as it appears in notebook, right-clicking on the cell and using "Save Selection As..." makes the above mentioned nodes overlap. It's a mess. Imagine dealing with larger graphs

-
 For the related issue re: exporting, it apparently happens only on some OSes... see this question (Szabolcs' palette is not really the answer, but might be a useful workaround in the meantime) – rm -rf♦ Jun 4 '12 at 1:28 Maybe ImageSize option can help to spread out the labels. – Vitaliy Kaurov Jun 4 '12 at 3:10

The reason why this misalignment happens (in the notebook display) is mainly that the vertex labels are centered at the vertex coordinates, which clashes with the necessarily asymmetric nature of the arrows that are used as connectors here.

I fixed this by defining a modified VertexRenderingFunction that aligns the top of the labels to each vertex coordinate:

TreePlot[{"Software Engr" -> "Specification",
"Software Engr" -> "Implementation",
"Specification" -> "Functional", "Specification" -> "NonFunctional",
"Functional" -> Style["What the system \n must do", Bold],
"NonFunctional" ->
Column[{Style["Lifecycle properties:", Bold], "Interoperability",
"Usability", "Scalability", "Safety", "Expandability",
"Flexibility", "Robustness",
"Modularity"}]}, Top, "Software Engr", VertexLabeling -> True,
PlotStyle -> {FontFamily -> "Helvetica", FontSize -> 24},
EdgeRenderingFunction -> ({GrayLevel[0.5], Arrow[#1]} &),
VertexRenderingFunction -> (Inset[
Framed[#2, Background -> Yellow,
FrameStyle -> Orange], #1, {Center, Top}] &),
ImageSize -> 600]


At the chosen font size, the vertex labels will overlap unless I specify a sufficiently large ImageSize (one could also add a different AspectRatio), but the main fix is that I use Inset with three arguments. The first argument is the text to be displayed, the second argument is the vertex position, and the third argument does the trick of top-aligning the label.

The arrows now no longer need any setting back - except that the starting points could be shifted down a little. For that, you could use something like Arrow[#1, {.2, 0}].

My color choices may not be so pleasing to the eye - so here are the options corresponding to your original colors (they'd have to be entered in the Framed command):

Background -> RGBColor[1, 1, 0.8],
FrameStyle -> RGBColor[0.94, 0.85, 0.36]

-

Jens has a great answer, but I'm thinking about solving the problem of label collisions in general and what it would take to do this? Here's one possible way to try avoiding horizontal label collisions, but maybe this should be a question on its own?

Needs["HierarchicalClustering"];
SpringIterate[poss_,widths_]:=
With[{dist=DistanceMatrix[Last/@poss,DistanceFunction->EuclideanDistance],len=Length[poss]},
With[{forces = Table[If[dist[[i,j]]<.1&&EuclideanDistance[poss[[i,1]],poss[[j,1]]]<Norm[{widths[[i]],0.1}]/2+Norm[{widths[[j]],0.1}]/2,
Normalize[poss[[i]]-poss[[j]]],
{0,0}],
{i,len},{j,len}
]},
poss+Total/@(forces*2)]
]

ef[{v1_,v2_},{n1_,n2_},__]:=
Style[Line[
{v1,{v1[[1]],v1[[2]]-Abs[v1[[2]]-v2[[2]]]/2},
{v2[[1]],v1[[2]]-Abs[v1[[2]]-v2[[2]]]/2},v2}],
Antialiasing->False]

BetterTreePlot[g_, root_:Automatic] := Module[
{tp, poss, widths, newposs},
tp = TreePlot[g, Top, root, ImageSize -> {800, Automatic},
EdgeRenderingFunction->(ef), VertexLabeling -> True];
poss = Flatten[Cases[tp,Rule[VertexCoordinateRules,x_] :> x,{0,\[Infinity]}],1];
widths = (First /@ ImageDimensions /@ Rasterize /@ Flatten[
Cases[tp, GraphicsComplex[_,x_,___]:>x, {0,\[Infinity]}],1][[All,1]])/.2//N;
newposs = FixedPoint[SpringIterate[#,widths]&, poss, 10];
TreePlot[g, Top, root, ImageSize -> {800,Automatic},
EdgeRenderingFunction->(ef),VertexLabeling -> True,VertexCoordinateRules->newposs,
BaseStyle->{Thin, Black, PointSize[Medium]}, LayerSizeFunction -> LayerSizeFunction->(3#&)]

]

RandomString[s_, n_] := StringJoin @@ Table[s, {n}]
two = RandomString["2", 40];
three = RandomString["3", 30];
four = RandomString["4", 30];
five = RandomString["5", 60];
six = RandomString["6", 30];
seven = RandomString["7", 40];
eight = RandomString["8", 30];
nine = RandomString["9", 30];
g = {1 -> two, 1 -> three, 1 -> four, three -> six, three -> seven,
two -> seven, four -> eight, four -> nine};

-
 I think you forgot to define RandomString. I agree that it would be nice to have an automatic "overlap avoidance mechanism." On the other hand, it's not too hard to use ImageSize and AspectRatio either: TreePlot[g, VertexLabeling -> True, ImageSize -> 800, AspectRatio -> .2]` – Jens Jun 5 '12 at 17:50 Right. (there should be a RandomString[] function in M- I think) – M.R. Jun 6 '12 at 0:00