3
$\begingroup$

I wish to use ListAnimate to show a sequence of plots with different plot ranges and differently sized plot lables, all unknown in advance, in a way that keeps the plots and labels stable in the animation.

For example:

frames = 
  Table[
    Plot[x + q, {x, 0, 1}, 
      Frame -> True, 
      PlotLabel -> StringForm["\!\(\*StyleBox[\"q\",\nFontSlant->\"Italic\"]\) = ``", q]], 
    {q, {1, Sqrt[2], 1/3, Sqrt[3]/2}}]
ListAnimate@frames

Basically, I want a way to post-process the frames to make them have the widest PlotRange and their PlotLabel is padded.

ListAnimate @ prettifyGraphics @ frames

I have some idea on how to guess the effective range of graphics, but no idea on how to measure effective size of their labels and how to pad them. Looking at graphics options doesn't help.

UPDATE

The list of function plotted is just a sample. In this case, obviously, I know ho to compute the maximum range and I can use across all plots. In a usefuul case assume you cannot know the functions in advance and you cannot easly compute the y-range of values of each function corresponding to x-range of values. You can also assume you completely ignore the way the list of Graphics object were generated.

$\endgroup$
  • $\begingroup$ I think the 'clarification' given in the update makes this question too vague to be answered. $\endgroup$ – m_goldberg Oct 11 '14 at 10:42
  • $\begingroup$ @m_goldberg Sorry for my bad English. Basically I have a list of Graphics objects, constructed in some unknown way, by Plot or even by hand from Graphics primitives like Line and Polygon. I want to post-process these graphics objects so that the plot range and plot label size are always the same. If you follow my sample, you should not rely on the knowledge of the function plotted to make the result. The approach of Kuba is what I'm searching for, possibly with a different way to set the height of padding. $\endgroup$ – unlikely Oct 11 '14 at 11:11
2
$\begingroup$

I'd add Pane to PlotLabel in order to fix it:

frames = Table[   Plot[x + q, {x, 0, 1}, Frame -> True, 
                    PlotLabel -> (Pane[#, {Automatic, 65}] &@   StringForm[
                         "\!\(\*StyleBox[\"q\",\nFontSlant->\"Italic\"]\) = ``",  q])],
                  {q, {1, Sqrt[2], 1/3, Sqrt[3]/2}}];

and here's your function. Keep in mind that AbsoluteOptions is not perfect.

