Skip to main content
Updated to fix PlotRange.
Source Link
Carl Woll
  • 131.7k
  • 6
  • 246
  • 359

This is what I believe the situation to be. AbsoluteOptions uses FullAxes under the hood. It turns out that FullAxes is still expecting Frame/FrameLabel options to be specified using the old Frame -> {b, l, t, r} syntax instead of the new Frame->{{l, r}, {b, t}} syntax. This is why FullAxes issues messages and doesn't work. This means the solution is simply to fix these options before running AbsoluteOptions/FullAxes on the graphic.

(update to fix PlotRange as well)

It turns out that the function PlotRange also has an issue with some "malformed" plot ranges, so I updated the code to handle that as well.

Here is a method of doing sothe revised code:

Begin["FullAxesDump`"];

With[{graphic = ListLogPlot[{10, 100}]},
    If[Quiet @ TrueQ @ Check[FullAxes @ ListPlot[{0,1000}]graphic, True],
        Unprotect[FullAxes];
        FullAxes[arg_] /; !TrueQ@$FACheck := Block[{$FACheck = TrueFACheck=True},
            FullAxes[fixOptions@arg]
        ];
        Protect[FullAxes];
 
    fixOptions[x_]:=x;]
]

With[{graphic = Graphics[{}, GridLines->None, fixOptions[(tag:Graphics3D|Graphics)[g_PlotRange->{{0, opts__]]1}, :={All, tag[All}}]},
    If[Quiet @ TrueQ @ gCheck[PlotRange[graphic], True],
        SequenceUnprotect[PlotRange];
 @@ ReplaceAll[      PlotRange[arg_] /; !TrueQ@$FACheck := Block[{$FACheck=True},
            {opts,PlotRange[fixOptions[arg]]
 Frame->False, Axes->False},      ];
        Protect[PlotRange];
    Rule[h]
]

fixOptions[x_]:Frame|FrameTicks=x
fixOptions[(tag:Graphics3D|Graphics)[g_, rhs_]opts__]] :>= htag[
 -> fixRule[rhs]  g,
    Sequence@@ReplaceAll[
    ]    {opts},
    ];    Rule[h:Frame|FrameTicks|PlotRange,rhs_] :> h->fixRule[h,rhs]
    ],
    fixRule[Frame->False, Axes->False
]

fixRule[Frame|FrameTicks, {{l_, r_}, {b_, t_}}] := {b, l, t, r};
   fixRule[Frame|FrameTicks, fixRule[{d_, s_}] := {d, Automatic, s, Automatic};

fixRule[PlotRange, a_List] := Replace[a, fixRule[rhs_]{All, :=All}->All, rhs;
{1}]

fixRule[_,rhs_]:=rhs

End[];

This is what I believe the situation to be. AbsoluteOptions uses FullAxes under the hood. It turns out that FullAxes is still expecting Frame/FrameLabel options to be specified using the old Frame -> {b, l, t, r} syntax instead of the new Frame->{{l, r}, {b, t}} syntax. This is why FullAxes issues messages and doesn't work. This means the solution is simply to fix these options before running AbsoluteOptions/FullAxes on the graphic. Here is a method of doing so:

Begin["FullAxesDump`"];

If[Quiet @ TrueQ @ Check[FullAxes @ ListPlot[{0,1000}],True],
    Unprotect[FullAxes];
    FullAxes[arg_] /; !TrueQ@$FACheck := Block[{$FACheck = True},
        FullAxes[fixOptions@arg]
    ];
    Protect[FullAxes];
 
    fixOptions[x_]:=x;
    fixOptions[(tag:Graphics3D|Graphics)[g_, opts__]] := tag[
        g,
        Sequence @@ ReplaceAll[
            {opts, Frame->False, Axes->False},
            Rule[h:Frame|FrameTicks, rhs_] :> h -> fixRule[rhs]
        ]
    ];

    fixRule[{{l_, r_}, {b_, t_}}] := {b, l, t, r};
    fixRule[{d_, s_}] := {d, Automatic, s, Automatic};

    fixRule[rhs_] := rhs;
]

End[];

This is what I believe the situation to be. AbsoluteOptions uses FullAxes under the hood. It turns out that FullAxes is still expecting Frame/FrameLabel options to be specified using the old Frame -> {b, l, t, r} syntax instead of the new Frame->{{l, r}, {b, t}} syntax. This is why FullAxes issues messages and doesn't work. This means the solution is simply to fix these options before running AbsoluteOptions/FullAxes on the graphic.

(update to fix PlotRange as well)

It turns out that the function PlotRange also has an issue with some "malformed" plot ranges, so I updated the code to handle that as well.

Here is the revised code:

Begin["FullAxesDump`"];

With[{graphic = ListLogPlot[{10, 100}]},
    If[Quiet @ TrueQ @ Check[FullAxes @ graphic, True],
        Unprotect[FullAxes];
        FullAxes[arg_] /; !TrueQ@$FACheck := Block[{$FACheck=True},
            FullAxes[fixOptions@arg]
        ];
        Protect[FullAxes];
    ]
]

With[{graphic = Graphics[{}, GridLines->None, PlotRange->{{0, 1}, {All, All}}]},
    If[Quiet @ TrueQ @ Check[PlotRange[graphic], True],
        Unprotect[PlotRange];
        PlotRange[arg_] /; !TrueQ@$FACheck := Block[{$FACheck=True},
            PlotRange[fixOptions[arg]]
        ];
        Protect[PlotRange];
    ]
]

fixOptions[x_]:=x
fixOptions[(tag:Graphics3D|Graphics)[g_,opts__]] := tag[
    g,
    Sequence@@ReplaceAll[
        {opts},
        Rule[h:Frame|FrameTicks|PlotRange,rhs_] :> h->fixRule[h,rhs]
    ],
    Frame->False, Axes->False
]

fixRule[Frame|FrameTicks, {{l_,r_},{b_,t_}}] := {b,l,t,r}
fixRule[Frame|FrameTicks, {d_,s_}] := {d,Automatic,s,Automatic}

fixRule[PlotRange, a_List] := Replace[a, {All, All}->All, {1}]

fixRule[_,rhs_]:=rhs

End[];
Remove superfluous code.
Source Link
Carl Woll
  • 131.7k
  • 6
  • 246
  • 359
System`Private`NewContextPath[{"System`"}];

Begin["FullAxesDump`"];

If[Quiet @ TrueQ @ Check[FullAxes @ ListPlot[{0,1000}],True],
    Unprotect[FullAxes];
    FullAxes[arg_] /; !TrueQ@$FACheck := Block[{$FACheck = True},
        FullAxes[fixOptions@arg]
    ];
    Protect[FullAxes];

    fixOptions[x_]:=x;
    fixOptions[(tag:Graphics3D|Graphics)[g_, opts__]] := tag[
        g,
        Sequence @@ ReplaceAll[
            {opts, Frame->False, Axes->False},
            Rule[h:Frame|FrameTicks, rhs_] :> h -> fixRule[rhs]
        ]
    ];

    fixRule[{{l_, r_}, {b_, t_}}] := {b, l, t, r};
    fixRule[{d_, s_}] := {d, Automatic, s, Automatic};

    fixRule[rhs_] := rhs;
]

End[];
 
System`Private`RestoreContextPath[];
  1. I useinitally used System`Private`NewContextPath/System`Private`RestoreContextPath so that the variables inside of Begin["FullAxesDump`"]/End[] use the "FullAxesDump`" context instead of theand "Global`"System`Private`RestoreContextPath context. Without the context path switching code, the variables in the above cell get theirbecause I had trouble with contexts at parse time (when the cell is read into the kernel) and not after the Begin statement is processed. An alternative is to make this a real packageof my variable names, but that seems too heavyweight formust have been a simple bug fixtransient thing related to earlier code.

  2. I only redefine FullAxes if using FullAxes on a ListPlot issues messages. This means that if you want to change the code after running it, you will need to first clear the new FullAxes downvalue that is created by the code. Something along the lines of Unprotect[FullAxes]; Clear[FullAxes]; Protect[FullAxes];

  3. I use the foo /; ! TrueQ@flag := Block[{flag = True}, foo] trick so that the options get tweaked, and then the existing kernel code for foo gets run.

  4. It turns out that Frame -> False needs to get explicitly added to the options so that FullAxes realizes that there really isn't a Frame, and it must process the Ticks/Axes code. Without Frame -> False, the FullAxes code turns Axes -> True into Axes -> {False, False}. Note that options handling uses the first instance of an option, so adding the default (Frame -> False) at the end should not affect output.

System`Private`NewContextPath[{"System`"}];

Begin["FullAxesDump`"];

If[Quiet @ TrueQ @ Check[FullAxes @ ListPlot[{0,1000}],True],
    Unprotect[FullAxes];
    FullAxes[arg_] /; !TrueQ@$FACheck := Block[{$FACheck = True},
        FullAxes[fixOptions@arg]
    ];
    Protect[FullAxes];

    fixOptions[x_]:=x;
    fixOptions[(tag:Graphics3D|Graphics)[g_, opts__]] := tag[
        g,
        Sequence @@ ReplaceAll[
            {opts, Frame->False, Axes->False},
            Rule[h:Frame|FrameTicks, rhs_] :> h -> fixRule[rhs]
        ]
    ];

    fixRule[{{l_, r_}, {b_, t_}}] := {b, l, t, r};
    fixRule[{d_, s_}] := {d, Automatic, s, Automatic};

    fixRule[rhs_] := rhs;
]

End[];
 
System`Private`RestoreContextPath[];
  1. I use System`Private`NewContextPath/System`Private`RestoreContextPath so that the variables inside of Begin["FullAxesDump`"]/End[] use the "FullAxesDump`" context instead of the "Global`" context. Without the context path switching code, the variables in the above cell get their contexts at parse time (when the cell is read into the kernel) and not after the Begin statement is processed. An alternative is to make this a real package, but that seems too heavyweight for a simple bug fix.

  2. I only redefine FullAxes if using FullAxes on a ListPlot issues messages. This means that if you want to change the code after running it, you will need to first clear the new FullAxes downvalue that is created by the code. Something along the lines of Unprotect[FullAxes]; Clear[FullAxes]; Protect[FullAxes];

  3. I use the foo /; ! TrueQ@flag := Block[{flag = True}, foo] trick so that the options get tweaked, and then the existing kernel code for foo gets run.

  4. It turns out that Frame -> False needs to get explicitly added to the options so that FullAxes realizes that there really isn't a Frame, and it must process the Ticks/Axes code. Without Frame -> False, the FullAxes code turns Axes -> True into Axes -> {False, False}. Note that options handling uses the first instance of an option, so adding the default (Frame -> False) at the end should not affect output.

Begin["FullAxesDump`"];

If[Quiet @ TrueQ @ Check[FullAxes @ ListPlot[{0,1000}],True],
    Unprotect[FullAxes];
    FullAxes[arg_] /; !TrueQ@$FACheck := Block[{$FACheck = True},
        FullAxes[fixOptions@arg]
    ];
    Protect[FullAxes];

    fixOptions[x_]:=x;
    fixOptions[(tag:Graphics3D|Graphics)[g_, opts__]] := tag[
        g,
        Sequence @@ ReplaceAll[
            {opts, Frame->False, Axes->False},
            Rule[h:Frame|FrameTicks, rhs_] :> h -> fixRule[rhs]
        ]
    ];

    fixRule[{{l_, r_}, {b_, t_}}] := {b, l, t, r};
    fixRule[{d_, s_}] := {d, Automatic, s, Automatic};

    fixRule[rhs_] := rhs;
]

End[];
  1. I initally used System`Private`NewContextPath and System`Private`RestoreContextPathbecause I had trouble with contexts of my variable names, but that must have been a transient thing related to earlier code.

  2. I only redefine FullAxes if using FullAxes on a ListPlot issues messages. This means that if you want to change the code after running it, you will need to first clear the new FullAxes downvalue that is created by the code. Something along the lines of Unprotect[FullAxes]; Clear[FullAxes]; Protect[FullAxes];

  3. I use the foo /; ! TrueQ@flag := Block[{flag = True}, foo] trick so that the options get tweaked, and then the existing kernel code for foo gets run.

  4. It turns out that Frame -> False needs to get explicitly added to the options so that FullAxes realizes that there really isn't a Frame, and it must process the Ticks/Axes code. Without Frame -> False, the FullAxes code turns Axes -> True into Axes -> {False, False}. Note that options handling uses the first instance of an option, so adding the default (Frame -> False) at the end should not affect output.

Source Link
Carl Woll
  • 131.7k
  • 6
  • 246
  • 359

This is what I believe the situation to be. AbsoluteOptions uses FullAxes under the hood. It turns out that FullAxes is still expecting Frame/FrameLabel options to be specified using the old Frame -> {b, l, t, r} syntax instead of the new Frame->{{l, r}, {b, t}} syntax. This is why FullAxes issues messages and doesn't work. This means the solution is simply to fix these options before running AbsoluteOptions/FullAxes on the graphic. Here is a method of doing so:

System`Private`NewContextPath[{"System`"}];

Begin["FullAxesDump`"];

If[Quiet @ TrueQ @ Check[FullAxes @ ListPlot[{0,1000}],True],
    Unprotect[FullAxes];
    FullAxes[arg_] /; !TrueQ@$FACheck := Block[{$FACheck = True},
        FullAxes[fixOptions@arg]
    ];
    Protect[FullAxes];

    fixOptions[x_]:=x;
    fixOptions[(tag:Graphics3D|Graphics)[g_, opts__]] := tag[
        g,
        Sequence @@ ReplaceAll[
            {opts, Frame->False, Axes->False},
            Rule[h:Frame|FrameTicks, rhs_] :> h -> fixRule[rhs]
        ]
    ];

    fixRule[{{l_, r_}, {b_, t_}}] := {b, l, t, r};
    fixRule[{d_, s_}] := {d, Automatic, s, Automatic};

    fixRule[rhs_] := rhs;
]

End[];

System`Private`RestoreContextPath[];

Some comments:

  1. I use System`Private`NewContextPath/System`Private`RestoreContextPath so that the variables inside of Begin["FullAxesDump`"]/End[] use the "FullAxesDump`" context instead of the "Global`" context. Without the context path switching code, the variables in the above cell get their contexts at parse time (when the cell is read into the kernel) and not after the Begin statement is processed. An alternative is to make this a real package, but that seems too heavyweight for a simple bug fix.

  2. I only redefine FullAxes if using FullAxes on a ListPlot issues messages. This means that if you want to change the code after running it, you will need to first clear the new FullAxes downvalue that is created by the code. Something along the lines of Unprotect[FullAxes]; Clear[FullAxes]; Protect[FullAxes];

  3. I use the foo /; ! TrueQ@flag := Block[{flag = True}, foo] trick so that the options get tweaked, and then the existing kernel code for foo gets run.

  4. It turns out that Frame -> False needs to get explicitly added to the options so that FullAxes realizes that there really isn't a Frame, and it must process the Ticks/Axes code. Without Frame -> False, the FullAxes code turns Axes -> True into Axes -> {False, False}. Note that options handling uses the first instance of an option, so adding the default (Frame -> False) at the end should not affect output.

I think that's enough explanation. Here is what happens after loading the above code:

AbsoluteOptions[LogPlot[x^x, {x, 1, 5}, Frame -> True], FrameTicks];

AbsoluteOptions[ReliefPlot[RandomReal[1, {10, 10}]]];

AbsoluteOptions[ParametricPlot[r t, {r, 0, 5}, {t, 1, 2}]];

No error messages, although I don't claim that this fixes all cases where AbsoluteOptions issues messages. A similar treatment is possible for FullGraphics