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$
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$
  • $\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 '13 at 1:46
  • $\begingroup$ No, they are not the same size. $\endgroup$ – Vahagn Poghosyan Sep 22 '13 at 1:52
  • $\begingroup$ Let us check. Please show me the output of the command InputForm[UserGraphics[...]] $\endgroup$ – Vahagn Poghosyan Sep 22 '13 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 '13 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$
  • $\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 '13 at 20:54
  • 1
    $\begingroup$ No problem, just declare them like this: BigPoint[p_] := Sequence[PointSize[BigPointSizeValue], Point[p]] $\endgroup$ – Vahagn Poghosyan Sep 21 '13 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$ – Vahagn Poghosyan Sep 21 '13 at 21:23
  • 1
    $\begingroup$ Do you like the idea of preprocessor? If yes, I can write a draft version of the code. $\endgroup$ – Vahagn Poghosyan Sep 21 '13 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 '13 at 22:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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