2
$\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$
2
  • $\begingroup$ I think the 'clarification' given in the update makes this question too vague to be answered. $\endgroup$
    – m_goldberg
    Oct 11, 2014 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, 2014 at 11:11

3 Answers 3

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$
3
  • $\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, 2014 at 10:14
  • $\begingroup$ @unlikely well, there is, Rasterize+ImageDimensions but it is not the cleanest method :/ $\endgroup$
    – Kuba
    Oct 11, 2014 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, 2014 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$
7
  • $\begingroup$ Thanks. +1 for the defaulted-but-not-fixed height of padding, but AbsoluteOptions is not more robust than Options? $\endgroup$
    – unlikely
    Oct 11, 2014 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, 2014 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, 2014 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, 2014 at 11:59
  • $\begingroup$ .. i like StringForm too -- and yes, input form does look scary:) $\endgroup$
    – kglr
    Oct 11, 2014 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$
2
  • $\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, 2014 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, 2014 at 7:56

Your Answer

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

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