Function Repository Resource:

PolygonMarker

Source Notebook

Create markers carefully designed for the creation of publication-quality plots

Contributed by: Alexey Popkov

ResourceFunction["PolygonMarker"]["name"]

returns a unit area Polygon describing the shape "name".

ResourceFunction["PolygonMarker"][{p1,,pn}]

returns a unit area Polygon with a shape described by points {p1,,pn} and a centroid at {0,0}.

ResourceFunction["PolygonMarker"][shape, size]

returns shape of the specified size.

ResourceFunction["PolygonMarker"][shape,{size,angle}]

returns shape rotated by angle.

ResourceFunction["PolygonMarker"][shape, spec, style]

returns a Graphics object suitable for PlotMarkers where the style of shape is defined by style.

ResourceFunction["PolygonMarker"][All]

returns the list of names of predefined shapes.

Details and Options

The basic usage syntax is ResourceFunction["PolygonMarker"][shape,spec], where shape is the name of a predefined shape or a list of 2D coordinates describing a simple polygon, and spec can be either size or {size, angle}.
The size can be given as a number or in Scaled or Offset form.
The angle in radians determines the angle of the counterclockwise rotation of shape about its centroid.
ResourceFunction["PolygonMarker"][shape,spec] returns a Polygon graphics primitive that can be used in Graphics.
ResourceFunction["PolygonMarker"][shape,spec,style], where style is a list of graphics directives applied to shape, returns a Graphics object that can be used as a marker for PlotMarkers.
ResourceFunction["PolygonMarker"][shape,spec,style,options] returns a Graphics object with options applied.
The centroid of the Polygon generated by ResourceFunction["PolygonMarker"] is always placed at {0,0} in the internal coordinate system of Graphics.
ResourceFunction["PolygonMarker"][shape,spec,positions], where positions is a list of 2D coordinates and spec contains a numeric specification for size, returns a list of Polygon graphics primitives with centroids placed at positions.
ResourceFunction["PolygonMarker"][shape,spec,positions], where positions is a list of 2D coordinates and spec contains Scaled or Offset specifications for size, evaluates to Translate[ResourceFunction["PolygonMarker"][shape,spec],positions]. It represents a collection of multiple identical copies of shape with centroids placed at positions.
With an Offset size specification, the plot marker has a fixed size specified in printer's points, independent of ImageSize.
ResourceFunction["PolygonMarker"][shape,size] returns a Polygon with area size2 in the internal coordinate system of Graphics. ResourceFunction["PolygonMarker"][shape,Offset[size]] returns a Polygon with area size2 square printer's points. The area taken by the boundary of the returned Polygon is not taken into account.
ResourceFunction["PolygonMarker"][All] and ResourceFunction["PolygonMarker"][] both return the complete list of names of predefined shapes.

Examples

Basic Examples (6) 

Create a unit area Polygon object of "TripleCross" type rotated counterclockwise by π/6:

In[1]:=
ptc = ResourceFunction["PolygonMarker"]["TripleCross", {1, \[Pi]/6}]
Out[1]=

The centroid of the polygon is placed at {0,0}:

In[2]:=
centroid = RegionCentroid[ptc] // Chop
Out[2]=

Visualize the polygon and its centroid:

In[3]:=
Graphics[{LightBlue, ptc, Red, PointSize[.05], Point[centroid]}, Axes -> True]
Out[3]=

Create a displaced Graphics marker in one step:

In[4]:=
mtps = ResourceFunction["PolygonMarker"][
  "ThreePointedStar", {Offset[7], 0}, {EdgeForm[Red], FaceForm[None]}]
Out[4]=

This marker is set up to be suitable for PlotMarkers:

