Point Renderings Slightly Off in Mathematica

Update: simple work-around added at bottom of post.

Analysis of the problem

This appears to be an issue with the default PlotMarkers. I do not see similar offsets using this:

Show[
 ListPlot[{stationaryPoints, inflectionPoints}, Frame -> True, 
  PlotMarkers -> {{Graphics@{Red, Disk[]}, 
     0.05}, {Graphics@{Blue, Rectangle[]}, 0.05}}],
 Plot[f[x], {x, -Pi - 1, Pi + 1}],
 ImageSize -> 500
]

enter image description here

You can see from this that the default PlotMarkers are actually font based, rather than Graphics primitives:

Graphics`PlotMarkers[] // InputForm
Graphics`PlotMarkers[][[1, 1]] // ToCharacterCode

I believe that these font glyphs are inherently prone to misalignment with Graphics primitives, due to a different rendering pipeline.


Addressing your question about (mis)alignment of PlotMarkers constructed from primitives, I believe this is the result of the chosen antialiasing scheme. One can see by magnifying a screen capture that orthogonal lines use "pixel snapping" or "hinting". This increases apparent sharpness, but sacrifices precise placement.

enter image description here

On my Windows system, a distinct "judder" is apparent in the following animation. Both the placement and the shape of the rectangles changes with ImageSize.

Show[ListPlot[{stationaryPoints, inflectionPoints}, 
          PlotMarkers ->
              {{Graphics@{Red, Disk[]}, 0.05},
               {Graphics@{Blue, Rectangle[]}, 0.05}}], 
     Plot[f[x], {x, -Pi - 1, Pi + 1}],
     Ticks -> None,
     ImageSize -> d
] ~Animate~ {d, 200, 300, 1}

I am not aware of a method to change the 2D antialiasing scheme. However, one work-around is to rasterize the image at a higher resolution, and then resample to the target size.


Changing the anti-aliasing method using Opacity

I learned from Simon Woods that using Opacity, even with an effectively opaque value of .999 (but not 1), will change the anti-aliasing method that is used for font glyphs. Therefore we can affect a work-around for this alignment problem by specifying: BaseStyle -> Opacity[.999] or BaseStyle -> {FontOpacity -> 0.999}.

Observe:

Show[Plot[f[x], {x, -Pi - 1, Pi + 1}], 
 ListPlot[{stationaryPoints, inflectionPoints}, PlotStyle -> {{Red}, {Blue}}, 
  PlotMarkers -> {Automatic, 15}, BaseStyle -> Opacity[.999]]]

enter image description here


This bug was already reported earlier in the official newsgroup several times. For example:

Dear all:

FYI, on Mathematica 8.0.1.0 (Linux x86-64), the following gives plot markers which are not centered on the line; they fall slightly below:

ListPlot[Transpose@Table[{1, 2, 3}, {x, 1, 10}], PlotMarkers -> {Automatic, 12}, Joined -> True]

On Mathematica 8.0.1.0 (Mac OS X), the plot markers are properly centered. I will use this as a workaround for now.

It is clear from this report that this behavior is OS-dependent. The reason for this was explained by Szabolcs Horvát:

Precise positioning is not really achievable when glyphs from a font are used as plot markers. The problem can be cured by using graphics objects:

PlotMarkers -> {Graphics[Circle[]], .05}

Or if you need disks as plot markers, then just use a larger point size.

The problem with the font-based Graphics`PlotMarkers[] is even worse than it should be because their resizing is implemented through the FontSize option which gives no smooth scaling for the glyphs:

In[8]:= Cases[ListPlot[{1}, PlotMarkers -> {Automatic, 12.5}], _Inset, 
  Infinity] // InputForm

Out[8]//InputForm=
{Inset[Style["\[FilledCircle]", FontSize -> 12.5], 3], 
    Inset[Style["\[FilledCircle]", FontSize -> 12.5], 4]}

One workaround for this is to implement your own scaling function:

PlotMarkers -> (Graphics`PlotMarkers[] /. {m_, s_} :> {m, s/2})

(in this example I made plot markers 2 times smaller).

But of course it does not solve the general problem with that Mathematica is unable to position font glyphs precisely. There are two workarounds: we could convert glyphs into outlines or define our own plot markers based on Graphics primitives. Here is my attempt to make a set of nice triangle-based plot markers:

align = Sequence[AlignmentPoint -> {0, 0}, AxesOrigin -> {0, 0}, 
   BaselinePosition -> Axis];
