# Create self-contained plots when Ticks or FrameTicks require external functions

Let's say I define tick marks for a plot in a separate function outside of the plot:

tick[xmin_, xmax_] :=
Map[{#, "", .03} &, FindDivisions[{xmin, xmax}, 4]];
p = Plot[Sin[x], {x, -Pi, Pi}, Frame -> True,
FrameTicks -> {{tick, tick}, {tick, tick}}];
q = Plot[Sin[x], {x, -Pi, Pi}, Axes -> True,
Ticks -> {tick, tick}];


Now these boring plots can be made much more colorful by evaluating this additional command:

Clear[tick]


The result of evaluating p (and similarly q) is now:

I didn't say it's colorful in a good way.

This is a problem especially if you have defined your tick function in the initialization of the notebook via SetOptions[Plot,...], and then at a later stage decide to copy and paste the Graphics output by one of these plots to another notebook where this initialization is not performed. Assuming that the Graphics is a self-contained drawing that can be copied in this way leads to an unpleasant surprise: although you can paste the graphic, you'll get an error when you paste it into a Show[...] expression and evaluate it:

So what to do about this? Of course, I could always embed all the tick functions in the plot itself, as in

q = Plot[Sin[x], {x, -Pi, Pi}, Axes -> True,
Ticks -> ({#, #} &[
Function[{xmin, xmax},
Map[{#, "", .03} &, FindDivisions[{xmin, xmax}, 4]]]])];


But this seems very cumbersome.

Alternatively, one could convert to PDF and back to Graphics using First@ImportString[ExportString[q,"PDF"],"PDF"]. But that's a big gun for a very specialized problem.

Another possibility is to hit the plot output with FullGraphics. However, that's buggy. For example,

FullGraphics[Plot[x^2, {x, -1, 1}, TicksStyle -> RGBColor[1, 0, 0]]]


can't seem to reproduce the red color for the tick marks (confirmed by Wolfram as a bug).

So are there any better ways to "burn in" the externally defined tick functions in the above examples so that the plot results can be considered self-contained? I guess I'm thinking of something like SaveDefinitions->True for plots. Come to think of it, maybe putting the plot in a Manipulate is another option... but ideally, I'd like to have a Graphics object. So at the time of generating the plot, one should force Mathematica to store the table of actually generated ticks in the graphic. Actually, something like this already happens in the notebook when I copy and paste a graphic (some data corresponding to the currently displayed graphic are cached). It would be nice if the tick function itself could take care of replacing itself with an explicit tick list.

Edit

The first screenshot is fixed by Rojo's answer, and that part of the question is really not caused by a bug in Mathematica. It's the special status of tick marks that requires the tick function to be available whenever we decide to show the plot output with a different plot range (using Show, e.g.).

The second screen shot above is caused by a bug (also suggested by Heike's observation), and therefore I can't expect anyone to give a good answer here (except for work-arounds). To show the bug more clearly: I still get an error even with this simple (apparently self-contained) definition:

q1 = Plot[Sin[x], {x, -Pi, Pi}, Axes -> True,
Ticks -> {Function[{xmin, xmax},
Map[{#, "", .03} &, Evaluate@FindDivisions[{xmin, xmax}, 4]]],
Automatic}]


Here is the result of pasting the output from above into a Show[ ] statement:

However, this is "fixed" by changing FindDivisions[{xmin, xmax}, 4] to FindDivisions[{xmin, xmax}, Round[4]]

-
FullGraphics is far too inconsistent and buggy for general use. –  Szabolcs Apr 20 '12 at 11:21
It is clear why this happens, but it was surprising that AbsoluteOptions give an error on p. –  Szabolcs Apr 20 '12 at 11:27
@Szabolcs Just try Off[General::stop]; AbsoluteOptions[Plot[Sin@x, {x, 0, 1}], #] & /@ (Options[Plot] /. Alternatives[(x_ -> ), (x :> _)] :> x) –  belisarius Apr 20 '12 at 11:52

Try giving the ticks as pure functions, so they actually get replaced

The copying issue I don't quite get it. It seems that the returned box structure changed the integers into doubles (the last argument of FindDivisions, the 4, into 4. in this case). It could be fixed by rewriting the function as

ff=Function[{xmin, xmax}, ({#1, "", 0.03} &) /@
FindDivisions[{xmin, xmax}, Round@4]]


with Ticks -> ({#, #} &[ff]) as you wrote in the comment

I vote for bug, let's see what others think

-
The second suggestion works! I just take ff = Function[{xmin, xmax}, Map[{#, "", .03} &, FindDivisions[{xmin, xmax}, 4]]] and say Ticks -> ({#, #} &[ff]) in the plot. That's it. The first option you suggested doesn't work - output still depends on whether tick exists. But basically you solved my problem, I think. –  Jens Apr 20 '12 at 6:35
Outch, it still doesn't work with copy and paste, as with the Show[...] example in my question. At least it's an improvement. –  Jens Apr 20 '12 at 6:39
@Jens But so far I have no clue about the copying issue –  Rojo Apr 20 '12 at 6:50
Maybe tomorrow - it's late here. –  Jens Apr 20 '12 at 6:55
@Jens, then tomorrow check the edit >P –  Rojo Apr 20 '12 at 7:24

The copy issue seems to be related to FindDivisions. For example when I use

tick = Function[{xmin, xmax}, Map[{#, "", .03} &, Range[xmin, xmax, (xmax-xmin)/4]]];


I don't get an error when copying the graphics as described above.

As a workaround you could hard wire the absolute values for the tick marks into your plot by doing something like

q = Module[{pp},
pp = Plot[Sin[x], {x, -Pi, Pi}, Axes -> True, Ticks -> {tick, tick}];
pp /. PatternSequence[Ticks -> _] :> AbsoluteOptions[pp, Ticks][[1]]];


and something similar using FrameTicks for p. This should solve the copy issue, but the downside is that the tick marks aren't updated when you change the plot range of q using for example Show`.

-
You make two important points: FindDivisions is causing trouble, and it's desirable to be able to update the plot range later. I'll put another screen shot in that points to a FindDivision problem. –  Jens Apr 20 '12 at 16:08