In[5]:=
ListPlot[
 Labeled[#, #, Background -> None] & /@ Table[Prime[n], {n, 10}], PlotMarkers -> {mtps}, PlotRange -> {{0, All}, {0, All}}]
Out[5]=

Get the complete list of built-in named shapes:

In[6]:=
allShapes = ResourceFunction["PolygonMarker"][All]
Out[6]=

Make a Grid of buttons with the shapes for easy selection:

In[7]:=
Grid[{allShapes[[;; 11]], allShapes[[12 ;; 17]], allShapes[[18 ;; 26]], allShapes[[27 ;; 32]], allShapes[[33 ;; 38]], allShapes[[39 ;;]]} /. shape_String :> Button[Tooltip[
     ResourceFunction["PolygonMarker"][shape, Offset[13], {FaceForm[RandomColor[ColorSpace -> LUVColor]], EdgeForm[{Black, AbsoluteThickness[72/96], JoinForm["Miter"]}]}], Row[{"\"", Style[shape, Bold], "\"", " (click to copy the name)"}]], CopyToClipboard@ToString[shape, InputForm], ImageSize -> {40, 40},
     FrameMargins -> False]]
Out[7]=

Filled markers that pick up PlotStyle and PlotTheme automatically:

In[8]:=
fm[name_String, size_ : 8] := ResourceFunction["PolygonMarker"][name, Offset[size], EdgeForm[]];
In[9]:=
SeedRandom[25];
In[10]:=
ListPlot[Table[Accumulate@RandomReal[1, 10] + i, {i, 6}], PlotMarkers -> fm /@ {"Triangle", "Y", "Diamond", "ThreePointedStar", "FivePointedStarSlim", "TripleCross"}, Joined -> True, PlotStyle -> ColorData[52, "ColorList"], PlotLegends -> PointLegend[Automatic, LegendMarkerSize -> {50, 37}, LegendLayout -> (Column[Row /@ #, Spacings -> -1] &)], ImageSize -> 450]
Out[10]=
In[11]:=
SeedRandom[2];
In[12]:=
ListPlot[Table[Accumulate@RandomReal[1, 10] + i, {i, 6}], PlotMarkers -> fm /@ {"Triangle", "LeftTriangle", "Diamond", "ThreePointedStar", "UpTriangleTruncated", "Square"}, PlotTheme -> "Marketing", PlotLegends -> PointLegend[Automatic, LegendMarkerSize -> {50, 35}, LegendLayout -> (Column[Row /@ #, Spacings -> -1] &)], ImageSize -> 450]
Out[12]=

Empty markers that pick up PlotStyle and PlotTheme automatically:

In[13]:=
em[name_String, size_ : 7] := ResourceFunction["PolygonMarker"][name, Offset[size],
   {Dynamic@
     EdgeForm[{CurrentValue["Color"], JoinForm["Round"], AbsoluteThickness[2], Opacity[1]}], FaceForm[White]},
   ImagePadding -> 6];
In[14]:=
SeedRandom[2];
In[15]:=
ListPlot[Table[Accumulate@RandomReal[1, 10] + i, {i, 3}], PlotMarkers -> em /@ {"Triangle", "Square", "Diamond"}, Joined -> True, PlotLegends -> PointLegend[Automatic, LegendMarkerSize -> {40, 25}], ImageSize -> 450]
Out[15]=
In[16]:=
SeedRandom[3];
In[17]:=
ListPlot[Table[Accumulate@RandomReal[1, 10] + i, {i, 3}], PlotMarkers -> em /@ {"Triangle", "Square", "Diamond"}, Joined -> True, PlotLegends -> PointLegend[Automatic, LegendMarkerSize -> {40, 25}], PlotTheme -> "Marketing", ImageSize -> 450]
Out[17]=

Filled markers with lighter filling colors:

In[18]:=
fm2[name_String, size_ : 9] := ResourceFunction["PolygonMarker"][name, Offset@size, {
    Dynamic@EdgeForm[{CurrentValue["Color"], Opacity[1]}],
    Dynamic@FaceForm@Lighter[CurrentValue["Color"], 0.75]}];

data = Table[{x, BesselJ[k, x]}, {k, 0, 2}, {x, 0, 10, 0.5}];

ListPlot[data, PlotMarkers -> fm2 /@ {"UpTriangle", "Square", "Circle"}, Joined -> True, Frame -> True, Axes -> False, ImageSize -> 450, PlotRangePadding -> {Scaled[.05], Scaled[.1]}]
Out[18]=

Scope (4) 

Use the third argument of PolygonMarker to specify the coordinate(s) where the marker should be placed:

In[19]:=
(* Evaluate this cell to get the example input *) CloudGet["https://www.wolframcloud.com/obj/a81f5ac7-3ed7-4e03-a891-36bde162e3ab"]
Out[19]=

Construct a list plot directly from graphics primitives:

In[20]:=
data = Table[{x, BesselJ[k, x]}, {k, 0, 3}, {x, 0, 10, 0.5}];
markers = {"Circle", "ThreePointedStar", "FourPointedStar", "FivePointedStar"};
colors = {Blue, Red, Darker@Green, Darker@Yellow};
Graphics[
 Table[{colors[[i]], Line[data[[i]]], FaceForm[White], EdgeForm[{colors[[i]], AbsoluteThickness[1], JoinForm["Miter"]}], ResourceFunction["PolygonMarker"][markers[[i]], Offset[7], data[[i]]]}, {i, Length[data]}], AspectRatio -> 1/2, ImageSize -> 450, Frame -> True]
Out[20]=

Construct a custom list plot where open plot markers have transparent faces for each other (but not for the plotted lines):

In[21]:=
data = Table[{x, BesselJ[k, x]}, {k, 0, 4}, {x, 0, 10, 0.5}];
markers = {"Circle", "ThreePointedStar", "FourPointedStar", "DiagonalFourPointedStar", "FivePointedStar"};
colors = {Blue, Red, Green, Yellow, Orange};
background = Darker@Gray;
Graphics[{Table[{colors[[i]], AbsoluteThickness[1.5], Line[data[[i]]],
     FaceForm[background], EdgeForm[None], ResourceFunction["PolygonMarker"][markers[[i]], Offset[7], data[[i]]]}, {i, Length[data]}], Table[{FaceForm[None], EdgeForm[{colors[[i]], AbsoluteThickness[1.5], JoinForm["Miter"]}], ResourceFunction["PolygonMarker"][markers[[i]], Offset[7], data[[i]]]}, {i, Length[data]}]}, AspectRatio -> 1/2, ImageSize -> 500, Frame -> True, Background -> background, FrameStyle -> White, ImagePadding -> {{25, 5}, {15, 5}}]
Out[21]=

Construct a custom list plot where the plot lines do not go through the plot markers (the corresponding portions of the lines are explicitly removed). Other objects like grid lines and background are visible through the markers.

First, define auxiliary functions:

In[22]:=
(* Evaluate this cell to get the example input *) CloudGet["https://www.wolframcloud.com/obj/40e981b4-d03f-4df5-9c83-f67da246af67"]

Input:

In[23]:=
(* The data set to plot *)
data = Table[{x, BesselJ[k, x]}, {k, 0, 4}, {x, 0, 10, 0.5}];
(* Labels for the legend *)
labels = Table[Style[Subscript[J, n][z], 15], {n, 0, 4}];
(* Aspect ratio of the whole plot area (doesn't include ImagePadding) *)
aspectRatio = 1/2;
(* Plot markers *)
markers = {"Circle", "ThreePointedStar", "FourPointedStar", "DiagonalFourPointedStar", "FivePointedStar"};
(* Colors for the lines&markers *)
colors = {LightBlue, Red, Green, Yellow, Orange};
(* Backgound image *)
background = Polygon[ImageScaled /@ {{0, 0}, {1, 0}, {1, 1}, {0, 1}}, VertexColors -> RGBColor /@ {{0, 0, 1, .9}, {0, 1, 0, .9}, {1, 0, 0, .9}, {1, 1, 0, .9}}];

Plotting:

In[24]:=
(* Determine the range of the input data *)
dataRange = MinMax /@ Transpose[Flatten[data, 1]];
(* Expand the plot area to fit all the graphics elements. This value must be specified as the value for PlotRange *)
fullPlotRange = padPlotRange[.04, aspectRatio]@dataRange;
(* Define full specifications for the markers *)
markerSpecs = Table[{markers[[i]], .02}, {i, Length[data]}];
(* Define styles for the lines and the markers *)
plotStyles = Table[{colors[[i]], AbsoluteThickness[1.5], CapForm[None], FaceForm[None], EdgeForm[{colors[[i]], AbsoluteThickness[1.5], JoinForm[{"Miter", 6}]}]}, {i, Length[data]}];
(* Generate graphics primitives representing the lines and the markers, along with the corresponding styling directives *)
plotPrimitives = Table[Join[plotStyles[[i]], createLineWithMarkers[markerSpecs[[i]], data[[i]], fullPlotRange, aspectRatio]], {i, Length[data]}];
(* Generate graphics primitives representing the legend *)
legendPrimitives = createLegend[markerSpecs, labels, {.045, .08, .06}, {.85, .29}, plotStyles, fullPlotRange, aspectRatio];
(* Construct a Graphics object from the set of graphics primitives *)
Graphics[{plotPrimitives, legendPrimitives}, PlotRange -> fullPlotRange, AspectRatio -> aspectRatio, ImageSize -> 500, Frame -> True, FrameStyle -> White, ImagePadding -> {{25, 5}, {15, 5}}, GridLines -> Automatic, GridLinesStyle -> Black, Prolog -> background]
Out[24]=

Neat Examples (5) 

Center markers that pick up PlotStyle and PlotTheme automatically:

In[25]:=
cfm[name_String, size_ : 9] := Show[
   ResourceFunction["PolygonMarker"][name, Offset@size, {
     FaceForm[White],
     Dynamic@
      EdgeForm[{CurrentValue["Color"], AbsoluteThickness[1], Opacity[1]}]}],
   ResourceFunction["PolygonMarker"][name, Offset[size/2], EdgeForm[None]]];

data = Table[{x, BesselJ[k, x]}, {k, 0, 2}, {x, 0, 10, 0.5}];

ListPlot[data, PlotMarkers -> cfm /@ {"UpTriangle", "Square", "Circle"}, Joined -> True, Frame -> True, Axes -> False, ImageSize -> 450, PlotRangePadding -> {Scaled[.05], Scaled[.1]}, PlotLegends -> PointLegend[Automatic, LegendMarkerSize -> {40, 30}], ImageSize -> 450]
Out[25]=

Half-filled markers that pick up PlotStyle and PlotTheme automatically:

In[26]:=
hfm1[name_String, size_ : 9] := Show[
   ResourceFunction["PolygonMarker"][name, Offset@size, {
     FaceForm[White],
     Dynamic@
      EdgeForm[{CurrentValue["Color"], AbsoluteThickness[1], Opacity[1]}]}],
   ResourceFunction["PolygonMarker"][name, Offset@size, EdgeForm[None]] /. {x_?Negative, y_?NumericQ} :> {0, y}];

data = Table[{x, BesselJ[k, x]}, {k, 0, 2}, {x, 0, 10, 0.5}];

ListPlot[data, PlotMarkers -> hfm1 /@ {"UpTriangle", "Square", "Circle"}, Joined -> True, Frame -> True, Axes -> False, ImageSize -> 450, PlotRangePadding -> {Scaled[.05], Scaled[.1]}, PlotLegends -> PointLegend[Automatic, LegendMarkerSize -> {40, 30}], ImageSize -> 450]
Out[26]=
In[27]:=
hfm2[name_String, size_ : 9] := Show[
   ResourceFunction["PolygonMarker"][name, Offset@size, {
     FaceForm[White],
     Dynamic@
      EdgeForm[{CurrentValue["Color"], AbsoluteThickness[1], Opacity[1]}]}],
   Graphics[{EdgeForm[None], Replace[RegionDifference[ResourceFunction["PolygonMarker"][name],
        Rectangle[{-10, -10}, {10, 0}]], p : {x_, y_} :> Offset[size p, {0, 0}], {-2}]}]];

data = Table[{x, BesselJ[k, x]}, {k, 0, 3}, {x, 0, 10, 0.5}];

ListPlot[data, PlotMarkers -> hfm2 /@ {"Diamond", "Square", "Circle", "RightTriangle"}, Joined -> True, Frame -> True, Axes -> False, ImageSize -> 450, PlotRangePadding -> {Scaled[.05], Scaled[.1]}, PlotLegends -> PointLegend[Automatic, LegendMarkerSize -> {40, 30}], ImageSize -> 450]
Out[27]=
In[28]:=
hfm3[name_String, size_ : 9] := Show[
   ResourceFunction["PolygonMarker"][name, Offset@size, {
     FaceForm[White],
     Dynamic@
      EdgeForm[{CurrentValue["Color"], AbsoluteThickness[1], Opacity[1]}]}],
   Graphics[{EdgeForm[None], Replace[RegionDifference[ResourceFunction["PolygonMarker"][name],
        Triangle[{{-10, -10}, {10, 10}, {10, -10}}]], p : {x_, y_} :> Offset[size p, {0, 0}], {-2}]}]];

data = Table[{x, BesselJ[k, x]}, {k, 0, 3}, {x, 0, 10, 0.5}];

ListPlot[data, PlotMarkers -> hfm3 /@ {"Diamond", "Square", "Circle", "DiagonalFourPointedStar"}, Joined -> True, Frame -> True, Axes -> False, ImageSize -> 450, PlotRangePadding -> {Scaled[.05], Scaled[.1]}, PlotLegends -> PointLegend[Automatic, LegendMarkerSize -> {40, 38}], ImageSize -> 450]
Out[28]=

Contrast markers that pick up PlotStyle and PlotTheme automatically:

In[29]:=
cfm1[name_String, size_ : 9] := Show[
   ResourceFunction["PolygonMarker"][name, Offset@size, {
     FaceForm[White],
     Dynamic@
      EdgeForm[{CurrentValue["Color"], AbsoluteThickness[1], Opacity[1]}]}],
   Graphics[{EdgeForm[None], Replace[RegionDifference[
       RegionDifference[ResourceFunction["PolygonMarker"][name], Rectangle[{0, 0}, {10, 10}]], Rectangle[{0, 0}, {-10, -10}]], p : {x_, y_} :> Offset[size p, {0, 0}], {-2}]}]];

data = Table[{x, BesselJ[k, x]}, {k, 0, 3}, {x, 0, 10, 0.5}];

ListPlot[data, PlotMarkers -> cfm1 /@ {"Diamond", "Square", "Circle", "FourPointedStar"}, Joined -> True, Frame -> True, Axes -> False, ImageSize -> 450, PlotRangePadding -> {Scaled[.05], Scaled[.1]}, PlotLegends -> PointLegend[Automatic, LegendMarkerSize -> {40, 38}], ImageSize -> 450]
Out[29]=
In[30]:=
cfm2[name_String, size_ : 9] := Show[
   ResourceFunction["PolygonMarker"][name, Offset@size, {
     FaceForm[White],
     Dynamic@
      EdgeForm[{CurrentValue["Color"], AbsoluteThickness[1], Opacity[1]}]}],
   Graphics[{EdgeForm[None], Replace[RegionDifference[
       RegionDifference[ResourceFunction["PolygonMarker"][name], Triangle[{{-10, 10}, {10, 10}, {0, 0}}]], Triangle[{{-10, -10}, {10, -10}, {0, 0}}]], p : {x_, y_} :> Offset[size p, {0, 0}], {-2}]}]];

data = Table[{x, BesselJ[k, x]}, {k, 0, 3}, {x, 0, 10, 0.5}];

ListPlot[data, PlotMarkers -> cfm2 /@ {"Diamond", "Square", "Circle", "DiagonalFourPointedStar"}, Joined -> True, Frame -> True, Axes -> False, ImageSize -> 450, PlotRangePadding -> {Scaled[.05], Scaled[.1]}, PlotLegends -> PointLegend[Automatic, LegendMarkerSize -> {40, 30}], ImageSize -> 450]
Out[30]=

Create an auxiliary function that (approximately) converts a simple glyph into a set of points suitable for PolygonMarker:

In[31]:=
pts[l_String] := First[Cases[
    ImportString[
     ExportString[Style[l, FontFamily -> "Verdana", FontSize -> 20], "PDF"], If[$VersionNumber >= 12.2, {"PDF", "PageGraphics"}, {"PDF", "Pages"}]], c_FilledCurve :> c[[2, 1]], Infinity]];

Show a set of markers in use, including some created from glyphs:

In[32]:=
(* Evaluate this cell to get the example input *) CloudGet["https://www.wolframcloud.com/obj/25744c2a-a2e8-40eb-b8d0-0a076b1ac3a2"]
Out[32]=

Black and white plot, where the markers overlap considerably:

In[33]:=
(* Evaluate this cell to get the example input *) CloudGet["https://www.wolframcloud.com/obj/9a8d224b-63d3-46fa-b42b-8710be629e14"]
Out[33]=

Use the resource functions RoundedPolygon and NotchedPolygon to make markers:

In[34]:=
(* Evaluate this cell to get the example input *) CloudGet["https://www.wolframcloud.com/obj/91ec3e45-de08-4473-93dd-e6f719eb76ad"]
Out[34]=

Publisher

AlexeyPopkov

Version History

  • 4.1.0 – 08 July 2022
  • 4.0.1 – 28 February 2022
  • 4.0.0 – 31 January 2022
  • 3.0.1 – 16 August 2021

Source Metadata

Related Resources

Author Notes

The original (author’s) version of the package which contains all the general-purpose functions used to generate the shapes can be found at https://github.com/AlexeyPopkov/PolygonPlotMarkers.

License Information