对于某些图形,我需要 Asymptote 而不是 TikZ。但是,我更喜欢 TikZ 中的箭头,而不是 Asymptote 中的箭头,尤其是stealth'
TikZ 中的箭头。
有没有办法至少stealth'
为 Asymptote 创建任何(全部?)TikZ 的箭头?(箭头需要足够“灵活”才能在 2D 或 3D 中使用。)
我不知道如何查看 TikZ 的代码stealth'
并为 Asymptote 模仿它。
梅威瑟:
\documentclass[10pt, varwidth]{standalone}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage[inline]{asymptote}
\begin{document}
\begin{asy}
settings.outformat="pdf";
settings.prc=false;
settings.render=16;
import graph3;
import three;
unitsize(1cm);
currentprojection = obliqueX;
// AXES
limits((0,0,0), (3,2.5,2.5));
xaxis3(Label("$x$", align=NW), black + linewidth(0.6pt), arrow=Arrow3(size=4, DefaultHead2(normal=Z)));
yaxis3(Label("$y$", align=N), black + linewidth(0.6pt), arrow=Arrow3(size=4, DefaultHead2(normal=Z)));
zaxis3(Label("$z$", align=W), black + linewidth(0.6pt), arrow=Arrow3(size=4, DefaultHead2));
\end{asy}
\end{document}
答案1
我正在更新我的答案,因为在将虚线与 MidArrow 组合时代码中有一个错误。这个新模块已经纠正了这个问题。我还没有发现任何错误。我或多或少地逐字采用了渐近线处理箭头的方式,但更改了几行以包含更锐利的箭头和隐形箭头。这是模块(名为 _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;
};
}
这是一个测试:
// 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));
输出: 我承认请求的是 3D 箭头,但除了模仿之外,我还没有找到令人满意的方法。例如:
// 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);
}
在我的 GitHub(GPL3 许可证)上可以找到大量使用此模块的示例: https://github.com/ryanmaguire/Mathematics-and-Physics/tree/master/asymptote