26
$\begingroup$

For various reasons related to workflow associated with creating figures for journals, I am creating functions that will accept graphics primitives and a set of options and spit out a Graphics object with those primitives and those options in some configuration.

(Of course, this sounds like all I'm doing is re-creating Graphics, in that I can call

Graphics[ <List of primitives>, <Sequence of options>]

but it's more complicated than that.)

In any case, I would like to be able to check whether the inputs to the function are graphics primitives or not. We can create our own GraphicsPrimitiveQ if we have a list of the Heads of graphics primitives. For instance,

Clear[GraphicsPrimitiveQ, listOfHeads]
listOfHeads = {Line, Polygon, Point, Arrow, Tube};
GraphicsPrimitiveQ[x_] := MatchQ[Head[x], Alternatives @@ listOfHeads]

in which case if we define

f[x__?GraphicsPrimitiveQ] := {x}

then

f[Point[{0, 0}], Line[{{1, 1}, {0, 0}}]]
(* {Point[{0, 0}], Line[{{1, 1}, {0, 0}}} *)

and

f[Point[{0, 0}], 1]
(* f[Point[{0, 0}], 1] *)
  • So: Is there something like a GraphicsPrimitiveQ?

  • Or: Is there hidden somewhere in Mathematica an easy way to get a list of all graphics primitives?

  • Alternatively, if there's no built-in ListOfGraphicsPrimitives[], then Is there a programmatic way of making the function GraphicsPrimitiveQ? (Added this because I for some reason didn't anticipate the clever answers below.)

$\endgroup$
5
  • 3
    $\begingroup$ Here's a parser I wrote for work. It has an almost complete list of primitives and directives. All that it is missing is the new stuff for 10.2. $\endgroup$
    – rcollyer
    Jul 15, 2015 at 21:15
  • $\begingroup$ @rcollyer. I saw this answer in my search before posting the question! I was hoping for something that could be called directly from Mathematica, but I can always deposit your primitives in a package if I need to. Thanks! $\endgroup$
    – march
    Jul 15, 2015 at 21:19
  • $\begingroup$ I think I have something that will work, programatically. $\endgroup$
    – rcollyer
    Jul 15, 2015 at 21:20
  • $\begingroup$ In the old days, I had to maintain a long list based on the list given in the docs. I like this question; maybe you should also ask about the possibility of a GraphicsDirectiveQ[]. :) $\endgroup$ Jul 16, 2015 at 0:32
  • $\begingroup$ @march correction, now the parser has a complete list of primitives. I forgot to add the v10 and v10.2 primitives even in my local implementation. $\endgroup$
    – rcollyer
    Jul 16, 2015 at 13:02

4 Answers 4

22
$\begingroup$

Edit: It was pointed out that the original form is not bullet-proof, e.g. GraphicsPrimitiveQ /@ {InputNotebook, Unique, Sequence} all returned True. However, when looking upon this answer about generating new graphics primitives, a superior answer came to light:

Clear[GraphicsPrimitiveQ];
GraphicsPrimitiveQ[s_Symbol | (s_)[___]] := 
 0 < Count[DownValues[Typeset`MakeBoxes], 
   Verbatim[HoldPattern][HoldPattern[Typeset`MakeBoxes[s[___], __, Graphics]]], -1]

which is now bullet-proof:

GraphicsPrimitiveQ /@ {Rectangle, Pyramid, Sequence}
(* {True, False, False} *)

I need to look deeper for one dealing with Graphics3D, though.


This is the old version. It works, but has issues.

This works for Graphics primitives:

Clear[GraphicsPrimitiveQ];
GraphicsPrimitiveQ[s_Symbol] := !MatchQ[ToBoxes@Graphics[s[]], GraphicsBox[_s]]


GraphicsPrimitiveQ /@ {Line, Bob, CapsuleShape (*3d primitive*)}
(* {True, False, False} *)

with the additional rule

GraphicsPrimitiveQ[e : (h_)[___]] := !MatchQ[ToBoxes@Graphics[e], GraphicsBox[_h]]

