Custom arrow shaft

Update: added a version using Inset below the original answer


Here's an extended version of the arrow heads customization code. There are two pieces. One is the arrow drawing routine. The other one is an arrow editor, similar to my arrowheads editor but with more controls. There is a 'Copy to Clipboard' button to copy the drawArrow function with necessary parameter values filled in to generate the designed arrow.

Code is at the bottom of this answer.

Mathematica graphics

usage:

Graph[{1 -> 2, 2 -> 3, 3 -> 4, 4 -> 1, 4 -> 5, 5 -> 6, 
  6 -> 7, 7 -> 8, 8 -> 1},
 EdgeShapeFunction -> 
   ({drawArrow[{{-6.5`, 1}, {-4, 1/2}, {-6, 0}, {-2, 0.2`}, {-2, 0.5`}, {-2, 1}, {-2, 1.1`}, {-1, 1}, {0, 0}}, 
       #1[[1]], #1[[2]], 
       ArrowFillColor -> RGBColor[1, 1, 0], 
       ArrowFillOpacity -> 0.5`, 
       ArrowEdgeThickness -> 0.1`, 
       ArrowEdgeColor -> RGBColor[1, 0.5`, 0], 
       ArrowEdgeOpacity -> 1, 
       LeftArrowSpacing -> 0.2, 
       RightArrowSpacing -> 0.2]} &), 
 VertexShapeFunction -> None, EdgeStyle -> Automatic]

Mathematica graphics

The 2nd and 3rd argument are the start and end positions of the arrow, respectively. Replacing these with #1[[1]] and #1[[2]] and adding an & at the end, turns the drawArrow function into a function that can be used as EdgeShapeFunction in Graph

More examples:

Mathematica graphics

The code:

Options[drawArrow] = {ArrowFillColor -> Black, 
   ArrowEdgeThickness -> 0.02, ArrowEdgeColor -> Black, 
   ArrowFillOpacity -> 1, ArrowEdgeOpacity -> 1, 
   LeftArrowSpacing -> 0, RightArrowSpacing -> 0};

drawArrow[{shaftEndLeft_, shaftMidLeft_, shaftEndMid_, baseMidLeft_, 
   innerMidLeft_, innerBaseLeft_, outerBaseLeft_, outerMidLeft_, 
   top_}, pstart_, pend_, OptionsPattern[]] :=
 Module[{baseMidRight, outerMidRight, innerMidRight, innerBaseRight, 
   outerBaseRight, shaftEndRight, shaftMidRight},

  shaftEndRight = {1, -1} shaftEndLeft;
  shaftMidRight = {1, -1} shaftMidLeft;
  baseMidRight = {1, -1} baseMidLeft;
  innerBaseRight = {1, -1} innerBaseLeft;
  outerBaseRight = {1, -1} outerBaseLeft;
  outerMidRight = {1, -1} outerMidLeft;
  innerMidRight = {1, -1} innerMidLeft;
  {
   If[OptionValue[ArrowEdgeColor] === None, EdgeForm[], 
    EdgeForm[
     Directive[Thickness[OptionValue[ArrowEdgeThickness]], 
      OptionValue[ArrowEdgeColor], 
      Opacity[OptionValue[ArrowEdgeOpacity]]]]],
   If[OptionValue[ArrowFillColor] === None, FaceForm[], 
    FaceForm[
     Directive[Opacity[OptionValue[ArrowFillOpacity]], 
      OptionValue[ArrowFillColor]]]],
   GeometricTransformation[
    FilledCurve[
     {
      Line[{shaftEndMid, shaftEndLeft}],
      BSplineCurve[{shaftEndLeft, shaftMidLeft, baseMidLeft}],
      BSplineCurve[{baseMidLeft, innerMidLeft, innerBaseLeft}],
      Line[{innerBaseLeft, outerBaseLeft}],
      BSplineCurve[{outerBaseLeft, outerMidLeft, top}],
      BSplineCurve[{top, outerMidRight, outerBaseRight}],
      Line[{outerBaseRight, innerBaseRight}],
      BSplineCurve[{innerBaseRight, innerMidRight, baseMidRight}],
      BSplineCurve[{baseMidRight, shaftMidRight, shaftEndRight}],
      Line[{shaftEndRight, shaftEndMid}]
      }
     ], FindGeometricTransform[{pstart, 
       pend}, {shaftEndMid + {-OptionValue[
            LeftArrowSpacing] EuclideanDistance[shaftEndMid, top], 0},
        top + {OptionValue[RightArrowSpacing] EuclideanDistance[
           shaftEndMid, top], 0}}][[2]]
    ]
   }
  ]

DynamicModule[{top, fill, edge, arrowFillColor, arrowEdgeColor, 
  arrowFillOpacity, arrowEdgeThickness, arrowEdgeOpacity},
 Manipulate[
  top = {0, 0};
  shaftEndMid = {1, 0} shaftEndMid;
  Graphics[
   h = drawArrow2[{shaftEndLeft, shaftMidLeft, shaftEndMid, 
      baseMidLeft, innerMidLeft, innerBaseLeft, outerBaseLeft, 
      outerMidLeft, top}, shaftEndMid, top,
     ArrowFillColor -> If[fill, arrowFillColor, None], 
     ArrowFillOpacity -> arrowFillOpacity,
     ArrowEdgeThickness -> arrowEdgeThickness, 
     ArrowEdgeColor -> If[edge, arrowEdgeColor, None], 
     ArrowEdgeOpacity -> arrowEdgeOpacity
     ];
   h /. {drawArrow2 -> drawArrow},
   PlotRange -> {{-7, 2}, {-2, 2}},
   GridLines -> {Range[-7, 2, 1/4], Range[-2, 2, 1/4]},
   GridLinesStyle -> Dotted,
   ImageSize -> 800,
   AspectRatio -> Automatic
   ],
  {{shaftEndLeft, {-6.5, 1}}, Locator},
  {{shaftMidLeft, {-4, 1/2}}, Locator},
  {{shaftEndMid, {-6, 0}}, Locator},
  {{baseMidLeft, {-2, 0.2}}, Locator},
  {{innerMidLeft, {-2, 0.5}}, Locator},
  {{innerBaseLeft, {-2, 1}}, Locator},
  {{outerBaseLeft, {-2, 1.1}}, Locator},
  {{outerMidLeft, {-1, 1}}, Locator},
  Grid[
   {
     {Style["Fill", Bold, 16], 
      Control@{{fill, True, "Fill"}, {True, False}}, "  ", 
      Control@{{arrowFillColor, Yellow, "Color"}, Yellow}, "  ", 
      Control@{{arrowFillOpacity, 0.5, "Opacity"}, 0, 1}, "", ""},
     {Style["Edge", Bold, 16], 
      Control@{{edge, True, "Edge"}, {True, False}}, "  ", 
      Control@{{arrowEdgeColor, Orange, "Color"}, Orange}, "  ", 
      Control@{{arrowEdgeThickness, 0.02, "Thickness"}, 0, 0.1}, "  ",
       Control@{{arrowEdgeOpacity, 1, "Opacity"}, 0, 1}}
     }\[Transpose]
   , Alignment -> Left,
   Dividers -> {{True, True, {False}, True}, {True, True, {False}, 
      True}}
   ],
  Button["Copy to clipboard",
   CopyToClipboard[
    h /. {drawArrow2 -> Defer[drawArrow]}
    ],
   ImageSize -> Automatic
   ]
  ]
 ]

UPDATE


I was not satisfied with the behavior of the line thickness in the arrow definition. The problem was discussed in this question. I implemented the Inset idea of Mr.Wizard and also improved the clipboard copying, based on Simon's idea, but got rid of his Sequence that ended up as junk in the copied code. At the bottom the new code. A result is shown here:

Show[
 Graph[GraphData["DodecahedralGraph", "EdgeRules"], 
  VertexShape -> Graphics@{Red, Disk[]}, 
  EdgeShapeFunction -> 
   Function[{p$, v$}, 
    drawArrow @@ {{{-6.2059999999999995`, 0.3650000000000002`}, {-4.052`, 1.045`}, {-6.156`, 0.`}, {-1.5380000000000003`, 0.2549999999999999`}, {-0.9879999999999995`,  0.46499999999999986`}, {-2, 1}, {-1.428`, 1.435`}, {-1, 1}, {0, 0}}, 
      p$[[1]], p$[[2]], 
      {ArrowFillColor -> RGBColor[0.`, 0.61538109407187`, 0.1625391012436103`], 
       ArrowFillOpacity -> 0.462`, 
       ArrowEdgeThickness -> 0.0616`, 
       ArrowEdgeColor -> RGBColor[0.06968795300221256`, 0.30768291752498667`, 0.`], 
       ArrowEdgeOpacity -> 1}}], 
  VertexCoordinates -> 
    MapIndexed[First[#2] -> #1 &, GraphData["DodecahedralGraph", "VertexCoordinates"]]],
 Method -> {"ShrinkWrap" -> True}
 ]

(Note the "ShrinkWrap". Using Inset apparently generates a lot of white space that has to be cropped)

Mathematica graphics

The code:

Options[drawArrow] = {ArrowFillColor -> Black, 
   ArrowEdgeThickness -> 0.02, ArrowEdgeColor -> Black, 
   ArrowFillOpacity -> 1, ArrowEdgeOpacity -> 1, 
   LeftArrowSpacing -> 0, RightArrowSpacing -> 0};

drawArrow[{shaftEndLeft_, shaftMidLeft_, shaftEndMid_, baseMidLeft_, 
   innerMidLeft_, innerBaseLeft_, outerBaseLeft_, outerMidLeft_, 
   top_}, pstart_, pend_, OptionsPattern[]] :=
 Module[{baseMidRight, outerMidRight, innerMidRight, innerBaseRight, 
   outerBaseRight, shaftEndRight, shaftMidRight},

  shaftEndRight = {1, -1} shaftEndLeft;
  shaftMidRight = {1, -1} shaftMidLeft;
  baseMidRight = {1, -1} baseMidLeft;
  innerBaseRight = {1, -1} innerBaseLeft;
  outerBaseRight = {1, -1} outerBaseLeft;
  outerMidRight = {1, -1} outerMidLeft;
  innerMidRight = {1, -1} innerMidLeft;
  Inset[
   Graphics[
    {
     If[OptionValue[ArrowEdgeColor] === None, EdgeForm[], 
      EdgeForm[
       Directive[Thickness[OptionValue[ArrowEdgeThickness]], 
        OptionValue[ArrowEdgeColor], 
        Opacity[OptionValue[ArrowEdgeOpacity]]]]],
     If[OptionValue[ArrowFillColor] === None, FaceForm[], 
      FaceForm[
       Directive[Opacity[OptionValue[ArrowFillOpacity]], 
        OptionValue[ArrowFillColor]]]],
     FilledCurve[
      {
       Line[{shaftEndMid, shaftEndLeft}],
       BSplineCurve[{shaftEndLeft, shaftMidLeft, baseMidLeft}],
       BSplineCurve[{baseMidLeft, innerMidLeft, innerBaseLeft}],
       Line[{innerBaseLeft, outerBaseLeft}],
       BSplineCurve[{outerBaseLeft, outerMidLeft, top}],
       BSplineCurve[{top, outerMidRight, outerBaseRight}],
       Line[{outerBaseRight, innerBaseRight}],
       BSplineCurve[{innerBaseRight, innerMidRight, baseMidRight}],
       BSplineCurve[{baseMidRight, shaftMidRight, shaftEndRight}],
       Line[{shaftEndRight, shaftEndMid}]
       }
      ]
     },
    PlotRangePadding -> 0,
    PlotRange -> {{-7, 1}, {-2, 2}}
    ],
   pstart, {-7, 0}, EuclideanDistance[pstart, pend], pend - pstart
   ]
  ]

DynamicModule[{top, fill, edge, arrowFillColor, arrowEdgeColor, 
  arrowFillOpacity, arrowEdgeThickness, arrowEdgeOpacity},
 Manipulate[
  top = {0, 0};
  shaftEndMid = {1, 0} shaftEndMid;
  Graphics[
   drawArrow[{shaftEndLeft, shaftMidLeft, shaftEndMid, baseMidLeft, 
     innerMidLeft, innerBaseLeft, outerBaseLeft, outerMidLeft, 
     top}, {-7, 0}, {1, 0},
    ArrowFillColor -> If[fill, arrowFillColor, None], 
    ArrowFillOpacity -> arrowFillOpacity,
    ArrowEdgeThickness -> arrowEdgeThickness, 
    ArrowEdgeColor -> If[edge, arrowEdgeColor, None], 
    ArrowEdgeOpacity -> arrowEdgeOpacity
    ],
   PlotRange -> {{-7, 1}, {-2, 2}},
   GridLines -> {Range[-7, 1, 1/4], Range[-2, 2, 1/4]},
   GridLinesStyle -> Dotted,
   ImageSize -> 800,
   AspectRatio -> Automatic
   ],
  {{shaftEndLeft, {-6.5, 1}}, Locator},
  {{shaftMidLeft, {-4, 1/2}}, Locator},
  {{shaftEndMid, {-6, 0}}, Locator},
  {{baseMidLeft, {-2, 0.2}}, Locator},
  {{innerMidLeft, {-2, 0.5}}, Locator},
  {{innerBaseLeft, {-2, 1}}, Locator},
  {{outerBaseLeft, {-2, 1.1}}, Locator},
  {{outerMidLeft, {-1, 1}}, Locator},
  Grid[
   {
     {Style["Fill", Bold, 16], 
      Control@{{fill, True, "Fill"}, {True, False}}, "  ", 
      Control@{{arrowFillColor, Yellow, "Color"}, Yellow}, "  ", 
      Control@{{arrowFillOpacity, 0.5, "Opacity"}, 0, 1}, "", ""},
     {Style["Edge", Bold, 16], 
      Control@{{edge, True, "Edge"}, {True, False}}, "  ", 
      Control@{{arrowEdgeColor, Orange, "Color"}, Orange}, "  ", 
      Control@{{arrowEdgeThickness, 0.02, "Thickness"}, 0, 0.1}, "  ",
       Control@{{arrowEdgeOpacity, 1, "Opacity"}, 0, 1}}
     }\[Transpose]
   , Alignment -> Left,
   Dividers -> {{True, True, {False}, True}, {True, True, {False}, 
      True}}
   ],
  Button["Copy to clipboard",
   With[
    {
     params = {shaftEndLeft, shaftMidLeft, shaftEndMid, baseMidLeft, 
       innerMidLeft, innerBaseLeft, outerBaseLeft, outerMidLeft, top},
     opts = {ArrowFillColor -> If[fill, arrowFillColor, None], 
       ArrowFillOpacity -> arrowFillOpacity, 
       ArrowEdgeThickness -> arrowEdgeThickness, 
       ArrowEdgeColor -> If[edge, arrowEdgeColor, None], 
       ArrowEdgeOpacity -> arrowEdgeOpacity}
     },
    CopyToClipboard[
     Defer[EdgeShapeFunction -> 
       Function[{p, 
         v}, (drawArrow @@ {params, p[[1]], p[[2]], opts})]]]
    ],
   ImageSize -> Automatic
   ]
  ], SaveDefinitions -> True
 ]

Any chance to come up with a solution that allows to have Graphs like the following with automatically drawn/scaled arrow?

Yes! Even without implementing your own Arrow function. The trick is simple, start with taking your image and extract and binarize one version of your arrow. After this I crop the image to be of squared size and apply a very light Gaussian filter whose purpose is explained later:

img = Import["http://i.stack.imgur.com/cIYRI.jpg"];
arrowImg = ImageCrop[Binarize@ImageTake[img, {1, 400}, {-300, -1}]]
With[{d = Max[ImageDimensions[arrowImg]]},
 arrowImg = 
  GaussianFilter[ImageCrop[arrowImg, {d, d} + 10, Padding -> White], 2]
 ]

Mathematica graphics

Now the magic happens. Interpolate the image matrix to get a function $f(x,y)$ which is 0 inside the arrow and 1 everywhere else. Then we have this fine function called RegionPlot in Mathematica.

func = ListInterpolation[
  ImageData[arrowImg, "Real", DataReversed -> True], {{0, 1}, {0, 1}}];
arrowGraphics = 
 RegionPlot[func[y, x] < 1/2, {x, 0, 1}, {y, 0, 1}, 
  PlotStyle -> Green, BoundaryStyle -> Directive[Thick, Black], 
  Frame -> False, AspectRatio -> Automatic, PlotPoints -> 40, 
  MaxRecursion -> 3]

Mathematica graphics

Beside that we have a green arrow again the true magic happened behind the scenes. RegionPlot creates Polygon directives to show its result which can of course be scaled, translated, and rotated without losing quality.

With[{a = First[arrowGraphics]},
 Graphics[
  {a, GeometricTransformation[
    a, {RotationTransform[2/3 Pi], RotationTransform[4/3 Pi]}]}]]

Mathematica graphics


You can specify any graphics you like using EdgeRenderingFunction in GraphPlot. Here's a function to generate a customised arrow, with various parameters controlling the shape:

arowsplit[{a_, b_}] := ((1 - #) a + # b) & /@ Range[0, 1, 1/10]

arow[c_, base_, baseinset_, basewidth_, neckwidth_, neck_, headwidth_,
    sweep_, tip_][{{x1_, y1_}, {x2_, y2_}}] := 
 GeometricTransformation[
  GeometricTransformation[FilledCurve[BSplineCurve /@
     Apply[{-Sqrt[(c^-2) - 0.25], 
         0.5} + ((Abs[1/c]) + #1) Through[{Cos, Sin}[
           ArcTan[(#2 - 0.5)/Sqrt[(c^-2) - 0.25]]]] &, 
      arowsplit /@ 
       Partition[{{0, base + baseinset}, {-basewidth, 
          base}, {-neckwidth headwidth, 
          base + neck (tip - base)}, {-headwidth, (1 - sweep) (base + 
             neck (tip - base))}, {0, 
          tip}, {headwidth, (1 - sweep) (base + 
             neck (tip - base))}, {neckwidth headwidth, 
          base + neck (tip - base)}, {basewidth, base}, {0, 
          base + baseinset}}, 2, 1], {2}]], {{Sign[c], 0}, {0, 1}}], 
  TransformationFunction[{{-y1 + y2, -x1 + x2, 
     x1}, {x1 - x2, -y1 + y2, y1}, {0, 0, 1}}]]

To make it easier to use I've made a Manipulate which lets you vary the parameters and then paste an EdgeRenderingFunction directly into the GraphPlot expression.

Manipulate[
 Graphics[{Red, PointSize[0.1], Point[{{0, 0}, {0, 1}}],
   EdgeForm[{Thickness[ethick], ecol}], col,
   arow[c, base, bi, bw, nw, neck, hw, sw, tip][{{0, 0}, {0, 1}}]},
  PlotRange -> {Automatic, {-0.1, 1.1}}],
 {{c, 1.3, "Curvature"}, -1.9, 1.9},
 {{tip, 0.8, "Tip"}, 0.5, 1},
 {{neck, 0.6, "Neck"}, 0, 1},
 {{nw, 0.25, "Neck width"}, 0, 1},
 {{hw, 0.08, "Head width"}, 0, 0.5},
 {{sw, 0.05, "Sweep"}, 0, 1},
 {{base, 0.2, "Base"}, 0, 0.5},
 {{bw, 0.07, "Base width"}, 0, 0.5},
 {{bi, 0.05, "Base inset"}, 0, 0.5}, Delimiter,
 {{ecol, Blue, "Edge colour"}, Blue},
 {{ethick, 0.005, "Edge thickness"}, 0, 0.1},
 {{col, Yellow, "Colour"}, Yellow}, Delimiter,
 Button["Paste",
  With[{params = Sequence[c, base, bi, bw, nw, neck, hw, sw, tip],
    ecol = ecol, ethick = ethick, col = col},
   Paste[Defer[EdgeRenderingFunction ->
      Function[
       p, {EdgeForm[{Thickness[ethick], ecol}], col, 
        arow[params][p]}]]]]],
 FrameLabel -> {"", "", "Edge Rendering Function Arrow\n"}]

enter image description here

The red dots show the position of the graph vertices relative to the arrow. I won't try to describe what all the parameters do, it's easier just to play with the Manipulate. Once you have an arrow you are happy with just position the cursor inside the GraphPlot command and click the paste button.

In the example below I've also used a custom VertexRenderingFunction:

GraphPlot[{1 -> 2, 2 -> 3, 3 -> 1}, 
 VertexRenderingFunction -> ({Green, EdgeForm[{Thick, Black}], 
     Disk[#, .1], Black, Style[Text[#2, #1], 20]} &), 
 EdgeRenderingFunction -> 
  Function[p$, {EdgeForm[{Thickness[0.005`], RGBColor[0, 0, 1]}], 
    RGBColor[1, 1, 0], 
    arow[Sequence[1.3`, 0.2`, 0.05`, 0.07`, 0.25`, 0.6`, 0.08`, 0.05`,
        0.8`]][p$]}]]

enter image description here