你可以看到原始的箭头有一些阴影,而我的没有。有没有有效的方法来实现它?我尝试根据这个解决方案制作锥形尖端这里。笔尖很完美,但如果没有阴影,就很难看清箭头指向的位置。
下面是重现我的身材的代码:
\documentclass[tikz,border=3mm]{standalone}
\usetikzlibrary{3d,arrows.meta}
\makeatletter
\pgfkeys{
/pgf/arrow keys/.cd,
pitch/.code={%
\pgfmathsetmacro\pgfarrowpitch{#1}
\pgfmathsetmacro\pgfarrowsinpitch{abs(sin(\pgfarrowpitch))}
\pgfmathsetmacro\pgfarrowcospitch{abs(cos(\pgfarrowpitch))}
},
}
\pgfdeclarearrow{
name = Cone,
defaults = { % inherit from Kite
length = +3pt +2,
width' = +0pt +0.5,
line width = +0pt 1 1,
pitch = +0, % lie on screen
},
cache = false, % no need cache
setup code = {}, % so no need setup
drawing code = { % but still need math
% draw the base
\pgfmathsetmacro\pgfarrowhalfwidth{.5\pgfarrowwidth}
\pgfmathsetmacro\pgfarrowhalfwidthsin{\pgfarrowhalfwidth*\pgfarrowsinpitch}
\pgfpathellipse{\pgfpointorigin}{\pgfqpoint{\pgfarrowhalfwidthsin pt}{0pt}}{\pgfqpoint{0pt}{\pgfarrowhalfwidth pt}}
\pgfusepath{fill}
% test if the cone part visible
\pgfmathsetmacro\pgfarrowlengthcos{\pgfarrowlength*\pgfarrowcospitch}
\pgfmathparse{\pgfarrowlengthcos>\pgfarrowhalfwidthsin}
\ifnum\pgfmathresult=1
% it is visible, so draw
\pgfmathsetmacro\pgfarrowlengthtemp{\pgfarrowhalfwidthsin*\pgfarrowhalfwidthsin/\pgfarrowlengthcos}
\pgfmathsetmacro\pgfarrowwidthtemp{\pgfarrowhalfwidth/\pgfarrowlengthcos*sqrt(\pgfarrowlengthcos*\pgfarrowlengthcos-\pgfarrowhalfwidthsin*\pgfarrowhalfwidthsin)}
\pgfpathmoveto{\pgfqpoint{\pgfarrowlengthcos pt}{0pt}}
\pgfpathlineto{\pgfqpoint{\pgfarrowlengthtemp pt}{ \pgfarrowwidthtemp pt}}
\pgfpathlineto{\pgfqpoint{\pgfarrowlengthtemp pt}{-\pgfarrowwidthtemp pt}}
\pgfpathclose
\pgfusepath{fill}
\fi
\pgfpathmoveto{\pgfpointorigin}
}
}
\begin{document}
\begin{tikzpicture}[z={(0,-0.5)}]
\begin{scope}[canvas is xz plane at y=0,line width=1.25pt,line cap=round]
%\draw (0,0) circle (2cm);
\draw[red!75!black,-{Cone[pitch=0]}](90:2) --++ (0:1cm);
\draw[red!75!black,-{Cone[pitch=36]}](126:2) --++ (36:1cm);
\draw[red!75!black,-{Cone[pitch=72]}](162:2) --++ (72:1cm);
\draw[red!75!black,-{Cone[pitch=108]}](198:2) --++ (108:1cm) node[above left] {$\vec{H}$};
\draw[red!75!black,-{Cone[pitch=144]}](234:2) --++ (144:1cm);
\draw[red!75!black,-{Cone[pitch=180]}](270:2) --++ (180:1cm);
\draw[red!75!black,-{Cone[pitch=216]}](306:2) --++ (216:1cm);
\draw[red!75!black,-{Cone[pitch=252]}](342:2) --++ (252:1cm);
\draw[red!75!black,-{Cone[pitch=288]}](378:2) --++ (288:1cm);
\draw[red!75!black,-{Cone[pitch=324]}](414:2) --++ (324:1cm);
\end{scope}
\draw[-{Cone},line width=1.5pt,line cap=round,blue!75!black] (0,0,0) -- (0,2,0) node[above]{$\vec{J}+\frac{\partial \vec{D}}{\partial t}$};
\end{tikzpicture}
\end{document}
答案1
代码很长,因为我将光中的一般锥体定义为对象pic
。它取决于八个参数:底面和顶面的中心(6 个参数)以及两个相应的半径。
基于此元素,实心箭头也构造为pipc
对象。这次只有六个参数(#4
始终为“空”)。但有两个键控制箭头的外观,并且可以修改:arrowhead
和arrow radius
。在示例中,它们都针对蓝色箭头进行了修改。
评论
我插入了一些可以与cone
物体一起绘制的圆锥体。也许有人会觉得它们有用。
代码
\documentclass[11pt, margin=15pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{math}
\begin{document}
\tikzset{%
view/.style 2 args={% observer longitude and latitude (y upwards)
% Remark. lomg=0 means x=0
z={({-sin(#1)}, {-cos(#1)*sin(#2)})},
x={({cos(#1)}, {-sin(#1)*sin(#2)})},
y={(0, {cos(#2)})},
evaluate={%
\tox={sin(#1)*cos(#2)};
\toy={sin(#2)};
\toz={cos(#1)*cos(#2)};
},
longitude = #1,
latitude = #2
},
sun/.style n args={3}{% longitude, latitude, light contrast in [0, 1]
sun longitude = #1,
sun latitude = #2,
contrast = #3,
evaluate={%
real \sunx, \suny, \sunz, \lightC;
\sunx = sin(\sLongit)*cos(\sLatit);
\suny = sin(\sLatit);
\sunz = cos(\sLongit)*cos(\sLatit);
}
}
}
\pgfkeys{/tikz/.cd,
latitude/.store in=\aLatit, % observer's latitude
latitude=0
}
\pgfkeys{/tikz/.cd,
longitude/.store in=\aLongit, % observer's longitude
longitude=0 % corresponds to x=0
}
\pgfkeys{/tikz/.cd,
sun latitude/.store in=\sLatit,
sun latitude = 80
}
\pgfkeys{/tikz/.cd,
sun longitude/.store in=\sLongit,
sun longitude=90
}
\pgfkeys{/tikz/.cd,
contrast/.store in=\lightC, % light contrast \in [0, 1]
contrast=.5
}
\pgfkeys{/tikz/.cd,
arrowhead/.store in=\arrowLConstant, % proportion of the head \in [0, 1]
arrowhead=.3
}
\pgfkeys{/tikz/.cd,
arrow radius/.store in=\arrowRadius, % body radius in cm
arrow radius=.08
}
\tikzmath{%
function coneBaseColor(\k) {% \k = 1 or 2 for the two disks: bottom top
\res = \nx*\tox + \ny*\toy + \nz*\toz;
if \k==1 then {% bottom; the normal vector is -n
\res = -\res;
};
if \res>0 then {% if seen
\tmp = int(100*\lightC*(\nx*\sunx + \ny*\suny + \nz*\sunz));
if \k==1 then {return -\tmp;} else {return \tmp;};
} else {return -1000;};
};
function coneFaceColor(\j, \M) {% verifies if seen
real \ang;
\t = 360*((\j-.5)/\M);
\ux = \vx*cos(\t) +\wx*sin(\t);
\uy = \vy*cos(\t) +\wy*sin(\t);
\uz = \vz*cos(\t) +\wz*sin(\t);
% modification needed when the radii are different
\ang = atan2(\coneRadiusB -\coneRadiusT, \tmpAB);
\ux = \ux*cos(\ang) +\nx*sin(\ang);
\uy = \uy*cos(\ang) +\ny*sin(\ang);
\uz = \uz*cos(\ang) +\nz*sin(\ang);
\res = \ux*\tox + \uy*\toy + \uz*\toz;
if \res>0 then {% if seen
\tmp = int(100*\lightC*(\ux*\sunx + \uy*\suny + \uz*\sunz));
return \tmp;
} else {return -1000;};
};
}
\tikzset{%
pics/cone/.style args={(#1,#2,#3), (#4,#5,#6), rB=#7, rT=#8}{%
% x, y, z of the bottom and top centers, bottom and top radius
code={%
\colorlet{mainColor}{.}
\colorlet{leftRGB{1}}{white}
\colorlet{leftRGB{0}}{mainColor!50!black}
\colorlet{mainDark}{mainColor!50!black}
\tikzmath{% A, B, rB, rT, and construction of the orthonormal basis
real \Ax, \Ay, \Az, \Bx, \By, \Bz, \coneRadiusB, \coneRadiusT;
\Ax = #1;
\Ay = #2;
\Az = #3;
\Bx = #4;
\By = #5;
\Bz = #6;
\coneRadiusB = #7;
\coneRadiusT = #8;
integer \coneN, \k, \j, \prevj, \i;
\coneN = 13 +int(max(40*#7, 40*#8));
real \tmpAB, \tmpx, \tmpy, \tmpz, \tmpcst, \tmpForColor;
real \nx, \ny, \nz, \vx, \vy, \vz, \wx, \wy, \wz;
\tmpx = \Bx -\Ax;
\tmpy = \By -\Ay;
\tmpz = \Bz -\Az;
\tmpAB = sqrt(\tmpx*\tmpx +\tmpy*\tmpy +\tmpz*\tmpz);
\nx = \tmpx/\tmpAB;
\ny = \tmpy/\tmpAB;
\nz = \tmpz/\tmpAB;
if abs(\ny)>=abs(\nz) then {%
\tmpcst = sqrt(\nx*\nx +\ny*\ny);
\vx = -\ny/\tmpcst;
\vy = \nx/\tmpcst;
\vz = 0;
\wx = -\vy*\nz;
\wy = \vx*\nz;
\wz = (1 -\nz*\nz)/\tmpcst;
} else {%
\tmpcst = sqrt(\nx*\nx +\nz*\nz);
\vx = -\nz/\tmpcst;
\vy = 0;
\vz = \nx/\tmpcst;
\wx = \vz*\ny;
\wy = (\ny*\ny -1)/\tmpcst;
\wz = -\vx*\ny;
};
%% points \P {1,\j} for bottom, \P {2,\j} for top
for \j in {0, ..., \coneN}{%
\t = \j/\coneN*360;
\Px{1,\j} = \Ax +\coneRadiusB*\vx*cos(\t) +\coneRadiusB*\wx*sin(\t);
\Py{1,\j} = \Ay +\coneRadiusB*\vy*cos(\t) +\coneRadiusB*\wy*sin(\t);
\Pz{1,\j} = \Az +\coneRadiusB*\vz*cos(\t) +\coneRadiusB*\wz*sin(\t);
\Px{2,\j} = \Bx +\coneRadiusT*\vx*cos(\t) +\coneRadiusT*\wx*sin(\t);
\Py{2,\j} = \By +\coneRadiusT*\vy*cos(\t) +\coneRadiusT*\wy*sin(\t);
\Pz{2,\j} = \Bz +\coneRadiusT*\vz*cos(\t) +\coneRadiusT*\wz*sin(\t);
};
%% lateral faces
for \j in {1, ..., \coneN}{%
\prevj = \j -1;
\tmpForColor = coneFaceColor(\j, \coneN);
if \tmpForColor>-999 then {%
if \tmpForColor>=0. then { \i = 1; } else {%
\i = 0; % in the shade for leftRGB{}
\tmpForColor = int(abs(\tmpForColor));
};
{%
\filldraw[leftRGB{\i}!\tmpForColor!mainColor, line width=.001pt]
(\Px{1,\prevj}, \Py{1,\prevj}, \Pz{1,\prevj})
-- (\Px{1,\j}, \Py{1,\j}, \Pz{1,\j})
-- (\Px{2,\j}, \Py{2,\j}, \Pz{2,\j})
-- (\Px{2,\prevj}, \Py{2,\prevj}, \Pz{2,\prevj})
-- cycle;
};
};
};
%% bottom or top face
for \k in {1, 2}{
\tmpForColor = coneBaseColor(\k);
if \tmpForColor>-999. then {%
if \tmpForColor>=0. then { \i = 1; } else {%
\i = 0; % in the shade for leftRGB{}
\tmpForColor = int(abs(\tmpForColor));
};
{
\fill[leftRGB{\i}!\tmpForColor!mainColor]
(\Px{\k,0}, \Py{\k,0}, \Pz{\k,0})
\foreach \j in {1, ..., \coneN}{%
-- (\Px{\k,\j}, \Py{\k,\j}, \Pz{\k,\j})
} -- cycle;
};
};
};
} % end tikzmath
}
},
pics/solid arrow/.style args={(#1,#2,#3)--#4+(#5,#6,#7)}{%
code={%
\colorlet{arrowColor}{.}
\tikzmath{%
real \aVx, \aVy, \aVz;
\aVx = #5;
\aVy = #6;
\aVz = #7;
real \CyBx, \CyBy, \CyBz, \Mx, \My, \Mz, \CoTx, \CoTy, \CoTz;
\CyBx = #1;
\CyBy = #2;
\CyBz = #3;
\CoTx = \CyBx +\aVx;
\CoTy = \CyBy +\aVy;
\CoTz = \CyBz +\aVz;
\Mx = \CyBx +(1 -\arrowLConstant)*\aVx;
\My = \CyBy +(1 -\arrowLConstant)*\aVy;
\Mz = \CyBz +(1 -\arrowLConstant)*\aVz;
real \cylinderRadius, \coneRadius, \arrowL;
\arrowL = sqrt(\aVx*\aVx +\aVy*\aVy +\aVz*\aVz);
\cylinderRadius = \arrowRadius;
\coneRadius = 2*\cylinderRadius;
\testTopSeen = \aVx*\tox + \aVy*\toy + \aVz*\toz;
if \testTopSeen>0 then {%
{
\draw pic[arrowColor]
{cone={(\CyBx, \CyBy, \CyBz), (\Mx, \My, \Mz),
rB=\cylinderRadius, rT=\cylinderRadius}};
\draw pic[arrowColor]
{cone={(\Mx, \My, \Mz), (\CoTx, \CoTy, \CoTz),
rB=\coneRadius, rT=0}};
};
} else {%
{
\draw pic[arrowColor]
{cone={(\Mx, \My, \Mz), (\CoTx, \CoTy, \CoTz),
rB=\coneRadius, rT=0}};
\draw pic[arrowColor]
{cone={(\CyBx, \CyBy, \CyBz), (\Mx, \My, \Mz),
rB=\cylinderRadius, rT=\cylinderRadius}};
};
};
} % end tikzmath
}
}
}
\begin{tikzpicture}[view={20}{30}, sun={-110}{30}{.5},
arrow radius=.07,
evaluate={%
real \r, \l;
\r = 3; \l = 1.6;
integer \N;
\N = 11;
}]
\foreach \k [evaluate=\k as \t using {90 +(\k/\N)*360}]
in {1, ..., \N}{%
\path pic[red]
{solid arrow={({\r*sin(\t)}, 0, {\r*cos(\t)})--
+({\l*cos(\t)}, 0, {-\l*sin(\t)})}};
}
\path pic[blue, arrow radius=.075, arrowhead=.17]
{solid arrow={(0, 0, 0)-- +(0, 3*\l, 0)}};
\path (0, 0, 0) +(0, 3*\l, 0)
node[right=1ex] {$\vec{J}+\frac{\partial\vec{D}}{\partial t}$};;
\end{tikzpicture}
\end{document}
答案2
用于 Asymptote 中的真实 3D 箭头。
// Run on http://asymptote.ualberta.ca/
import three;
usepackage("amsmath");
size(5cm);
currentprojection=orthographic(2,3,1,zoom=.9);
string s="$\vec{J}+\dfrac{\partial\vec{D}}{\partial t}$";
draw(Label(s,align=N,EndPoint,blue),O--3Z,blue+3pt,Arrow3(size=15pt));
// (x(t),y(t)) is the circle of radius r
real r=2;
real x(real s){return r*cos(s);}
real y(real s){return r*sin(s);}
// (x'(t),y'(t)) are tangents
real xt(real s){return -r*sin(s);}
real yt(real s){return r*cos(s);}
int n=10; // the number of tangent vectors
for (int i=0; i<n; ++i){
real s=i*2pi/n;
path3 vec=O--(xt(s),yt(s),0);
transform3 T=shift(x(s),y(s),0)*scale3(.5);
draw(T*vec,magenta+2pt,Arrow3(size=10pt));
}
label("$\vec{H}$",(0,-r-1,0),magenta);
更新:为了获得更好的外观,每个箭体都被视为一个圆柱体(solids
需要模块)。
// Run on http://asymptote.ualberta.ca/
import three;
import solids;
usepackage("amsmath");
size(5cm);
currentprojection=orthographic(2,3,1,zoom=.8);
string s="$\vec{J}+\dfrac{\partial\vec{D}}{\partial t}$";
draw(Label(s,align=N,EndPoint,blue),O--3Z,blue,Arrow3(size=15pt));
draw(scale(.05,.05,2.8)*unitcylinder,blue);
// (x(t),y(t)) is the circle of radius r
real r=2;
real x(real s){return r*cos(s);}
real y(real s){return r*sin(s);}
// (x'(t),y'(t)) are tangents
real xt(real s){return -r*sin(s);}
real yt(real s){return r*cos(s);}
int n=10; // the number of tangent vectors
for (int i=0; i<n; ++i){
real s=i*2pi/n;
triple B=(xt(s),yt(s),0),A=(x(s),y(s),0);
path3 vec=A--A+.6B;
draw(vec,magenta,Arrow3(size=10pt));
draw(shift(A)*surface(cylinder(O,.05,1,B)),magenta);
}
label("$\vec{H}$",(0,-r-1,0),magenta);