GraphicsPrimitiveQ[#[{{1,2},{3,4}}]]& /@ {Polygon, List}
(* {True, False} *)

This works because in the presence of Graphics, the primitives have a box form, e.g.

ToBoxes@Graphics@Line[{{1,2},{3,4}}]
(* GraphicsBox@LineBox[{{1,2},{3,4}}] *)

but non-primitives do not, even those that have box forms

ToBoxes@Subscript[x, 2]
(* SubscriptBox[x, 2] *)

ToBoxes@Graphics@Subscript[x, 2]
(* GraphicsBox[Subscript[x, 2]] *)

nor do primitives expand to their box form outside of Graphics, e.g.

ToBoxes@Line[]
(* RowBox[{"Line", "[", "]"}] *)

We can do the same with Graphics3D,

Clear[GraphicsPrimitiveQ];
Graphics3DPrimitiveQ[s_Symbol] := 
   !MatchQ[ToBoxes@Graphics3D[s[]], Graphics3DBox[_s]]
Graphics3DPrimitiveQ[e : (h_)[___]] := 
   !MatchQ[ToBoxes@Graphics3D[e], Graphics3DBox[_h]]

but it returns True for exclusively 2D primitives, e.g.

Graphics3DPrimitiveQ @ Rectangle
(* True *)

So, I do not know how to approach this, if you want something that will exclusively detect 3D primitives.

$\endgroup$
13
  • $\begingroup$ That's... mysterious. I'll try to parse that later. In the meantime, +1. (I'm out of votes for the day, but I'll upvote in a couple of hours.) I will need it to find 3D primitives, in general. Also, in order to match the primitive and not just the Head, could we do instead GraphicsPrimitiveQ[s_Symbol[__]]? $\endgroup$
    – march
    Jul 15, 2015 at 21:36
  • 1
    $\begingroup$ @Mr.Wizard well, it's a good thing I found a better way. I'll post it shortly, I hope. $\endgroup$
    – rcollyer
    Jul 16, 2015 at 2:33
  • 1
    $\begingroup$ Using your own list of primitives from (32719) reveals that GraphicsPrimitiveQ fails more often that it succeeds. In 10.1.0 failures are: {AASTriangle, ASATriangle, BezierCurve, BSplineCurve, BSplineSurface, Circle, Circumsphere, Cone, Cylinder, Disk, EmptyRegion, FullRegion, Hexahedron, Parallelepiped, Parallelogram, Polygon, Prism, Pyramid, Raster, Raster3D, SASTriangle, SSSTriangle, Tetrahedron, Tube} $\endgroup$
    – Mr.Wizard
    Jul 16, 2015 at 13:35
  • 1
    $\begingroup$ You'll notice the conspicuous lack of an answer from me. Happy fiddling! ;-) $\endgroup$
    – Mr.Wizard
    Jul 16, 2015 at 13:54
  • 1
    $\begingroup$ I decided to post the only simple answer I can think of. It's not a good match for what the OP requested but I think it has potentially useful behavior of its own. I expect you'll inform me of its failings. :-) $\endgroup$
    – Mr.Wizard
    Jul 16, 2015 at 14:24
11
$\begingroup$

With WolframLanguageData your list of graphics primitives will stay up to date.

ListOfGraphicsPrimitives[] = Symbol /@ 
 WolframLanguageData[
  EntityClass["WolframLanguageSymbol", {"FunctionalityArea","GraphicsPrimitiveFunctions"}], "Name"]

{AASTriangle, AffineHalfSpace, AffineSpace, Annulus, Arrow, ASATriangle, Ball, BezierCurve, BSplineCurve, CapsuleShape, Circle, Circumsphere, Cone, ConicHullRegion, Cuboid, Cylinder, Disk, DiskSegment, Ellipsoid, EmptyRegion, FilledCurve, FullRegion, GraphicsComplex, HalfLine, HalfPlane, HalfSpace, Hexahedron, Hyperplane, Insphere, JoinedCurve, Line, Parallelepiped, Parallelogram, Point, Polygon, Prism, Rectangle, RegularPolygon, SASTriangle, Simplex, Sphere, SphericalShell, SSSTriangle, StadiumShape, Tetrahedron, Triangle, Tube}

