1
$\begingroup$

I would like to have a function that produces labeled arrows, where the labels can be given as more or less arbitrary expressions. I have started with some code that worked alright, up to the point where I need to modify the offset of my label based on the dimension of the rendered form of the expression. Here is what I have so far:

labeledArrow[vec : {{_, _}, {_, _}}, 
  ardir_List: {Arrowheads[{0.03}]}, txt_, 
  txtdir_List: {18, "Times"}] := 
  Graphics[{Join[ardir, {Arrow[vec]}],
    Text[Style[txt, txtdir], 
      Offset[-txtdir[[1]]/Norm[vec[[2]] - vec[[1]]] 
        {-(vec[[2, 2]] - vec[[1, 2]]), (vec[[2, 1]] - vec[[1, 1]])}
        - txtdir[[1]]/Norm[vec[[2]] - vec[[1]]] 
        {(vec[[2, 1]] - vec[[1, 1]]), (vec[[2, 2]] - vec[[1, 2]])}
        + {Rasterize[Text[txt], "RasterSize"][[1]]/1.6, 0}, 
      vec[[2]]]]}]

You can see that the intention is to put the label close to the arrowhead (at an offset that's roughly one character height in a direction that's convenient), and this works fine up until the point where I need to take into account the length of the rendered expression. Ideally, I would like the x-position of the first character of my label to be fixed relative to the arrowhead.

I stopped when I found that I had to introduce a strange factor (that 1/1.6 up there) in order for my Rasterize trick to kind of work. Since the Offset command is supposed to place the text relative to the center of the rendered text, this factor should be 1/2, but if I choose that, the label's start will move if the expression becomes longer. I could fiddle around with this to try and get the factor just right, but clearly I'm doing something wrong here, and things are getting ugly.

Can anyone help me fix this mess?

$\endgroup$
  • 2
    $\begingroup$ "You can see that the intention" I cant see that, because this code doesn't show anything when run. $\endgroup$ – Feyre Aug 2 '16 at 21:11
1
$\begingroup$

I fixed the problem. The code below does what I want it to do, but it's admittedly not pretty. I'm open to any suggestions to do this more elegantly... ;-)

labeledArrow[vec: {{_, _}, {_, _}}, 
  ardir_List: {Arrowheads[{0.03}]}, txt_, 
  txtdir_List: {18, "Times"}] := 
Graphics[{Join[ardir, {Arrow[vec]}],
    Text[Style[txt, txtdir], Offset[-txtdir[[1]]/
        Norm[vec[[2]] - vec[[1]]] {-(vec[[2, 2]] - vec[[1, 2]]), 
        (vec[[2, 1]] - vec[[1, 1]])}
      - txtdir[[1]]/Norm[vec[[2]] - vec[[1]]] 
        {(vec[[2, 1]] - vec[[1, 1]]), (vec[[2, 2]] - vec[[1, 2]])}
      + Sign[vec[[2, 2]] - vec[[1, 2]]] 
      {ImageDimensions[ImageCrop[Graphics[Text[Style[txt, txtdir]]]]][[1]]/2, 0},vec[[2]]]]}]
$\endgroup$
0
$\begingroup$

Have you considered using the third argument to Arrowheads? It can be an arbitrary graphic, so let's build it with a label:

labeledArrowheads[text_, size : _Real | Automatic : Automatic, 
   styleops___] :=
  Arrowheads[{
    {
     size,
     .99,
     Graphics[
      {
       Polygon[{
         {-.25, .5}, {1, 0},
         {-.25, -.5}, {0, 0}
         }],
       Text[Style[text, styleops], {.25, -.75}, {1, 0}]
       }
      ]
     }
    }
   ];

Then let's see how we did:

Graphics[{
  Circle[{0, 0}, 3],
  Hue[0, .5, .5],
  labeledArrowheads["Comic sans is the best font", "Comic Sans"],
  Arrow[{{0, 0}, First@CirclePoints[2.5, 5]}],
  Hue[.6, .5, .5],
  labeledArrowheads["Big words are fun", 15],
  Arrow[{{0, 0}, CirclePoints[2.5, 5][[2]]}],
  Hue[.4, .5, .5],
  labeledArrowheads["I'm purple", Purple, Italic],
  Arrow[{{0, 0}, CirclePoints[2.5, 5][[3]]}],
  Arrowheads[Automatic],
  Black,
  Arrow[{{0, 0}, CirclePoints[2.5, 5][[4]]}],
  Arrow[{{0, 0}, CirclePoints[2.5, 5][[5]]}],
  }]

arrow circle

Of course the labels follow the arrows which might be a bit of a pain.

$\endgroup$

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.