10
$\begingroup$

Is it possible to define new graphics directives?

For example, suppose I want to be able to use the following code:

Graphics[{ BigPointSize[0.07], SmallPointSize[0.04],
           Red, BigPoint[{1,1}], BigPoint[{1,3}], SmallPoint[{3,1}],
           Blue, SmallPoint[{2,2}], SmallPoint[{3,2}], BigPoint[{0,0}]
        }]

Is there any way to define BigPointSize, SmallPointSize, BigPoint, and SmallPoint so that this code will work as intended? Ideally BigPointSize and SmallPointSize should have all of the functionality of other graphics directives, e.g. scoping inside of lists, and the ability to call the command multiple times within the same list.

(Obviously it's possible to draw these points in other ways, but I'm curious whether it's possible to get this syntax to work.)

Edit: Just to clarify, I would like BigPointSize and SmallPointSize to work the same way as PointSize and other graphics directives. For example, the code

Graphics[{ BigPointSize[0.1],
           { BigPointSize[0.05], BigPoint[{0,0}] },
           BigPoint[{1,0}]
        }]

should produce one point of size 0.05 and one point of size 0.1.

$\endgroup$

3 Answers 3

5
$\begingroup$
Protect[BigPointSize, BigPoint, SmallPointSize, SmallPoint];

UserGraphics[gr_, opt : OptionsPattern[Graphics]] := Module[
  {
    BigPointSizeStack, BigPointSizeExec, BigPointExec,
    SmallPointSizeStack, SmallPointSizeExec, SmallPointExec,
    TempList, TempListExec,
  },

  BigPointSizeStack = {0.1};  (* default value *)
  BigPointSizeExec[s_] := (BigPointSizeStack[[-1]] = s; {});
  BigPointExec[p_] := {PointSize[BigPointSizeStack[[-1]]], Point[p]};

  SmallPointSizeStack = {0.1};(* default value *)
  SmallPointSizeExec[s_] := (SmallPointSizeStack[[-1]] = s; {});
  SmallPointExec[p_] := {PointSize[SmallPointSizeStack[[-1]]], Point[p]};

  TempListExec[x___] := Module[{retval},
    AppendTo[BigPointSizeStack, BigPointSizeStack[[-1]]];
    AppendTo[SmallPointSizeStack, SmallPointSizeStack[[-1]]];

    retval = {x} /. {
                      BigPointSize -> BigPointSizeExec,
                      SmallPointSize -> SmallPointSizeExec,
                      BigPoint -> BigPointExec,
                      SmallPoint -> SmallPointExec
                    };

   BigPointSizeStack = Delete[BigPointSizeStack, -1];
   BigPointSizeStack = Delete[SmallPointSizeStack, -1];
   retval
   ];

  Graphics[(gr //. x_List :> TempList @@ x) /. TempList :> TempListExec, opt]
]




gr = {Line[{{0, -1}, {6, 0}}], BigPoint[{1, 0}], BigPointSize[0.02], 
      BigPoint[{2, 0}], {BigPointSize[0.03], BigPoint[{3, 0}], Red, 
      BigPointSize[0.04], BigPoint[{4, 0}]}, BigPoint[{5, 0}]};



UserGraphics[gr, Frame -> True, PlotRange -> {{0, 6}, {-1, 1}}]

I don't belive, but I wrote it, and it works !!!

I tried to use obvious tricks and commands, which easily can be found in the documentation. The function UserGraphics supports all options and properties of Graphics. It also supports BigPointSize, SmallPointSize directives, and BigPoint, SmallPoint primitives.

It is the first version, so all found bugs/questions/comments/remarks are welcome !

$\endgroup$
4
  • $\begingroup$ This looks almost right, but the code under "Edit" in my original question (but with UserGraphics instead of graphics) is producing two points of the same size. $\endgroup$
    – Jim Belk
    Sep 22, 2013 at 1:46
  • $\begingroup$ No, they are not the same size. $\endgroup$ Sep 22, 2013 at 1:52
  • $\begingroup$ Let us check. Please show me the output of the command InputForm[UserGraphics[...]] $\endgroup$ Sep 22, 2013 at 1:57
  • 1
    $\begingroup$ Ok, I restarted the kernel and it's working for me now. It must have been because I evaluated your previous code snippet and didn't ClearAll the symbols. Thanks a lot for the answer! $\endgroup$
    – Jim Belk
    Sep 22, 2013 at 4:20
5
$\begingroup$

It is possible to use Style options as "graphics directives", and CurrentValue can be used to query the values of these options. For example, suppose we use AutoIndent as the graphics directive:

Graphics[
    {
    AutoIndent -> .1, {PointSize[Dynamic@CurrentValue@AutoIndent], Point[{0,0}]}, 
    {AutoIndent -> .2, {PointSize[Dynamic@CurrentValue@AutoIndent], Point[{1,1}]}},
    {PointSize[Dynamic@CurrentValue@AutoIndent], Point[{1,0}]}
    },
    ImageSize->200,
    PlotRange->{{-1,2},{-1,2}},
    Axes->True
]

enter image description here

Notice how the point at {1, 0} uses the initial AutoIndent value. It is also possible to use Typeset`MakeBoxes so that BigPointSize and BigPoint only evaluate when the Graphics expression is converted to boxes, as pointed out by Simon Woods in his answer to How to create custom [Graphics](http://reference.wolfram.com/language/ref/Graphics) primitive?. I will use a couple AutoStyleOptions settings as my "graphics directives":

Typeset`MakeBoxes[BigPointSize[rhs_], StandardForm, Graphics] := Typeset`Hold[
    "AutoStyleOptionsHighlightGlobalToLocalScopeConflicts"->rhs
]
Typeset`MakeBoxes[SmallPointSize[rhs_], StandardForm, Graphics] := Typeset`Hold[
    "AutoStyleOptionsHighlightMissingArgumentsWithTemplate"->rhs
]
Typeset`MakeBoxes[BigPoint[a_], StandardForm, Graphics] := {
    PointSize -> Dynamic @ Replace[
        CurrentValue["AutoStyleOptionsHighlightGlobalToLocalScopeConflicts"],
        Except[_?NumberQ]->.1
    ],
    PointBox[a]
}
Typeset`MakeBoxes[SmallPoint[a_], StandardForm, Graphics] := {
    PointSize -> Dynamic @ Replace[
        CurrentValue["AutoStyleOptionsHighlightMissingArgumentsWithTemplate"],
        Except[_?NumberQ]->.03
    ],
    PointBox[a]
}

And here's an example:

Graphics[
    {
    Line[{{0,-1},{6,0}}],
    BigPoint[{1,0}], Red, SmallPoint[{1,.5}],
    BigPointSize[0.02], SmallPointSize[.01],
    BigPoint[{2,0}], Blue, SmallPoint[{2, .5}],
    {
        BigPointSize[0.03], SmallPointSize[.04], BigPoint[{3,0}],
        Green, BigPointSize[0.04], BigPoint[{4,0}], SmallPoint[{4, .5}]
    },
    BigPoint[{5,0}], SmallPoint[{5, .5}]
    },
    Frame->True,
    PlotRange->{{0,6},{-1,1}}
]

enter image description here

$\endgroup$
2
$\begingroup$
BigPointSizeValue = 1;

BigPointSize[s_] := (BigPointSizeValue = s;)

BigPoint[p_] := {PointSize[BigPointSizeValue], Point[p]}

SmallPointSizeValue = 1;

SmallPointSize[s_]:=(SmallPointSizeValue = s;)

SmallPoint[p_] := {PointSize[SmallPointSizeValue], Point[p]}
$\endgroup$
6
  • $\begingroup$ Thanks for the answer. This works for the given code snippet, but it doesn't have the other properties of graphics directives, such as scoping within lists. $\endgroup$
    – Jim Belk
    Sep 21, 2013 at 20:54
  • 1
    $\begingroup$ No problem, just declare them like this: BigPoint[p_] := Sequence[PointSize[BigPointSizeValue], Point[p]] $\endgroup$ Sep 21, 2013 at 20:57
  • 1
    $\begingroup$ I think without a "preprocessor" function it is impossible to inform to a given BigPointSize[...] at what level of the list it is called. If there is no built-in method to add Directives, we should use Graphics with preprocessiong function in this form Graphics[proc[gr]]. $\endgroup$ Sep 21, 2013 at 21:23
  • 1
    $\begingroup$ Do you like the idea of preprocessor? If yes, I can write a draft version of the code. $\endgroup$ Sep 21, 2013 at 22:00
  • 1
    $\begingroup$ It sounds like a preprocessor would work well, especially if it is possible to redefine the Graphics command to include the preprocessing. If it's not too much trouble, I'd love to see some possible code. $\endgroup$
    – Jim Belk
    Sep 21, 2013 at 22:21

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.