size = Sequence[PlotRange -> {{-2, 2}, {-2, 2}}, 
   PlotRangePadding -> Scaled[edgeThickness/2 + plotRangePadding], 
   ImagePadding -> 0, ImageMargins -> 0];
plotRangePadding = .01;
edgeThickness = .1;
size = Sequence[PlotRange -> {{-2, 2}, {-2, 2}}, 
   PlotRangePadding -> Scaled[edgeThickness/2 + plotRangePadding], 
   ImagePadding -> 0, ImageMargins -> 0];
halfTr = {Graphics[{EdgeForm[], FaceForm[Opacity[1]], 
     Polygon[{{0, 2}, {2/Sqrt[3], 0}, {-(2/Sqrt[3]), 0}}], 
     EdgeForm[{Opacity[1], Thickness[edgeThickness], 
       JoinForm["Round"]}], FaceForm[], 
     Polygon[{{0, 2}, {Sqrt[3], -1}, {-Sqrt[3], -1}}]}, align, 
    "size"], 
   Graphics[{EdgeForm[], FaceForm[Opacity[1]], 
     Polygon[{{2, 0}, {-1, 0}, {-1, Sqrt[3]}}], 
     EdgeForm[{Opacity[1], Thickness[edgeThickness], 
       JoinForm["Round"]}], FaceForm[], 
     Polygon[{{2, 0}, {-1, -Sqrt[3]}, {-1, Sqrt[3]}}]}, align, 
    "size"], 
   Graphics[{EdgeForm[], FaceForm[Opacity[1]], 
     Polygon[{{-Sqrt[3], 1}, {-(2/Sqrt[3]), 0}, {2/Sqrt[3], 0}, {Sqrt[
        3], 1}}], 
     EdgeForm[{Opacity[1], Thickness[edgeThickness], 
       JoinForm["Round"]}], FaceForm[], 
     Polygon[{{0, -2}, {-Sqrt[3], 1}, {Sqrt[3], 1}}]}, align, "size"],
    Graphics[{EdgeForm[], FaceForm[Opacity[1]], 
     Polygon[{{-2, 0}, {1, 0}, {1, Sqrt[3]}}], 
     EdgeForm[{Opacity[1], Thickness[edgeThickness], 
       JoinForm["Round"]}], FaceForm[], 
     Polygon[{{-2, 0}, {1, -Sqrt[3]}, {1, Sqrt[3]}}]}, align, "size"]};
halfTr = Flatten[{halfTr, 
     Table[halfTr /. 
       p : {_?NumericQ, _?NumericQ} :> 
        RotationMatrix[\[Theta]].p, {\[Theta], {Pi, Pi/2, -Pi/2}}]}, 
    2] /. "size" -> size;
Magnify[#, .1] & /@ halfTr
ListPlot[Flatten[Table[{{n, y}}, {y, Range[1, 3]}, {n, 20}], 1], 
 PlotMarkers -> Table[{s, 0.07}, {s, halfTr}], 
 PlotStyle -> ColorData[60, "ColorList"], 
 GridLines -> {Range[20], Range[3]}, PlotRange -> {{0, 21}, {0, 4}}, 
 Axes -> False, Frame -> True]

screenshot

I wonder: why a set of precise plot markers is not included in Mathematica by default? It is not too hard to implement such functionality but it takes significant time from an ordinary user to implement this...


f := #1 + Sin[2 #1] &;
inflectionPoints =       Table[{x, f[x]}, {x, -Pi, Pi, Pi/2}];
stationaryPoints = Union[Table[{x, f[x]}, {x, -2 Pi/3,   Pi/3, Pi}], 
                         Table[{x, f[x]}, {x, -  Pi/3, 2 Pi/3, Pi}]];
Show[
 Plot[f[x], {x, -Pi - 1, Pi + 1}], 
 Graphics[{Red,  PointSize[Large], Point /@ stationaryPoints,
           Blue, PointSize[Large], Point /@ inflectionPoints}]]

enter image description here

The problem is the ListPlot marker placement function:

Show[
 ListPlot[{stationaryPoints, inflectionPoints}, 
           PlotStyle   -> {{Yellow}, {Green}}, 
           PlotMarkers -> {Automatic, 15}], 
 Graphics[{Black, PointSize[Large], Point /@ stationaryPoints, 
           Black, PointSize[Large], Point /@ inflectionPoints}]]

enter image description here