ListOfGraphicsFunctions[] = Symbol /@ 
 WolframLanguageData[
  EntityClass["WolframLanguageSymbol", {"FunctionalityArea","GraphicsFunctions"}], "Name"]

{AbsoluteDashing, AbsolutePointSize, AbsoluteThickness, Antialiasing, Arrowheads, AspectRatio, Axes, AxesEdge, AxesLabel, AxesOrigin, AxesStyle, Axis, Back, Background, BezierFunction, BoundaryDiscretizeGraphics, Boxed, BoxRatios, BoxStyle, BSplineFunction, BSplineSurface, CapForm, ClippingStyle, ClipPlanes, ClipPlanesStyle, ClipRange, ContentSelectable, ContourLabels, Contours, ContourShading, CoordinatesToolOptions, CurveClosed, Dashing[{Small, Small}], Dashing, DataRange, DataReversed, DateTicksFormat, DefaultAxesStyle, DefaultBaseStyle, DefaultBoxStyle, DefaultFaceGridsStyle, DefaultFrameStyle, DefaultFrameTicksStyle, Directive, DiscretizeGraphics, DisplayFunction, Dashing[{0, Small, Small, Small}], Dashing[{0, Small}], EdgeForm, Epilog, Exclusions, ExtentElementFunction, ExtentMarkers, ExtentSize, FaceGrids, Filling, FillingStyle, Frame, FrameLabel, FrameMargins, FrameTicks, Front, FullGraphics, Graphics, Graphics3D, GraphicsColumn, GraphicsGrid, GraphicsGroup, GraphicsRow, GridLines, Inset, ItemAspectRatio, Joined, Lighting, LightingAngle, Mesh, MeshFunctions, MeshShading, NCache, NormalsFunction, Offset, Opacity, OpacityFunction, OpacityFunctionScaling, PixelConstrained, PointSize, PolarAxes, PolarAxesOrigin, PolarGridLines, PolarTicks, Prolog, Raster, Raster3D, Rasterize, RasterSize, RegionFunction, RevolutionAxis, Rotate, RotateLabel, RotationAction, RoundingRadius, Scale, Scaled, Show, Specularity, SphericalRegion, SplineClosed, SplineDegree, SplineKnots, SplineWeights, StreamScale, Texture, Thickness[Large], Thickness, Thickness[Tiny], Ticks, Translate, GrayLevel[ 0, 0], VertexColors, VertexDataCoordinates, VertexNormals, VertexTextureCoordinates, ViewAngle, ViewCenter, ViewMatrix, ViewPoint, ViewRange, ViewVector, ViewVertical}

Hop this helps.

$\endgroup$
2
  • $\begingroup$ I tried to get this yesterday, but I think I will need a lot of time till I get intuition how to work entities :-/ $\endgroup$
    – Kuba
    Jan 26, 2016 at 7:13
  • $\begingroup$ On my Mac with v11.3, ListOfGraphicsPrimitives gives me different results than shown here and does not include the following primitives listed in the documentation for Graphics: Disk, InfiniteLine, Inset, GraphicsGroup, Line, Locator, Point, Polygon, Raster, SASTriangle, and Text. The bolded entries are also not in your results. $\endgroup$
    – Bob Hanlon
    Oct 15, 2018 at 0:51
10
$\begingroup$

The answer to the question depends upon what exactly should be called as "graphics primitive". In this answer from the practical point of view I define it as a container which can be found inside of Graphics or Graphics3D, which draws something and is not a graphical directive or Dynamic wrapper. This definition differs from the usual meaning but covers all the key cases. Here is (I hope) complete list of such containers for version 10.2:

