69
$\begingroup$

The Mathematica code

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}], 
     ListPlot[{stationaryPoints, inflectionPoints}, 
     PlotStyle -> {{Red}, {Blue}}, 
  PlotMarkers -> {Automatic, 15}]]

produces:

original

The points (circles and squares) are supposed to be at the critical points, but (as can be seen most clearly via the point at the origin) the points are too far down and too far left.

After manually adjusting the points, I expect the original output should look more like this: enter image description here

Question

Why are the points being rendered at the wrong positions? How can this be fixed?

(First) Response to answers

First off, yoda suggested that this might be OS dependent. I am using Ubuntu 9.10 32-bit with Mathematica 8.0.1.0.

Mr. Wizard's code looks correct in my Mathematica, but if I remove the frame, then the points are rendered too far to the left:

enter image description here

belisarius's code from his machine looks good (see picture in his answer), but on my Mathematica, the points are again rendered too far to the left:

enter image description here

Mr. Wizard and belisarius (and others), do these new facts change your answer?

Conclusion

I believe Mr. Wizard's (as explained in more detail by Alexey Popkov) provides the correct answer. My example (above) of Mr. Wizard's code having points slightly shifted to the left seems to be a different problem. Namely, the picture is so small that Mathematica cannot position things properly. Using a larger image size, everything appears to be in the correct position.

$\endgroup$
6
  • 1
    $\begingroup$ my eyesight is not good enough to see the displacement in your (my) Graphics@Point ... But if Graphics[{PointSize[Large], Point@{0, 0}}, Axes -> True] is not centered in your machine ... the problem is really big $\endgroup$ Oct 18, 2011 at 3:37
  • $\begingroup$ @belisarius Your code in the above comment works as expected...the point appears centered. $\endgroup$
    – Tyson Williams
    Oct 19, 2011 at 14:11
  • $\begingroup$ but the blue point at {0,0} in your last plot is not centered, and it is generated using the same expression $\endgroup$ Oct 19, 2011 at 14:33
  • $\begingroup$ @belisarius Yes, I think that image suffers from the same problem I just mentioned in the new Conclusion section...that points are now placed correctly for smaller image sizes. $\endgroup$
    – Tyson Williams
    Oct 19, 2011 at 15:03
  • $\begingroup$ I see the left-shift you are taking about. I shall try to find an explanation or workaround. $\endgroup$
    – Mr.Wizard
    Oct 19, 2011 at 15:56

4 Answers 4

44
$\begingroup$

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

$\endgroup$
4
  • $\begingroup$ Switching antialiasing off by wrapping graphics with Style[...,Antialiasing->False] does not remove the "judder" in this animation. So I think it is due to Mathematica's rendering method, not due to antialiasing scheme. $\endgroup$ Oct 20, 2011 at 7:57
  • 1
    $\begingroup$ @Alexey, you are correct. Perhaps I should have said it is caused by the antialiasing scheme that is NOT used. That is to say, I believe there is a scheme, without hinting or pixel-snapping, that would greatly reduce the judder, but make the image seem less sharp. Does this make sense? In the enlargement I included above, the blue rectangles should/could have fuzzy edges to approximate correctly placed squares, but they do not. $\endgroup$
    – Mr.Wizard
    Oct 20, 2011 at 15:27
  • 1
    $\begingroup$ Good explanation! I believe that Adobe Acrobat already has such a correct antialiasing scheme because I never faced similar problems with it rendering and exporting. $\endgroup$ Oct 20, 2011 at 17:48
  • $\begingroup$ @AlexeyPopkov Wow, you are right! Though the plot generated by Mathematica has aligning problem, but when right click and save it as pdf, the aligning automatic corrected. $\endgroup$
    – matheorem
    Jul 12, 2016 at 13:14
29
$\begingroup$

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...

$\endgroup$
2
  • 5
    $\begingroup$ That is a nice set of markers, although if I scale them down some of them still don't look great. I used {s, 0.04} for the markers. For example, the "down" pointing triangles in the second row seem too low, and the triangle at (14,2) looks a bit too far to the right. It looks much better if I wrap everything with Style[..., Antialiasing -> True. (By default your glyphs get antialiases, but the frame and gridlines aren't. Although I wouldn't expect this to be an issue for markers on the curve in a plot.) $\endgroup$ Oct 18, 2011 at 15:15
  • 1
    $\begingroup$ @Brett Magnifying the Notebook up to 300% reveals that all the triangles are correctly positioned. The inaccurate positioning at lower resolution obviously is an artifact of Mathematica's rendering method. But exporting to PDF gives well-looking plot. $\endgroup$ Oct 18, 2011 at 16:01
15
$\begingroup$
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

$\endgroup$
4
  • 2
    $\begingroup$ I don't think there is anything wrong with the marker placement function. Please see my answer. Rather, I think that the default markers, being font based, are prone to misalignment. $\endgroup$
    – Mr.Wizard
    Oct 18, 2011 at 1:12
  • $\begingroup$ @Mr. I believe the "correct" algorithm should take into account the barycenter of the plot markers, notwithstanding how they are defined. Surely the problem can be interpreted both ways, though. $\endgroup$ Oct 18, 2011 at 1:19
  • 1
    $\begingroup$ I am not sure I can agree with that. Glyphs are placed relative to a baseline; frankly I would find it disconcerting of the difference in baseline position between e.g. "^" and "," was eliminated. $\endgroup$
    – Mr.Wizard
    Oct 18, 2011 at 1:22
  • $\begingroup$ @Mr. May be, but really I can't imagine any use for an eccentric plot marker right now. $\endgroup$ Oct 18, 2011 at 1:28
8
$\begingroup$

Maybe you could do something with Epilog. For example, this

Plot[f[x], {x, -Pi - 1, Pi + 1}, 
 Epilog -> {Translate[
      Inset[Graphics[#1], {0, 0}, {0, 0}, Scaled[0.04]], #2] & @@@ 
       {{{Red, Disk[]}, stationaryPoints},
       {{Blue, Rectangle[{-1, -1}, {1, 1}]}, inflectionPoints}}},
 ImageSize -> 500]

produces this (on my Mac at least)

Mathematica graphics

If the markers are still off you could fine tune their placement by adjusting the second or third argument of Inset.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.