1
$\begingroup$

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)

correct frames

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

exp1=Fooooo/2;

incorrect frames

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?

$\endgroup$
2
  • $\begingroup$ 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? $\endgroup$
    – Jason B.
    Commented Feb 2, 2016 at 9:33
  • $\begingroup$ 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. $\endgroup$
    – Berg
    Commented Feb 2, 2016 at 9:47

1 Answer 1

6
$\begingroup$

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}]
 ]

enter image description here

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]

enter image description here

$\endgroup$
2
  • $\begingroup$ That does the trick, thanks. $\endgroup$
    – Berg
    Commented Feb 2, 2016 at 10:58
  • $\begingroup$ 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. $\endgroup$
    – Berg
    Commented Feb 4, 2016 at 23:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.