Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to determine PlotRange to include all of graphics?

Given Graphics object, how do I determine the range of coordinates needed to include all of graphics? Basically I need something like what Show does by default, but I want to specify PlotRange,PlotRangePadding and ImagePadding explicitly.

Example, two Shows below should render the same

g = Graphics[{Thickness[1], CapForm["Round"], Line[{{0, 0}, {1, 1}}]}];
Show[g]
Show[g, PlotRange -> getPlotRange[g], PlotRangePadding->getPlotRangePadding[g], ImagePadding->getImagePadding[g]]

Motivation: fixing diagrams in this question

Update: AbsoluteOptions gives me PlotRange but not the other two options. Explicitly specifying ImagePadding->Automatic changes appearance though it's supposedly Automatic by default.

Two images below show differently and I don't understand why

g = Graphics[{Thickness[1], CapForm["Round"], Line[{{0, 0}, {1, 1}}]}];
Show[g]
Show[g, Sequence @@ AbsoluteOptions[Show[g]]]

Update 2: A similar problem was brought up a year ago, with no solutions proposed, and not fixed as of Mathematica 8.0. To summarize

  1. There's no way to reproduce Show[g] above with explicit setting of PlotRange
  2. There's no way to get absolute ImagePadding used by Show[g]
  3. Show[g,PlotRange->Automatic] looks different from Show[g]
  4. AbsoluteOptions can give the wrong result for PlotRange
like image 243
Yaroslav Bulatov Avatar asked Nov 26 '10 21:11

Yaroslav Bulatov


3 Answers

I can suggest the following Ticks hack:

pl = Plot[Sin[x], {x, 0, 10}];
Reap[Rasterize[Show[pl, Ticks -> {Sow[{##}] &, Sow[{##}] &}, ImageSize -> 0], 
   ImageResolution -> 1]][[2, 1]]

=> {{-0.208333, 10.2083}, {-1.04167, 1.04167}} 

The trick is that real PlotRange is determined by the FrontEnd, not by the Kernel. So we must force the FrontEnd to render the graphics in order to get tick functions evaluated. This hack gives the complete PlotRange with explicit value of PlotRangePadding added.

More general solution taking into account a possibility that pl has non-standard value of DisplayFinction option and that it may have Axes option set to False:

completePlotRange[plot:(_Graphics|_Graphics3D|_Graph)] := 
 Quiet@Last@
   Last@Reap[
     Rasterize[
      Show[plot, Axes -> True, Frame -> False, Ticks -> (Sow[{##}] &), 
       DisplayFunction -> Identity, ImageSize -> 0], ImageResolution -> 1]]

One can get the exact PlotRange (without the PlotRangePadding added) with the following function:

plotRange[plot : (_Graphics | _Graphics3D | _Graph)] := 
 Quiet@Last@
   Last@Reap[
     Rasterize[
      Show[plot, PlotRangePadding -> None, Axes -> True, Frame -> False, 
       Ticks -> (Sow[{##}] &), DisplayFunction -> Identity, ImageSize -> 0], 
      ImageResolution -> 1]]

P.S. On the Documentation page for PlotRange under the "More information" one can read: "AbsoluteOptions gives the actual settings for options used internally by Mathematica when the setting given is Automatic or All. " (emphasis mine). So it seems that the Documentation does not even guarantee that AbsoluteOptions will give correct values for PlotRange when it is not Automatic or All.

like image 151
Alexey Popkov Avatar answered Nov 13 '22 09:11

Alexey Popkov


I, too, sometimes find it confusing how to get Mathematica to display Graphics in a consistent way, particularly when insetting graphics.

For the specified graphic g, it doesn't matter what you provide for the PlotRange, because Thickness[1] always draws a line whose thickness is equal to the horizontal plot range. In your example, Show[g, ___] gives the correct result:

automatic image padding.

Show[g], or simply g, is anomalous.

Why?

I don't know where/if this is documented, but here are a few things that might be relevant to the question.

  1. Obviously DisplayForm[Graphics[___]] is a raster.

  2. We can get a raster for g using Rasterize[g]. What is the RasterSize? From trial and error, I found that RasterSize is 10 * screen resolution (reported as 72 pixels per inch on my system). How do I know this? If I rasterize g with resolutions less than 718, I get an image with dimensions {360,361}, whereas the default image size for g is 360 pixels on my system, so I figure to Show[] a graphic, Mathematica Rasterize's it at 10x the screen resolution. Anybody know if this is true? You can get your screen resolution (at least as Mathematica sees it) from the Options Inspector. Edit That the following expression evaluates as True seems to show that the displayed graphic is rasterized at the ImageSize: ImportString[ExportString[Show[g,ImageSize->100],"PNG"]] === ImportString[ExportString[Rasterize[g,RasterSize->100,ImageSize->100],"PNG"]

  3. To reproduce Show[g] when using PlotRange I need to use Show[g,PlotRange->{{0,1},{0,1}},ImagePadding->90.3]

specific image padding

to get it to crop to the perimeter of the line. So it seems that Mathematica is telling the truth that the PlotRange is {{0,1},{0,1}} when using AbsoluteOptions[]. It is not reporting the actual value of ImagePadding. Perhaps because ImagePadding->Automatic is based on a rule that uses the current ImageSize, PlotRangeClipping,... settings? The ImagePadding of 90.3 only works for ImageSize->360; setting ImageSize->200 makes the ImagePadding value wrong. For your graphic, ImagePadding->90.3*OptionValue[ImageSize]/360 reproduces Show[g,ImageSize->_] on my system.

That's all I've found out so far.

like image 28
JxB Avatar answered Nov 13 '22 10:11

JxB


You can try adding a recognizable object at a known location and then see where it shows up in the exported version to provide a scale reference. I thought a vector export (SVG or EPS) would be easier to parse, but I think raster is easier after playing around a bit.

For example, add a green rectangle covering the theoretical plot range:

g = Graphics[{Blue, Thickness[1], CapForm["Round"], 
   Line[{{0, 0}, {1, 1}}], Green, Rectangle[{0, 0}, {1, 1}]}];

enter image description here

im = Rasterize[g, ImageSize -> 360];
xy = Transpose[Position[ImageData[im], {0., 1., 0.}]];
pad = Map[{Min[#1], 360 - Max[#1] } &, xy];
Show[g, ImagePadding -> pad]

The code is basically identifying where all the green pixels are. The padding in this case is {{92, 92}, {92, 92}}, but it need not be symmetrical.

like image 3
xan Avatar answered Nov 13 '22 09:11

xan