# How to scale a vertical quantity (e.g., scaled offset) with the width of a plot?

As stated in the Mathematica documentation,

PointSize[d] is a graphics directive which specifies that points which follow are to be shown if possible as circular regions with diameter d. The diameter d is given as a fraction of the total width of the plot.

On the other hand,

Scaled[{dx,dy,...},{x_0,y_0,...}] gives a position obtained by starting at ordinary coordinates x_0,y_0,..., then moving by a scaled offset {dx,dy,...}.

In this case, the scaled offsets are specified as a fraction of each plot direction.

This leads to an apparent incompatibility in combining circular objects whose sizes are determined by the width of the plot (and because they are circular, their extent along the vertical direction is also specified by the width of the plot) and objects with vertical offsets which are determined by the height of the plot -- how can this be reconciled?

As an example, if I apply a horizontal offset, the offset objects are positioned as expected (on the boundary of the points), regardless of the specified plot range:

sites = Flatten[Table[{i, j}, {i, 0, 3}, {j, 0, 1}], 1];
psize = 0.12;
offset = psize/2;

Graphics[{PointSize[psize], Map[Point[#] &, sites], PointSize[0.2 psize],
Red, Map[Point[Scaled[{offset, 0}, #]] &, sites]},
PlotRange -> Automatic, Frame -> True]
Graphics[{PointSize[psize], Map[Point[#] &, sites], PointSize[0.2 psize],
Red, Map[Point[Scaled[{offset, 0}, #]] &, sites]},
PlotRange -> {{-1, 4}, Automatic}, Frame -> True]


If I try the same with a vertical offset, however, the offsets do not remain fixed since the point size is changing as the width of the plot is adjusted:

Graphics[{PointSize[psize], Map[Point[#] &, sites], PointSize[0.2 psize],
Red, Map[Point[Scaled[{0, offset}, #]] &, sites]},
PlotRange -> Automatic, Frame -> True]
Graphics[{PointSize[psize], Map[Point[#] &, sites], PointSize[0.2 psize],
Red, Map[Point[Scaled[{0, offset}, #]] &, sites]},
PlotRange -> {{-1, 4}, Automatic}, Frame -> True]


Is there way to specify a vertical offset based on the width of the plot, or some other way to resolve this issue?

Lastly, I am using Point rather than Disk because of flattening that occurs do to scaling, as discussed in this question. So any solution that needs to replace Point with Disk would preferably have a way of resolving the flattening issue, as well.

Thank you!

• You could use AspectRatio ->1 but that would distort the axis scales, which is probably not something you want... – Jens Jun 27 '18 at 17:22
• @Jens Yeah, that wouldn't be an ideal solution, as it does cause various problematic distortions. Thanks for the response, though. – AnInquiringMind Jun 27 '18 at 21:03

If you know in advance that the option value for AspectRatio is, say, ar you can simply use offset / ar for the vertical offset. If not, you can post-process the Graphics output to extract the AspectRatio and make the necessary change in Scaled coordinates using ReplaceAll:

ClearAll[aRatio, adjustScaled]
aRatio =  AspectRatio /. AbsoluteOptions[#, AspectRatio] &;
adjustScaled = # /. Scaled[a_, b___] :> Scaled[{1, 1 / aRatio[#]} a, b] &;


Examples:

sites = Flatten[Table[{i, j}, {i, 0, 3}, {j, 0, 1}], 1];
psize = 0.12;
offset = psize/2;

g3 = Graphics[{PointSize[psize], Map[Point[#] &, sites],
PointSize[0.2 psize], Red, Map[Point[Scaled[{0, offset}, #]] &, sites]},
PlotRange -> Automatic, Frame -> True];
g4 = Graphics[{PointSize[psize], Map[Point[#] &, sites],
PointSize[0.2 psize], Red, Map[Point[Scaled[{0, offset}, #]] &, sites]},
PlotRange -> {{-1, 4}, Automatic}, Frame -> True];
g5 = Show[g3, AspectRatio -> 1/2, PlotRange -> {{-2, 5}, {-1, 2}}];

Row[{g3, adjustScaled @ g3}, Spacer] // Style[#, ImageSizeMultipliers -> {1, 1}] & Row[{g4, adjustScaled @ g3}, Spacer] // Style[#, ImageSizeMultipliers -> {1, 1}] & Row[{g5, adjustScaled @ g3}, Spacer] // Style[#, ImageSizeMultipliers -> {1, 1}] & The point sizes scale with the image size:

ag4 = adjustScaled @ g4;
frames = Table[Pane[Show[ag4, ImageSize -> s],
ImageSize -> {500, 300}, Alignment -> Center],
{s, Join @@ {#, Reverse @ Most @ #} & @ Range[100, 500, 10]}];
Export["g4.gif", frames] • Thanks for the solution! I will have to test this out in non-contrived situations to get a sense of the performance hit, but it does exactly what I want. – AnInquiringMind Jul 24 '18 at 4:39

Instead of trying to use PointSize and Scaled, why not use AbsolutePointSize and Offset? For example (note that I tweaked your code slightly to use Tuples to generate the sites and a single Point object where possible):

sites = Tuples[{Range[0,3], Range[0,1]}];
psize = 40;
offset = psize/2;

Graphics[
{
AbsolutePointSize[psize], Point[sites],
AbsolutePointSize[.2 psize], Red, Point[Thread[Offset[{offset,0}, sites], List, -1]]
},
PlotRange->Automatic,
Frame->True
]

Graphics[
{
AbsolutePointSize[psize], Point[sites],
AbsolutePointSize[.2 psize], Red, Point[Thread[Offset[{0, offset}, sites], List, -1]]
},
PlotRange->Automatic,
Frame->True
]  