I'm updating my answer since there was a bug in the code when combining dashed lines with MidArrow. This new module has corrected this. I've yet to find any bugs. I more or less adopted verbatim the way asymptote does arrows, but changed a few lines to incorporate sharper arrows and stealth styled arrow heads. Here's the module (named _custom_arrows.asy).
// This module contains functions for mimicing the tikz style of arrow heads.
// In particular, the sharpness of detail (arrows come to a point), and provides
// code for the stealth arrowhead which basic asymptote lacks.
arrowhead StealthHead(real dir=arrowdir, real barb=arrowbarb)
{
arrowhead a;
a.head=new path(path g, position position=EndPoint, pen p=currentpen,
real size=0, real angle=arrowangle)
{
if(size == 0) size=a.size(p);
angle=min(angle*arrowhookfactor, 45);
bool relative=position.relative;
real position=position.position.x;
if(relative) position=reltime(g, position);
path r=subpath(g, position, 0);
pair x=point(r, 0);
real t=arctime(r, size);
pair y=point(r,t);
path base=arrowbase(r,y,t,size);
path left=rotate(-angle,x)*r;
path right=rotate(angle,x)*r;
real[] T=arrowbasepoints(base,left,right,1);
pair denom=point(right,T[1])-y;
real factor=denom != 0 ? length((point(left,T[0])-y)/denom) : 1;
path left=rotate(-angle*factor,x)*r;
path right=rotate(angle*factor,x)*r;
real[] T=arrowbasepoints(base,left,right,1);
left=subpath(left,0,T[0]);
right=subpath(right,T[1],0);
pair pl0=point(left,0), pl1=relpoint(left,1);
pair pr0=relpoint(right,0), pr1=relpoint(right,1);
pair M=(pl1+pr0)/2;
pair v=barb*unit(M-pl0);
pl1=pl1+v; pr0=pr0+v;
left=pl0{dir(-dir+degrees(M-pl0, false))}--pl1--M;
right=M--pr0--pr1{dir(dir+degrees(pr1-M, false))};
return left--right&cycle;
};
return a;
}
arrowhead StealthHead=StealthHead();
private real position(position currentpos, real size, path g, bool center)
{
// Set pos to the real value equivalent of position.
real pos = currentpos.position.x;
if(currentpos.relative) {
pos *= arclength(g);
if(center) pos += 0.5*size;
pos=arctime(g, pos);
}
else if (center)
pos=arctime(g, arclength(subpath(g, 0, pos))+0.5*size);
return pos;
}
private void drawsharparrow(frame f, arrowhead arhead=DefaultHead,
path g, pen p=currentpen, real size=0,
real angle=arrowangle, filltype fill=null,
position currentpos=EndPoint, bool forwards=true,
margin the_margin=NoMargin, bool center=false)
{
// Paths for a portion of the path g and the arrow head.
path head, r;
// Boolean used when drawing the path.
bool endpoint;
// Integer used for the length of the path.
int L;
// Used for the real value equivalent of the input currentpos.
real pos;
// If size was not set, return size of the current pen.
if(size == 0) size = arhead.size(p);
// If fill was not set, use the fill type of the current pen.
if(fill == null) fill = arhead.defaultfilltype(p);
// Make sure the size is a legal value.
size = min(arrowsizelimit*arclength(g), size);
// Convert the current position into a real number.
pos = position(currentpos, size, g, center);
// Adjust the path by the selected margin.
g = the_margin(g, p).g;
// Store the length of the new path as a variable.
L = length(g);
// If the path should be going backwards, adjust g and pos.
if(!forwards) {
g = reverse(g);
pos = L-pos;
}
// Get the subpath of g with respect to the position pos.
r = subpath(g, pos, 0);
// Again, make sure size is a legal value.
size = min(arrowsizelimit*arclength(r), size);
// Set information about the arrow head.
head = arhead.head(g, pos, p, size, angle);
endpoint = pos > L-sqrtEpsilon;
if(cyclic(head) && (fill == NoFill || endpoint)) {
if(pos > 0) draw(f, subpath(r, arctime(r, size), length(r)), p);
if(!endpoint) draw(f, subpath(g, pos, L), p);
}
else draw(f, g, p);
// Fill the arrow head, setting line width to 0.0 to make it "sharp".
fill.fill(f, head, p+linewidth(0.0)+solid);
}
private void drawsharparrow2(frame f, arrowhead arhead=DefaultHead, path g,
pen p=currentpen, real size=0,
real angle=arrowangle, filltype fill=null,
margin the_margin=NoMargin)
{
// Paths for a portion of the path g, the arrow head, and arrow tail.
path head, tail, r;
// Integer used for the length of the path.
int L;
// If size was not set, return size of the current pen.
if(size == 0) size = arhead.size(p);
// If fill was not set, use the fill type of the current pen.
if(fill == null) fill = arhead.defaultfilltype(p);
// Make sure the size is a legal value.
size = min(arrowsizelimit*arclength(g), size);
// Adjust the path by the selected margin.
g = the_margin(g, p).g;
// Store the length of the new path as a variable.
L = length(g);
// Set r to the reverse path of g.
r = reverse(g);
// Set information about the arrow heads.
head = arhead.head(g, L, p, size, angle);
tail = arhead.head(r, L, p, size, angle);
if(cyclic(head))
draw(f, subpath(r, arctime(r, size), L-arctime(g, size)), p);
else draw(f,g,p);
// Fill in the head and tail ends of the path with arrows.
fill.fill(f,head,p+linewidth(0.0)+solid);
fill.fill(f,tail,p+linewidth(0.0)+solid);
}
private picture sharparrow(arrowhead arhead=DefaultHead,
path g, pen p=currentpen, real size=0,
real angle=arrowangle, filltype fill=null,
position currentpos=EndPoint, bool forwards=true,
margin the_margin=NoMargin, bool center=false)
{
// Picture we're adding an arrow to.
picture pic;
// Real equivalent of currentpos.
real pos;
// Path used for reversing arrow if forwards=false is set.
path G;
// If size was not set, return size of the current pen.
if(size == 0) size = arhead.size(p);
// Add the arrow to pic.
pic.add(
new void(frame f, transform t) {
drawsharparrow(f, arhead, t*g, p, size, angle, fill, currentpos,
forwards, the_margin, center);
}
);
// Add the path to the picture with the selected pen.
pic.addPath(g, p);
// Get the real value equivalent of currentpos.
pos = position(currentpos, size, g, center);
// If the path should be backwards, reverse it.
if(!forwards) {
G = reverse(g);
pos = length(g)-pos;
}
else G = g;
// Draw the arrow on the picture.
addArrow(pic, arhead, G, p, size, angle, fill, pos);
return pic;
}
picture sharparrow2(arrowhead arhead=DefaultHead, path g, pen p=currentpen,
real size=0, real angle=arrowangle, filltype fill=null,
margin the_margin=NoMargin)
{
// Picture we're adding an arrow to.
picture pic;
// Integer representing the length of the path.
int L;
// If size was not set, return size of the current pen.
if(size == 0) size = arhead.size(p);
pic.add(
new void(frame f, transform t) {
drawsharparrow2(f, arhead, t*g, p, size, angle, fill, the_margin);
}
);
// Add the path with the selected pen to the picture.
pic.addPath(g, p);
// Set L to the length of g.
L = length(g);
// Add an arrow to the head and tail of the path.
addArrow(pic, arhead, g, p, size, angle, fill, L);
addArrow(pic, arhead, reverse(g), p, size, angle, fill, L);
return pic;
}
arrowbar BeginSharpArrow(arrowhead arhead=DefaultHead, real size=0,
real angle=arrowangle, filltype fill=null,
position currentpos=BeginPoint)
{
return new bool(picture pic, path g, pen p, margin the_margin) {
add(pic, sharparrow(arhead, g, p, size, angle, fill, currentpos,
forwards=false, the_margin));
return false;
};
}
arrowbar SharpArrow(arrowhead arhead=DefaultHead, real size=0,
real angle=arrowangle, filltype fill=null,
position currentpos=EndPoint)
{
return new bool(picture pic, path g, pen p, margin the_margin) {
add(pic, sharparrow(arhead, g, p, size, angle, fill,
currentpos, the_margin));
return false;
};
}
arrowbar EndSharpArrow(arrowhead arhead=DefaultHead, real size=0,
real angle=arrowangle, filltype fill=null,
position currentpos=EndPoint)=SharpArrow;
arrowbar MidSharpArrow(arrowhead arhead=DefaultHead, real size=0,
real angle=arrowangle, filltype fill=null)
{
return new bool(picture pic, path g, pen p, margin the_margin) {
add(pic, sharparrow(arhead, g, p, size, angle, fill, MidPoint,
the_margin, center=true));
return false;
};
}
arrowbar SharpArrows(arrowhead arhead=DefaultHead, real size=0,
real angle=arrowangle, filltype fill=null)
{
return new bool(picture pic, path g, pen p, margin the_margin) {
add(pic, sharparrow2(arhead, g, p, size, angle, fill, the_margin));
return false;
};
}
And here's a test:
// Seting output format to "pdf".
import settings;
import _custom_arrows;
settings.outformat="pdf";
settings.render=4;
// Size of output.
size(300);
// Pairs of points to draw arrows between.
pair O = (0, 0);
pair X = (1, 0);
// Size of arrowhead.
real arsize = 5bp;
path g = O--X;
real dy = -0.5;
real dx = 1.5;
draw(shift(0*dx, 0*dy)*g, black, SharpArrow(arsize));
draw(shift(0*dx, 1*dy)*g, black, EndSharpArrow(arsize));
draw(shift(0*dx, 2*dy)*g, black, MidSharpArrow(arsize));
draw(shift(0*dx, 3*dy)*g, black, SharpArrows(arsize));
draw(shift(0*dx, 4*dy)*g, black, BeginSharpArrow(arsize));
draw(shift(1*dx, 0*dy)*g, black+dashed, SharpArrow(arsize));
draw(shift(1*dx, 1*dy)*g, black+dashed, EndSharpArrow(arsize));
draw(shift(1*dx, 2*dy)*g, black+dashed, MidSharpArrow(arsize));
draw(shift(1*dx, 3*dy)*g, black+dashed, SharpArrows(arsize));
draw(shift(1*dx, 4*dy)*g, black+dashed, BeginSharpArrow(arsize));
draw(shift(2*dx, 0*dy)*g, black, SharpArrow(StealthHead, arsize));
draw(shift(2*dx, 1*dy)*g, black, EndSharpArrow(StealthHead, arsize));
draw(shift(2*dx, 2*dy)*g, black, MidSharpArrow(StealthHead, arsize));
draw(shift(2*dx, 3*dy)*g, black, SharpArrows(StealthHead, arsize));
draw(shift(2*dx, 4*dy)*g, black, BeginSharpArrow(StealthHead, arsize));
draw(shift(3*dx, 0*dy)*g, black+dashed, SharpArrow(StealthHead, arsize));
draw(shift(3*dx, 1*dy)*g, black+dashed, EndSharpArrow(StealthHead, arsize));
draw(shift(3*dx, 2*dy)*g, black+dashed, MidSharpArrow(StealthHead, arsize));
draw(shift(3*dx, 3*dy)*g, black+dashed, SharpArrows(StealthHead, arsize));
draw(shift(3*dx, 4*dy)*g, black+dashed, BeginSharpArrow(StealthHead, arsize));
The output:
I acknowledge the request was for 3D arrows, but I've yet to find a satisfactory way of doing this other than by mimicry. Such as:
// Seting output format to "pdf".
import settings;
import _custom_arrows;
import graph;
settings.outformat="pdf";
settings.render=4;
// Size of output.
size(150);
// Various pens used throughout (axes, curves, perpendiculars).
pen apen = black+linewidth(0.8pt);
pen cpen = black+linewidth(0.4pt);
pen ppen = black+linewidth(0.2pt)+linetype("8 4");
// Paths for drawing.
path g;
// Mimic 3D drawing with these.
pair O = (0.0, 0.0);
pair X = scale(1/sqrt(2))*(-1.0, -1.0);
pair Y = (1.0, 0.0);
pair Z = (0.0, 1.0);
// Label for the axes.
Label L;
// Variable for indexing and angles.
int i;
real phi;
// Number of perpendiculars to drop.
int n = 8;
// Size of the arrow head.
real arsize = 5bp;
// Used for mimicing 3D drawing.
pair xyzpoint(real a, real b, real c){
return scale(a)*X+scale(b)*Y+scale(c)*Z;
}
// 3D curve.
pair f0(real t){
real xt = 0.4*cos(t);
real yt = 0.4*sin(t);
real zt = 0.4*cos(4.0*t);
return xyzpoint(xt, yt, zt);
}
// Projection of 3D curve.
pair f1(real t){
real xt = 0.4*cos(t);
real yt = 0.4*sin(t);
return xyzpoint(xt, yt, 0.0);
}
g = O--X;
L = Label("$x$", position=1.0, SW);
draw(L, g, apen, SharpArrow(StealthHead, arsize));
g = O--Y;
L = Label("$y$", position=1.0, E);
draw(L, g, apen, SharpArrow(StealthHead, arsize));
g = O--Z;
L = Label("$z$", position=1.0, N);
draw(L, g, apen, SharpArrow(StealthHead, arsize));
g = graph(f0, 0, 2pi, 400, operator ..);
draw(g, cpen);
g = graph(f1, 0, 2pi, 100, operator ..);
draw(g, cpen+dashed);
for (i=0; i<n; ++i){
phi = 2*pi*i/n;
g = f0(phi)--f1(phi);
draw(g, ppen);
}
Output:

A plethora of examples using this module can be found on my GitHub (GPL3 license):
https://github.com/ryanmaguire/Mathematics-and-Physics/tree/master/asymptote
pgflibraryarrows.code.texwhere it is in the code of\pgfarrowsdeclare{stealth'}{stealth'}. Whether or not it is straightforward to convert this to an asymptote code is a different question. You may attract more attention to your nice question if you provide us with the asymptote code in which you want to use these arrows. – Sep 16 '18 at 18:01stealth'). – Lucy Sep 16 '18 at 18:37Arrow3(HookHead3)looks a bit like a 3D generalization ofstealth'but of course that's not well-defined. – Sep 16 '18 at 18:38HookHead2is not close enough, do you? – Sep 16 '18 at 19:28stealth'in TikZ. – Lucy Sep 16 '18 at 19:30HookHead2look more like what you want. This is easier than it sounds: add lines likeplain_arrows.arrowbarb=1;to your own asy file (after the imports but before you draw anything) and see what happens. This approach will affect all the arrows you draw in the picture, so use it with caution. – Charles Staats Sep 19 '18 at 17:33