Skip to content

Flèches 3D avec TikZ

Nos meilleurs chercheurs ont épuisé leurs réservoirs de café, cherchant jour et nuit la résolution, jusqu'à ce que Lucas trouve la réponse dans Beanstalk. Aujourd'hui, il la partage avec nous.

Solution :

Cette approche vous permet d'attribuer un pas à partir de 0 à 900 signifie couché sur l'écran et 90 signifie perpendiculaire à l'écran.
(Vous pouvez certainement attribuer 91.1 ou 5566 ou tout bon nombre. Actuellement, le abs dans le code éliminent toutes les situations amusantes).

Une fois que vous avez fixé la hauteur, le drawing code fera quelques calculs nécessaires tels que les positions des points tangents. Remarquez que si les pointes de flèches prédéfinies ont quelques options supplémentaires telles que. open et harpoonmon astuce ne les met pas en œuvre. J'utilise même fill pour se débarrasser du problème de largeur de ligne.

documentclass[tikz,border=9]{standalone}
usepgflibrary{arrows.meta}

makeatletter

pgfkeys{
  /pgf/arrow keys/.cd,
  pitch/.code={%
    pgfmathsetmacropgfarrowpitch{#1}
    pgfmathsetmacropgfarrowsinpitch{abs(sin(pgfarrowpitch))}
    pgfmathsetmacropgfarrowcospitch{abs(cos(pgfarrowpitch))}
  },
}

pgfdeclarearrow{
  name = Cone,
  defaults = {       % inherit from Kite
    length     = +3.6pt +5.4,
    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
    pgfmathsetmacropgfarrowhalfwidth{.5pgfarrowwidth}
    pgfmathsetmacropgfarrowhalfwidthsin{pgfarrowhalfwidth*pgfarrowsinpitch}
    pgfpathellipse{pgfpointorigin}{pgfqpoint{pgfarrowhalfwidthsin pt}{0pt}}{pgfqpoint{0pt}{pgfarrowhalfwidth pt}}
    pgfusepath{fill}
    % test if the cone part visible
    pgfmathsetmacropgfarrowlengthcos{pgfarrowlength*pgfarrowcospitch}
    pgfmathparse{pgfarrowlengthcos>pgfarrowhalfwidthsin}
    ifnumpgfmathresult=1
      % it is visible, so draw
      pgfmathsetmacropgfarrowlengthtemp{pgfarrowhalfwidthsin*pgfarrowhalfwidthsin/pgfarrowlengthcos}
      pgfmathsetmacropgfarrowwidthtemp{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}[line width=5]
    draw[-{Cone          }](0,0,0)--(1,0,0);
    draw[-{Cone          }](0,0,0)--(0,1,0);
    draw[-{Cone[pitch=60]}](0,0,0)--(0,0,1);
    path(2,0,0)(0,2,0)(0,0,2);
end{tikzpicture}

foreachtheta in{0,10,...,350}{
    tikz[line width=5]draw[-{Cone[pitch=theta]}](-2,0)(2,0)(0,0)--({cos(theta)},0);
}

foreachtheta in{0,10,...,350}{
    tikz[line width=5,opacity=.5]draw[-{Cone[pitch=theta]}](-2,0)(2,0)(0,0)--({cos(theta)},0);
}

end{document}

Mise à jour

J'ai écrit trois conseils Cone1, Cone2et Cone3.

Cone1

Il utilise le pas comme le Cone ci-dessus, sauf qu'il vérifie maintenant si sin(pitch) est positif. Si c'est le cas, il présume que la flèche est dirigée vers vos yeux et ajoute donc un point blanc. (En même temps, cos(pitch) est obligatoirement positif).

En parlant de points, il est difficile de décider de la taille correcte et de la couleur de celui-ci. Actuellement Cone1 lit le paramètre dans line width et remplit un cercle blanc dont le diamètre est la largeur. C'est une bonne idée puisque la largeur de ligne n'est utilisée nulle part ailleurs mais aussi une mauvaise idée si vous vous souciez de la lisibilité du code.

Cone2

Cette astuce accepte un tangente option par tangent={(1,2,3)} afin qu'il puisse calculer le pas (en fait le sinus et le cosinus de celui-ci).

Le problème est que, dans tout le monde de Ti k Z personne ne s'est jamais soucié des projections de vecteurs 3D perpendiculaires à l'écran. Si nous avons la chance que la projection actuelle sur l'écran soit de type orthogonal, ce qui est probablement assigné par tikz-3dplotalors la projection perpendiculaire à l'écran est bien définie de manière mathématique jusqu'à un signe. (Nous ne pouvons pas faire la différence entre dans l'écran et hors de l'écran compte tenu de la projection sur l'écran).

Malheureusement, dans la plupart des cas, si vous attribuez x=, y=, et z= à la main, ce ne serait pas une projection orthogonale.

Ici, j'utilise simplement un produit en croix pour calculer la projection manquante soumise à la condition que le résultat sera... correct si on utilise tikz-3dplot pour attribuer la projection.

Plus précisément, [email protected]@[email protected] est une commande utilisée dans Ti k Z pour analyser les coordonnées telles que (1,2), (3:4), (A)ou (5,6,7). Lorsque j'écris

[email protected]@[email protected]pgfarrowtangenttosincos#1

et #1 c'est, disons, (5,6,7)Ti k Z se retrouvera avec

pgfarrowtangenttosincos{pgfpointxyz{5}{6}{7}}

Et ensuite, selon la définition de pgfarrowtangenttosincosPGF exécutera

pgfpointxyz{5}{6}{7}
tdplotcrossprod([email protected],[email protected],[email protected])([email protected],[email protected],[email protected])

Maintenant

Donc

Donc

  • le sinus est a/c
  • le cosinus est b/c

Après cela, tout fonctionne comme suit Cone1.

Cone3

Je pirate pgfpointxyz pour qu'il mette en mémoire tampon deux coordonnées 3D récentes. Pendant que la pointe de la flèche est dessinée, je présume que l'ancienne coordonnée mise en mémoire tampon est la fin et que l'ancienne coordonnée mise en mémoire tampon est le début. Ainsi, la tangente devrait être la différence de ces deux coordonnées.

Code

documentclass[tikz]{standalone}
usepgflibrary{arrows.meta}
usepackage{tikz-3dplot}
begin{document}

makeatletter

pgfkeys{
  /pgf/arrow keys/.cd,
  pitch/.code={%
    pgfmathsetmacropgfarrowpitch{#1}
    pgfmathsetmacropgfarrowcospitch{abs(cos(pgfarrowpitch))}
    pgfmathsetmacropgfarrowsinpitch{    sin(pgfarrowpitch)}
  }
}

pgfdeclarearrow{
  name = Cone1,
  defaults = {       % inherit from Kite
    length     = +3.6pt +5.4,
    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
    pgfmathsetmacropgfarrowhalfwidth{.5pgfarrowwidth}
    pgfmathsetmacropgfarrowhalfwidthsin{pgfarrowhalfwidth*abs(pgfarrowsinpitch)}
    pgfpathellipse{pgfpointorigin}{pgfqpoint{pgfarrowhalfwidthsin pt}{0pt}}{pgfqpoint{0pt}{pgfarrowhalfwidth pt}}
    pgfusepath{fill}
    % test if the cone part visible
    pgfmathsetmacropgfarrowlengthcos{pgfarrowlength*pgfarrowcospitch}
    ifdimpgfarrowlengthcos pt>pgfarrowhalfwidthsin pt
      % it is visible, so draw
      pgfmathsetmacropgfarrowlengthtemp{pgfarrowhalfwidthsin*pgfarrowhalfwidthsin/pgfarrowlengthcos}
      pgfmathsetmacropgfarrowwidthtemp{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}
    else
      % it is invisible, check in pointing your eye
      ifdimpgfarrowsinpitch pt>0pt
      pgfpathcircle{pgfqpoint{pgfarrowlengthcos pt}{0pt}}{.5pgfarrowlinewidth}
      pgfsetcolor{white}
      pgfusepath{fill}
      fi
    fi
    pgfpathmoveto{pgfpointorigin}
  }
}
%begin{tikzpicture}[line width=5]
%   draw[-{Cone1          }](0,0,0)--(1,0,0);
%   draw[-{Cone1          }](0,0,0)--(0,1,0);
%   draw[-{Cone1[pitch=60]}](0,0,0)--(0,0,1);
%   path(3,0,0)(0,3,0)(0,0,3);
%end{tikzpicture}
%foreachtheta in{0,10,...,350}{
%   tikz[line width=5]draw[-{Cone1[width'=0 1,pitch=theta]}](-2,-.5)(2,.5)(0,0)--({cos(theta)},0);
%}
%foreachtheta in{0,10,...,350}{
%   tikz[line width=5,opacity=.5]draw[-{Cone1[width'=0 1,pitch=theta]}](-2,-.5)(2,.5)(0,0)--({cos(theta)},0);
%}

defpgfarrowtangenttosincos#1{
    #1
    tdplotcrossprod([email protected],[email protected],[email protected])([email protected],[email protected],[email protected])
    pgfmathsetmacropgfarrowtangentxxyy{[email protected]*[email protected]+[email protected]*[email protected]}
    pgfmathsetmacropgfarrowtangentxy{sqrt(pgfarrowtangentxxyy)}
    pgfmathsetmacropgfarrowtangentz{([email protected]*tdplotresx+[email protected]*tdplotresy+[email protected]*tdplotresz)/72.27*2.54}
    %message{^^J^^J(tdplotresx,tdplotresy,tdplotresz)(pgfarrowtangentxy,pgfarrowtangentz)}show\
    pgfmathsetmacropgfarrowtangentxyz{sqrt(pgfarrowtangentxxyy+pgfarrowtangentz*pgfarrowtangentz)}
    pgfmathsetmacropgfarrowcospitch{pgfarrowtangentxy/pgfarrowtangentxyz}
    pgfmathsetmacropgfarrowsinpitch{pgfarrowtangentz/pgfarrowtangentxyz}
}
pgfkeys{
  /pgf/arrow keys/.cd,
  tangent/.code={%
    [email protected]@[email protected]pgfarrowtangenttosincos#1
  }
}
pgfdeclarearrow{
  name = Cone2,
  defaults = {             % inherit from Kite
    length     = +3.6pt +5.4,
    width'     = +0pt +0.5,
    line width = +0pt 1 1,
    tangent    = {(1,0,0)} % lie on x-axis
  },
  cache = false,           % no need cache
  setup code = {},         % so no need setup
  drawing code = {         % but still need math
    % draw the base
    pgfmathsetmacropgfarrowhalfwidth{.5pgfarrowwidth}
    pgfmathsetmacropgfarrowhalfwidthsin{pgfarrowhalfwidth*abs(pgfarrowsinpitch)}
    pgfpathellipse{pgfpointorigin}{pgfqpoint{pgfarrowhalfwidthsin pt}{0pt}}{pgfqpoint{0pt}{pgfarrowhalfwidth pt}}
    pgfusepath{fill}
    % test if the cone part visible
    pgfmathsetmacropgfarrowlengthcos{pgfarrowlength*pgfarrowcospitch}
    ifdimpgfarrowlengthcos pt>pgfarrowhalfwidthsin pt
      % it is visible, so draw
      pgfmathsetmacropgfarrowlengthtemp{pgfarrowhalfwidthsin*pgfarrowhalfwidthsin/pgfarrowlengthcos}
      pgfmathsetmacropgfarrowwidthtemp{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}
    else
      % it is invisible, check in pointing your eye
      ifdimpgfarrowsinpitch pt>0pt
      pgfpathcircle{pgfqpoint{pgfarrowlengthcos pt}{0pt}}{.5pgfarrowlinewidth}
      pgfsetcolor{white}
      pgfusepath{fill}
      fi
    fi
    pgfpathmoveto{pgfpointorigin}
  }
}
tdplotsetmaincoords{70}{110}
begin{tikzpicture}[line width=5,tdplot_main_coords]
    draw[-{Cone2[tangent={(1,0,0)}]}](0,0,0)--(1,0,0)node[cyan]{X};
    draw[-{Cone2[tangent={(0,1,0)}]}](0,0,0)--(0,1,0)node[cyan]{Y};
    draw[-{Cone2[tangent={(0,0,1)}]}](0,0,0)--(0,0,1)node[cyan]{Z};
    path(-2cm,-2cm)(2cm,2cm);
end{tikzpicture}

foreachtheta in{0,10,...,350}{
    tdplotsetrotatedcoords{theta}{30}{30}
    tikz[line width=5,line cap=round,tdplot_rotated_coords]{
        draw[-{Cone2[tangent={(1,0,0)}]}](0,0,0)--(1,0,0)node[cyan]{X};
        draw[-{Cone2[tangent={(0,1,0)}]}](0,0,0)--(0,1,0)node[cyan]{Y};
        draw[-{Cone2[tangent={(0,0,1)}]}](0,0,0)--(0,0,1)node[cyan]{Z};
        path(-2cm,-2cm)(2cm,2cm);
    }
}

defpgfpointxyz#1#2#3{%
  pgfmathparse{#1}%
  let[email protected]=pgfmathresult%
  pgfmathparse{#2}%
  let[email protected]=pgfmathresult%
  pgfmathparse{#3}%
  let[email protected]=pgfmathresult%
  [email protected]=[email protected][email protected]%
  advance[email protected] by [email protected][email protected]%
  advance[email protected] by [email protected][email protected]%
  [email protected]=[email protected][email protected]%
  advance[email protected] by [email protected][email protected]%
  advance[email protected] by [email protected][email protected]%
  % ↑↑↑old definition↑↑↑ ↓↓↓new code↓↓↓
  globalletpgfolderpointxpgfoldpointx
  globalletpgfolderpointypgfoldpointy
  globalletpgfolderpointzpgfoldpointz
  globalletpgfoldpointx[email protected]
  globalletpgfoldpointy[email protected]
  globalletpgfoldpointz[email protected]
}

pgfdeclarearrow{
  name = Cone3,
  defaults = {             % inherit from Kite
    length     = +3.6pt +5.4,
    width'     = +0pt +0.5,
    line width = +0pt 1 1,
    tangent    = {(1,0,0)} % lie on x-axis
  },
  cache = false,           % no need cache
  setup code = {},         % so no need setup
  drawing code = {         % but still need math
    % calculate the tangent
    pgfkeys{pgf/arrow keys/tangent={(pgfoldpointx-pgfolderpointx,pgfoldpointy-pgfolderpointy,pgfoldpointz-pgfolderpointz)}}
    % draw the base
    pgfmathsetmacropgfarrowhalfwidth{.5pgfarrowwidth}
    pgfmathsetmacropgfarrowhalfwidthsin{pgfarrowhalfwidth*abs(pgfarrowsinpitch)}
    pgfpathellipse{pgfpointorigin}{pgfqpoint{pgfarrowhalfwidthsin pt}{0pt}}{pgfqpoint{0pt}{pgfarrowhalfwidth pt}}
    pgfusepath{fill}
    % test if the cone part visible
    pgfmathsetmacropgfarrowlengthcos{pgfarrowlength*pgfarrowcospitch}
    ifdimpgfarrowlengthcos pt>pgfarrowhalfwidthsin pt
      % it is visible, so draw
      pgfmathsetmacropgfarrowlengthtemp{pgfarrowhalfwidthsin*pgfarrowhalfwidthsin/pgfarrowlengthcos}
      pgfmathsetmacropgfarrowwidthtemp{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}
    else
      % it is invisible, check in pointing your eye
      ifdimpgfarrowsinpitch pt>0pt
      pgfpathcircle{pgfqpoint{pgfarrowlengthcos pt}{0pt}}{.5pgfarrowlinewidth}
      pgfsetcolor{white}
      pgfusepath{fill}
      fi
    fi
    pgfpathmoveto{pgfpointorigin}
  }
}
tdplotsetmaincoords{70}{110}
begin{tikzpicture}[line width=5,tdplot_main_coords]
    draw[-{Cone2[tangent={(1,0,0)}]}](0,0,0)--(1,0,0)node[cyan]{X};
    draw[-{Cone2[tangent={(0,1,0)}]}](0,0,0)--(0,1,0)node[cyan]{Y};
    draw[-{Cone2[tangent={(0,0,1)}]}](0,0,0)--(0,0,1)node[cyan]{Z};
    path(-2cm,-2cm)(2cm,2cm);
end{tikzpicture}

foreachtheta in{0,5,...,355}{
    tdplotsetrotatedcoords{theta}{2*theta}{3*theta}
    tikz[line width=5,line cap=round,tdplot_rotated_coords]{
        draw[-Cone3](0,0,0)--(1,0,0)node[cyan]{X};
        draw[-Cone3](0,0,0)--(0,1,0)node[cyan]{Y};
        draw[-Cone3](0,0,0)--(0,0,1)node[cyan]{Z};
        path(-2cm,-2cm)(2cm,2cm);
    }
}

end{document}

qu'en est-il de celui-ci ?

enter image description here

documentclass[margin=10pt]{standalone}
usepackage[svgnames]{xcolor}
usepackage{tikz}
begin{document}

begin{tikzpicture}[scale=3]
defopaque{0.035}    % rendering opacity 
defprop{1.3}        % variable of rendering opacity
defR{2} %           % cone slant height
deftheta{21}        % 2*theta is the angle of expanded by the tip of the cone
defrange{100}       % number of interaction for smooth rending effect
defratio{0.4}       % ellipse b/a for cone lower edge 
defaratio{0.35}     % a_rod / a_cone  
defheight{0.8}      % rod height 
deffraction{0.375}  % ellipse b/a for rod edge 
defangle{12}        % angle extended by rod edge
deftotal{60}        % number of interaction for smooth rending effect
defconecolor{red!range!black!40!red}   % cone color 

foreach i in {0,...,range}
{
    fill[conecolor, opacity={opaque+prop*opaque*(range-2*i)/range}] (0, {R*cos(theta)}) -- ({R*sin(theta)*cos(270+90*i/range)},{ratio*R*sin(theta)*sin(270+90*i/range)})  arc({270+90*i/range}:360: {R*sin(theta)}  and {ratio*R*sin(theta)} ) -- cycle;
    fill[conecolor, opacity={opaque+prop*opaque*(range-i)/range}] (0, {R*cos(theta)}) -- ({R*sin(theta)*cos(270-90*i/range)},{ratio*R*sin(theta)*sin(270-90*i/range)})  arc({270-90*i/range}:180: {R*sin(theta)}  and {ratio*R*sin(theta)} ) -- cycle;
}
definecolor{conered}{RGB}{255,114,114}
definecolor{conegreen}{RGB}{37,146,37}
definecolor{coneblue}{RGB}{107,107,236}

fill[conered] (-0.7167,0) arc (180:360: 0.7167 and 0.2867)  arc (0:180: 0.7167 and 0.2867) -- cycle ;

pgfmathparse{R*sin(theta)*aratio}

fill[conered] (-0.251, -{sqrt(1-0.251*0.251/(0.7167*0.7167) )*0.2867}) arc ({360-acos(-0.251/0.7167) )}:{360-acos(0.251/0.7167 )}: 0.7167 and 0.2867 ) -- (0.251,-height) arc (360:180:0.251 and 0.0941) --cycle ;

foreach i  in {0,...,total}
{
    fill[conecolor, opacity={2*opaque+prop*opaque*(total-1*i)/total}] ({pgfmathresult*i/total}, {pgfmathresult*ratio*sqrt(1-i/total*i/total) } )  arc ( {acos(i/total)}:0: {R*sin(theta)*aratio} and {R*sin(theta)*aratio*ratio} ) -- ++(0,-height) arc (360: {360-acos(i/total)}: {R*sin(theta)*aratio} and {R*sin(theta)*aratio*ratio} ) -- cycle;
    fill[conecolor, opacity={2*opaque+prop*opaque*(total-1*i)/total}] ({-R*sin(theta)*aratio*i/total}, {R*sin(theta)*aratio*ratio*sqrt(1-i/total*i/total) } )  arc ( {acos(-i/total)}:180: {R*sin(theta)*aratio} and {R*sin(theta)*aratio*ratio} ) -- ++(0,-height) arc (180: {360-acos(-i/total)}: {R*sin(theta)*aratio} and {R*sin(theta)*aratio*ratio} ) -- cycle;
}

draw[thick, conered, yshift=-0.8cm] (-0.251,0.8) -- (-0.251,0) arc (180:360:0.251 and 0.0941) -- (0.251,0.8);
end{tikzpicture}

end{document}

Vous êtes en mesure de corroborer notre mission en ajoutant un commentaire ou en l'évaluant, nous vous en remercions.



Utilisez notre moteur de recherche

Ricerca
Generic filters

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.