I am writing my own TreeForm function and for this I want to make sure that framed text boxes in Graphics do not overlap. First, I set up a function that returns a framed box with some expression in it:

ExpBox[exp_]:=Text[Framed[Style[exp, 12], FrameMargins->5],FormatType->StandardForm]

ExpBox[exp_, xy_]:=Text[Framed[Style[exp, 12], FrameMargins->5],xy,

FormatType->StandardForm]

I measure the width of such a box using Rasterize. Consider, for example, the two expressions

exp1=Fooooo*2;

exp2=Bar;

with the width

w1=First@Rasterize[ExpBox[exp1], “RasterSize”]

w2=First@Rasterize[ExpBox[exp2], “RasterSize”]

With those values I can set up a graphics with two nonoverlapping text boxes

Graphics[{ExpBox[exp1, {w1/2, 0}], ExpBox[exp2, {w1 + w2/2, 0}]},

PlotRange -> {{0, w1 + w2}, {-20, 20}}, ImageSize -> {w1 + w2, 40}]

and I get (as desired)

However, this does not work correctly as soon as a division is found within any of the expressions:

exp1=Fooooo/2;

The reason seems to be that, if there happens to be a division in an expression, then the ExpBox is “printed” at smaller size without a containing Graphics function.

How can I avoid this difference? Or is there another way to get the width of such a box?

=================

I notice that Row[{ExpBox[Fooooo*2], ExpBox[Bar]}] gives the same output as your original function, is there a reason not to do it like that instead?

– JasonB

Feb 2 at 9:33

I want to write a TreeForm variant and for this I also need to connect various boxes with Lines. I also need vertical alignment of boxes. For this I need the coordinates of the boxes.

– Berg

Feb 2 at 9:47

=================

1 Answer

1

=================

The width returned by Rasterize is not always correct, as you found out, and since you aren’t going to Rasterize before building your tree then the size of the raster graphics is irrelevant in this case.

This is the best way to get the size of the text cell (thanks to Sjoerd C. de Vries),

{w1, h1} = ImageDimensions@ImageCrop@Graphics@ExpBox[Fooooo/2];

{w2, h2} = ImageDimensions@ImageCrop@Graphics@ExpBox[Bar];

Of course, you can just grab the width by using First as in your code, but I also noticed that the height needs adjusting in the latter case.

Then you can plot

With[{h = Max[{h1, h2}]},

Graphics[{ExpBox[Fooooo/2, {w1/2, 0}], ExpBox[Bar, {w1 + w2/2, 0}]},

PlotRange -> {{0, w1 + w2}, h {-.5, .5}},

ImageSize -> {w1 + w2, h}]

]

I’m the type that always makes a function for something I’m doing more than twice, so you can wrap it all up like this:

combBoxes[exps__] := Module[{widths, heights, boxes, totalsize},

{widths, heights} =

Transpose@(ImageDimensions@ImageCrop@Graphics@ExpBox@# & /@ {exps});

heights = {Total@widths, Max@heights};

widths =

Total[widths[[;; #]]] – widths[[#]]/2 & /@ Range@Length@widths;

boxes = ExpBox[#1, {#2, 0}] & @@@ Transpose[{{exps}, widths}];

Graphics[boxes,

PlotRange -> {{0, heights[[1]]}, heights[[2]] {-.5, .5}},

ImageSize -> heights]

]

combBoxes[Foooooo/2, Bar]

combBoxes[This, is, how, I, would, make, these, boxes,

FooooooooBaaarrrrr^2/2]

That does the trick, thanks.

– Berg

Feb 2 at 10:58

I just noticed that this solution only works if the expressions whose width is measured happen to be small enough to fit in the standard size of Graphics. The width of very large boxes is always measured as 360.

– Berg

Feb 4 at 23:23