maxsize = 
 With[{size = MapThread[  
       Compose, 
       {{Min, Max, Min, Max}, 
        Flatten[Transpose[ PlotRange /. (AbsoluteOptions /@ #), {3, 1, 2}], 1]
       }]
      },
   Show[#, PlotRange -> {size[[;; 2]], size[[3 ;;]]}, Axes -> False] & /@ #
   ] &


 ListAnimate[ maxsize @ frames ]
$\endgroup$
  • $\begingroup$ This is basically what I was trying to implement. The only issue is the fixed height (65) of the Pane. There is no way to measure the height of each label and use the minimum required height? $\endgroup$ – unlikely Oct 11 '14 at 10:14
  • $\begingroup$ @unlikely well, there is, Rasterize+ImageDimensions but it is not the cleanest method :/ $\endgroup$ – Kuba Oct 11 '14 at 10:15
  • $\begingroup$ While not being exactly a complete and general answer to my question, I choose this as answer (instead of mine) because shown me (with the comment) the right direction to build a more general answer. Thanks $\endgroup$ – unlikely Oct 15 '14 at 7:17
3
$\begingroup$

Wrapping plot labels with Pane as in @kuba's answer and using an alternative method for getting the bounds for plot ranges:

ClearAll[resizeAndPaneF, paneF];
paneF[vs_:50] := Pane[PlotLabel /. (Options@#), {Automatic, vs}, Alignment -> {Center, Center}] &;

resizeAndPaneF[vs_:50] :=  With[{pr = PlotRange@Show[#, PlotRange -> All]},
    Show[#, PlotRange -> pr, Axes -> False, PlotLabel -> paneF[vs]@#] & /@ #] &;

Using OP's frames

resizedframes = resizeAndPaneF[]@frames

enter image description here

enter image description here

An aside: you could also use

pr = Through[{Min, Max}@#] & /@ Transpose[PlotRange /@ #]

to get the bounding plot ranges.

Another: perhaps

Row[{Style["q = ", FontSlant -> Italic], q}]

instead of StringForm["\!\(\*StyleBox[\"q\",\nFontSlant->\"Italic\"]\) = ``", q]

$\endgroup$
  • $\begingroup$ Thanks. +1 for the defaulted-but-not-fixed height of padding, but AbsoluteOptions is not more robust than Options? $\endgroup$ – unlikely Oct 11 '14 at 11:23
  • $\begingroup$ The use of Options with Graphics objects appear undocumented, but I now think can be simpler (and more efficient also?) than AbsoluteOptions... $\endgroup$ – unlikely Oct 11 '14 at 11:43
  • $\begingroup$ As a C programmer, I like StringForm because, like printf and similar API, allow a fixed, potentially localized, 'message' to be filled with parameters. On a notebook interface my code doesn't appear so bad :) $\endgroup$ – unlikely Oct 11 '14 at 11:46
  • $\begingroup$ @unlikely, i am using Options to get the plot labels only. Regarding Options vs AbsoluteOptions: Options[graphicsobj, PlotRange] will not work for cases where graphicsobj has PlotRange->All. $\endgroup$ – kglr Oct 11 '14 at 11:59
  • $\begingroup$ .. i like StringForm too -- and yes, input form does look scary:) $\endgroup$ – kglr Oct 11 '14 at 12:01
0
$\begingroup$

Building on @Kuba answer and comments I'm now using this implementation:

maxPlotRange[glist_] := 
  Through[{Min, Max}[#]] & /@ 
   (* Interval @@@ *) 
    Transpose[PlotRange /. AbsoluteOptions[#, PlotRange] & /@ glist];
maxPlotLabelSize[glist_] := 
  Max /@ Transpose@
    Map[Rasterize[#, "RasterSize"] &, 
     PlotLabel /. Options[#, PlotLabel] & /@ glist];
makePrettyFrames[glist_] :=
 With[{maxRange = maxPlotRange[frames], 
   maxLabelSize = maxPlotLabelSize[frames]}, Show[#,
     PlotRange -> maxRange,
     PlotLabel -> 
      Pane[PlotLabel /. Options[#, PlotLabel], 
       ImageSize -> maxLabelSize + {2, 0}, 
       Alignment -> {Center, Center}]
     ] & /@ glist
  ]

I initially found interesting the use of Interval to combine all plot ranges and to represent with a meaningful symbol the operation done; Through[{Min, Max}[#]] get the bounding interval of an Interval and handle the case when the Interval is composed of several disjoint ranges, i.e. when plot ranges doens't overlap. As of @kguler comment Interval proven later to be probably unecessary.

I choose to use AbsoluteOptions instead of @kguler way because it's documented and hopefully stable, while not perfect.

As of @Kuba comment, I use Rasterize[#, "RasterSize"] to get the size of the plot labels, but apparently it's necessary to add something to the width to prevent the word-wrapping.

Any improvement on this direction appreciated.

$\endgroup$
  • $\begingroup$ do you have an example where Interval makes a difference, that is, where you get a different result when you remove Interval@@@ ? $\endgroup$ – kglr Oct 12 '14 at 7:20
  • $\begingroup$ You are right, Interval@@@ is probably superfluous. I started to use it because represent and remember the operation I'm doing, an union of several ranges. But I later realized that I can end up with an Interval still composed of two or more ranges, so I added the Through[{Min, Max}[#]] &. At this point Interval became unnecessary. $\endgroup$ – unlikely Oct 12 '14 at 7:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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