{Point, PointBox, Line, LineBox, Arrow, ArrowBox, Rectangle, RectangleBox, Parallelogram, Triangle, JoinedCurve, JoinedCurveBox, FilledCurve, FilledCurveBox, StadiumShape, DiskSegment, Annulus, BezierCurve, BezierCurveBox, BSplineCurve, BSplineCurveBox, BSplineSurface, BSplineSurface3DBox, SphericalShell, CapsuleShape, Raster, RasterBox, Raster3D, Raster3DBox, Polygon, PolygonBox, RegularPolygon, Disk, DiskBox, Circle, CircleBox, Sphere, SphereBox, Ball, Ellipsoid, Cylinder, CylinderBox, Tetrahedron, TetrahedronBox, Cuboid, CuboidBox, Parallelepiped, Hexahedron, HexahedronBox, Prism, PrismBox, Pyramid, PyramidBox, Simplex, ConicHullRegion, ConicHullRegionBox, Hyperplane, HalfSpace, AffineHalfSpace, AffineSpace, ConicHullRegion3DBox, Cone, ConeBox, InfiniteLine, InfinitePlane, HalfLine, InfinitePlane, HalfPlane, Tube, TubeBox, GraphicsComplex, GraphicsComplexBox, GraphicsGroup, GraphicsGroupBox, GeoGraphics, Graphics, GraphicsBox, Graphics3D, Graphics3DBox, MeshRegion, BoundaryMeshRegion, GeometricTransformation, GeometricTransformationBox, Rotate, Translate, Scale, SurfaceGraphics, Text, TextBox, Inset, InsetBox, Inset3DBox, Panel, PanelBox, Legended, Placed, LineLegend, Texture}

$\endgroup$
7
$\begingroup$

Since there seems no other simple answer at this time I propose the approach of rendering a Graphics expression and seeing if it has errors. By definition this will pass both primitives and directives, as well as inert expressions such as {}. I hope it nevertheless serves some purpose.

I rasterize the graphic and look for the tell-tale pink warning color. Of course it would be easy to create a false negative simply by creating an element with this very color but that is a corner case.

graphicsQ = 
  FreeQ[Union @@ ImageData @ Image[Graphics[#], ImageSize -> 30], 
    x_ /; x == {1.`, 0.9019607843137255`, 0.9019607843137255`}] &;

Positive examples:

graphicsQ /@ {Thick, Green, Rectangle[{0, -1}, {2, 1}], Red, Disk[], Blue, Circle[{2, 0}],
   Yellow, Polygon[{{2, 0}, {4, 1}, {4, -1}}], Purple, Arrowheads[Large], 
  Arrow[{{4, 3/2}, {0, 3/2}, {0, 0}}], Black, Dashed, Line[{{-1, 0}, {4, 0}}]}

{True, True, True, True, True, True, True, True, True, True, True, True, True, True, True}

Some negative examples:

graphicsQ /@ {Circle["x"], Arrow[], Line[], Pi, 2.2}

{False, False, False, False, False}

Specifically I included malformed primitives to demonstrate that these are not passed. This may or may not be desirable depending on your application.

$\endgroup$
4
  • 1
    $\begingroup$ The pink color works, but I think the superior method is to adapt this answer, and yes this was my ultimate fall back method. $\endgroup$
    – rcollyer
    Jul 16, 2015 at 14:26
  • $\begingroup$ @rcollyer I'm sad to say I forgot about that answer! Would you like to post a second answer using that method and I shall delete mine? $\endgroup$
    – Mr.Wizard
    Jul 16, 2015 at 14:29
  • 1
    $\begingroup$ I'll might do that this afternoon. I might edit yours. Neither will happen until this afternoon. :P $\endgroup$
    – rcollyer
    Jul 16, 2015 at 14:34
  • 1
    $\begingroup$ I think the fact that it outputs False for malformed primitives (something that I didn't think of) is extremely useful, for a number of reasons, so this is a good addition. $\endgroup$
    – march
    Jul 16, 2015 at 16:40

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.