Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

How to create custom Graphics primitive?

It should have the following properties, resembling properties of built-in geometric figures, like Circle etc:

  1. Has constant head, say RoundedRectangle. It should not evaluate to the list of lines, so as normal Rectangle.

  2. Can serve as a subject for GeometricTransformation

share|improve this question
2  
Rectangle is a function turning some parameters into a graphics object. Such a function for rounded rectangles can be found in this post: mathematica.stackexchange.com/questions/1882/… –  Pickett Jun 18 '13 at 8:19
    
Suzan, I don't understand what you are looking for in this question. Can you be a bit more explicit about what you are looking for? –  bill s Jun 18 '13 at 10:05
    
Are you asking how to define an object that Mathematica will treat as a graphic primitive such as Disk or Circle? –  m_goldberg Jun 18 '13 at 10:14
1  
You might want to look into the old packages Graphics`Arrow` or Graphics`Spline` to see how they implemented primitives that were once not built-in. –  J. M. Jun 18 '13 at 10:49
1  
It seems that you are under a common misconception. As far as the kernel is concerned, Graphics is inert, i.e. it has no DownValues. It is only in the formatting end of things that it is turned into an image. The same applies to the primitives, like Rectangle, but they are only formatted when found within a Graphics(3D) object. So, Rectangle remains a Rectangle and GeometricTransformation can be applied to it. –  rcollyer Jun 18 '13 at 12:31

2 Answers 2

Try this:

SetAttributes[createPrimitive, HoldAll]

createPrimitive[patt_, expr_] := 
 Typeset`MakeBoxes[p : patt, fmt_, Graphics] := 
  Typeset`MakeBoxes[Interpretation[expr, p], fmt, Graphics]

Example:

createPrimitive[face[x_: 0.1],
 {Circle[{0, 0}, 1], Circle[{-0.3, 0.5}, x],
  Circle[{0.3, 0.5}, x], Line[{{-0.4, -0.2}, {0.4, -0.2}}]}]

It works as expected in Graphics:

g = Graphics[face[]]

enter image description here

face has no DownValues so it remains as face in InputForm:

InputForm[g]
(*  Graphics[face[]]  *)

enter image description here

(*  Graphics[face[], ImageSize -> {63., Automatic}]  *)

It works with GeometricTransformation:

Graphics[GeometricTransformation[face[0.2], ShearingTransform[Pi/4, {1, 0}, {0, 1}]]]

enter image description here

A note about colours

A commenter asked "How would you rewrite this function to color the different components differently?" The answer is that colours can be used in the definition of the custom primitive just as in any other graphics expression, but note that the expression that goes into Typeset`MakeBoxes must be something that the Front End understands, e.g. RGBColor[1,0,0] rather than Red. If you want to use named colours like Red you will need to let the kernel evaluate the expression to convert them to RGBColor directives.

So for example you could:

Manually specify the colours as RGBColor directives:

createPrimitive[myprim[x_], {RGBColor[1, 0, 0], Circle[{0, 0}, x]}]

Use named colours and override the hold attribute with Evaluate:

createPrimitive[myprim[x_], Evaluate @ {Red, Circle[{0, 0}, x]}]

Or just remove the hold attribute completely:

ClearAttributes[createPrimitive, HoldAll];
createPrimitive[myprim[x_], {Red, Circle[{0, 0}, x]}]

In the last two cases you should guard against x already having a value, e.g. with Block or by using \[FormalX] instead of x

share|improve this answer
2  
+1, I like it, and now I require a lot more letters to fulfill the 15 letter requirement. –  rcollyer Jun 18 '13 at 15:58
1  
+1. What does Typeset`MakeBoxes do exactly? (lazy to start a spelunking battle) –  Rojo Dec 13 '13 at 18:56
1  
@Rojo, I don't understand the details, but it's used internally by MakeBoxes to convert graphics primitives to boxes. I don't think it's possible to give definitions for creating graphics boxes directly with MakeBoxes, you have to use Typeset`MakeBoxes instead. –  Simon Woods Dec 13 '13 at 19:17
    
That's nice, thanks –  Rojo Dec 13 '13 at 22:19

I don't see how to do what you are asking for without modifying the built-in definition of Graphics, something I would be extremely reluctant to do. In my own work, the closest I have come to meeting your requirements is to define functions that define shapes by returning lists consisting of graphics directives and primitives. This has worked well enough to satisfy me so far.

Here is an example.

dashedPoly[pts : {{_, _} ..}, 
           fill : (_RGBColor | _GrayLevel | _Hue) : Transparent] :=
    {fill, EdgeForm[Dashing[Small]], Polygon[pts]}

And here are two examples of the function in use.

Module[{A, B, C},
  A = {0., 0.}; B = {0., 3.}; C = {4., 0.};
  Graphics[dashedPoly[{A, B, C}]]]

triangle1.png

With[{blue = ColorData["HTML", "DeepSkyBlue"]}, 
  Module[{A, B, C},
    A = {0., 0.}; B = {0., 3.}; C = {4., 0.};
    Graphics[dashedPoly[{A, B, C}, blue]]]]

triangle2.png

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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