4
$\begingroup$

A ListPlot's default markers resize as the plot is resized.

ListPlot[{{{1, 1}}, {{2, 2}}, {{3, 3}}}]

However, if you specify custom PlotMarkers that use Scaled for their dimensions then these markers do not resize as the plot is resized.

ListPlot[{{{1, 1}}, {{2, 2}}, {{3, 3}}},
 PlotMarkers -> Graphics /@ {
    Circle[{0, 0}, Scaled[.1]],
    Disk[{0, 0}, Scaled[.1]],
    Disk[{0, 0}, Scaled[.1], {0, 4}]}]

How do you specify PlotMarkers that resize when the plot is resized?

A solution that does not use Dynamic is preferred. I am thinking that there must be a means to do this since without Dynamic it works on the default markers.

$\endgroup$
3
$\begingroup$

There is a way to specify scaled sizes of the plot markers explicitly via the PlotMarkers option. Citing the Documentation page for PlotMarkers (under the "Details" section):

With PlotMarkers -> {g, s}, the object g is scaled so that one unit of its coordinates corresponds to a fraction s of the width of the whole graphic.

This statement is slightly incorrect because the specification PlotMarkers -> {g, s} (where g has Head Graphics) is directly translated by ListPlot into

Inset[g, pos, Automatic, Scaled[{s, s}]]

(where pos is coordinates of a datapoint) what can be seen from InpuForm of generated graphics:

First@Cases[
   ListPlot[{0, 1, 3, 4, 5}, PlotMarkers -> {{Graphics[Disk[]], .1}}],
   _Inset, -1] /. _Graphics -> g
Inset[g, {1., 0.}, Automatic, Scaled[{0.1, 0.1}]]

So a more correct statement would be:

With PlotMarkers -> {g, s}, the object g with head Graphics is scaled so that its size is equal to a fraction s of the whole plotting range.


For the case in the question a solution may be written as follows:

ListPlot[{{{1, 1}}, {{2, 2}}, {{3, 3}}}, PlotRange -> {{0, 4}, All}, 
 PlotMarkers -> ({Graphics[#], .1} & /@ {Circle[{0, 0}, 1], Disk[{0, 0}, 1], 
     Disk[{0, 0}, 1, {0, 4}]})]

output

Note that you don't have to use Scaled radii/coordinates in this case (although you can if you have a good reason for this).

Note also that if you wrap your primitives by List, then they will be styled automatically according to PlotStyle option:

ListPlot[{{{1, 1}}, {{2, 2}}, {{3, 3}}}, PlotRange -> {{0, 4}, All}, 
 PlotMarkers -> ({Graphics[{#}], .1} & /@ {Circle[{0, 0}, 1], Disk[{0, 0}, 1], 
     Disk[{0, 0}, 1, {0, 4}]})]

output

$\endgroup$
  • $\begingroup$ This is excellent. I missed this syntax option. (+1) $\endgroup$ – Edmund May 12 '17 at 17:06
2
$\begingroup$

Here is a post-processing approach:

ListPlot[{{{1, 1}}, {{2, 2}}, {{3, 3}}},
 PlotMarkers -> Graphics /@ {
    Circle[{0, 0}, Scaled[.1]],
    Disk[{0, 0}, Scaled[.1]],
    Disk[{0, 0}, Scaled[.1], {0, 4}]}
 ] /. Inset[g_Graphics, pos_] :> Inset[g, pos, Automatic, Scaled[1]]

Explanation. By default the inset is rendered with fixed size and your Scaled radius is calculated relative to this fixed size. When we specify the size of inset to be Scaled[1] we make the size of the inset equal to the size of the whole plotting range of the enclosing graphics. As the result when you resize the enclosing graphics (by the mouse or by changing the ImageSize option) the inset is scaled with it.

$\endgroup$
  • $\begingroup$ Good workaround. (+1) Do you think this is a bug with Scaled? $\endgroup$ – Edmund May 12 '17 at 14:19
  • $\begingroup$ @Edmund No, Inset behaves here exactly as described on its Documentation page: your radius Scaled[.1] is calculated relative to the size of Inset, not relative to the size of the whole plot. My solution is to make the size of Inset be equal to the size of the whole plot. $\endgroup$ – Alexey Popkov May 12 '17 at 14:54

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.