以下のコードでは、3 つのケース (コメント アウト) が完全に同等であると予想していました。残念ながら (私にとっては) 3 番目のケースでは、座標 (X) から座標 (Y) への直線が生成されます。私は常に、\foreach
中括弧で囲まれたコードを入力ストリームに「そのまま」挿入するだけのステートメントだと思っていましたが、そうではないようです。
何が足りないのでしょうか? それともこれは (既知の) バグでしょうか?
\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
\path
(0,0) coordinate(X)
(1,1) coordinate(A) node[minimum size=4.3pt,inner sep=0pt](nA){}
(2,2) coordinate(B) node[minimum size=4.3pt,inner sep=0pt](nB){}
(3,3) coordinate(Y)
;
\draw[rotate=45]
(A) +(-7pt/2,0) arc[start angle=180,end angle=0,radius=7pt/2]
(B) +(-7pt/2,0) arc[start angle=180,end angle=0,radius=7pt/2]
;
% Case (1)
%\draw (X) -- (nA) -- (nB) -- (Y) ;
% Case (2)
%\foreach \x/\y in {X/nA,nA/nB,nB/Y} {\draw (\x) -- (\y);}
% Case (3)
%\draw (X) \foreach \x in {nA,nB,Y} { -- (\x)};
\end{tikzpicture}
\end{document}
編集: ノードの使用は、アークのサイズを処理するために意図的に行われます。また、私の実際の使用例は、tikz 交差パッケージの範囲内です。その場合、多くのパス交差を処理するために foreach を使用する必要があります。その場合、3 番目のケースが最も自然な構成になります。
編集 2: コメントの議論と (今のところ) 唯一の答えから。私はこれを実現するための別の方法は必要ありません。私はすでに実際のケースを解決しました (ケース (2) に基づく戦略を展開することによって)。私の唯一の質問は、3 番目のケースで \foreach に何が起こっているのかということです。なぜそのように動作するのですか? それが唯一の質問であり、その落とし穴を回避するための別の代替方法ではありません。
編集 3: Andrew Stacey さんの回答は、私の最初の質問以上のものでした。つまり、私の唯一の質問は、何が起こっているのか、それが予想されていたのか、ということでした。foreach の実装方法とパス サブシステムとのやり取りを考えると、これはバグであることは明らかです。foreach は、括弧内のコードを展開していくつかの変数 (foreach の変数) を置き換えるだけではありません。
答え1
Skillmon が回答のコメントで述べているように、これはグループ化の問題です。このサイトのさまざまな質問で文書化され、検討されているように、\foreach
コードはグループに入れられます (記憶が正しければ、実際には 2 つのグループがあります)。そのため、1 つのループから次のループに記憶する必要があるものはすべて、そのグループ化から取り出すために少し作業が必要です。ユーザー レベルでは、これは通常、remember
Qrrbrbirlbel が質問に対するコメントの 1 つで言及しているキーを使用して実現されます。
しかし、パス内では、前の座標を記憶することは非常に便利で一般的なことなので、TikZ は、それが正しく記憶されるようにするために、かなりの努力をします (文字通り)。そのため、TikZ は、ループの各反復の開始と終了に、グループ化が存在しないかのように最後のポイントを記憶するコードをインストールします。これを行うコードは比較的長くなります。
\def\tikz@foreach{%
\def\pgffor@beginhook{%
\tikz@lastx=\tikz@foreach@save@lastx%
\tikz@lasty=\tikz@foreach@save@lasty%
\tikz@lastxsaved=\tikz@foreach@save@lastxsaved%
\tikz@lastysaved=\tikz@foreach@save@lastysaved%
\setbox\tikz@figbox=\box\tikz@tempbox%
\setbox\tikz@figbox@bg=\box\tikz@tempbox@bg%
\expandafter\tikz@scan@next@command\pgfutil@firstofone}%
\def\pgffor@endhook{\pgfextra{%
\xdef\tikz@foreach@save@lastx{\the\tikz@lastx}%
\xdef\tikz@foreach@save@lasty{\the\tikz@lasty}%
\xdef\tikz@foreach@save@lastxsaved{\the\tikz@lastxsaved}%
\xdef\tikz@foreach@save@lastysaved{\the\tikz@lastysaved}%
\global\setbox\tikz@tempbox=\box\tikz@figbox%
\global\setbox\tikz@tempbox@bg=\box\tikz@figbox@bg%
\pgfutil@gobble}}%
\def\pgffor@afterhook{%
\tikz@lastx=\tikz@foreach@save@lastx%
\tikz@lasty=\tikz@foreach@save@lasty%
\tikz@lastxsaved=\tikz@foreach@save@lastxsaved%
\tikz@lastysaved=\tikz@foreach@save@lastysaved%
\let\pgffor@beginhook\relax%
\let\pgffor@endhook\relax%
\let\pgffor@afterhook\relax%
\setbox\tikz@figbox=\box\tikz@tempbox%
\setbox\tikz@figbox@bg=\box\tikz@tempbox@bg%
\tikz@scan@next@command}%
\global\setbox\tikz@tempbox=\box\tikz@figbox%
\global\setbox\tikz@tempbox@bg=\box\tikz@figbox@bg%
\xdef\tikz@foreach@save@lastx{\the\tikz@lastx}%
\xdef\tikz@foreach@save@lasty{\the\tikz@lasty}%
\xdef\tikz@foreach@save@lastxsaved{\the\tikz@lastxsaved}%
\xdef\tikz@foreach@save@lastysaved{\the\tikz@lastysaved}%
\foreach}%
参考までに、TikZ の現在のバージョンでは、これは の 2582 行目から始まりますtikz.code.tex
。
ここで、TikZ はパス上にアンカーのないノードに遭遇すると、moveto
パスに を追加してパスにブレークを挿入します。ただし、次の座標に最も適したノードの境界上の終了ポイントを選択するため、 のターゲットmoveto
は遭遇した時点ではわかりません。したがって、 がmoveto
完了していないこと、および TikZ が次の座標を認識したときにmoveto
パスの次の部分の残りを計算する前にそれを完了することを記録する必要があり、これを実行する必要があることをフラグ付けするために、TikZ はマクロを定義し\tikz@moveto@waiting
、このマクロは TikZ が計算を完了するために必要なノード名を保持します。
私が上に投稿したコードをよく見ると\tikz@foreach
、明らかに参照が欠けていることがわかります。\tikz@moveto@waiting
つまり、しない各反復の最後に更新されます。つまり、TikZ が反復を開始するたびに、\tikz@moveto@waiting
ループの外側から元の値が保持されます。あなたの場合、それが最初のノードであり、すべての行が前のノードからX
ではなく、そこから始まるのはそのためです。X
幸いなことに、必要な行を追加するのは難しくありません。以下は のパッチバージョンです\tikz@foreach
。
\def\tikz@foreach{%
\def\pgffor@beginhook{%
\global\let\tikz@moveto@waiting\tikz@foreach@moveto@waiting%
\tikz@lastx=\tikz@foreach@save@lastx%
\tikz@lasty=\tikz@foreach@save@lasty%
\tikz@lastxsaved=\tikz@foreach@save@lastxsaved%
\tikz@lastysaved=\tikz@foreach@save@lastysaved%
\setbox\tikz@figbox=\box\tikz@tempbox%
\setbox\tikz@figbox@bg=\box\tikz@tempbox@bg%
\expandafter\tikz@scan@next@command\pgfutil@firstofone}%
\def\pgffor@endhook{\pgfextra{%
\global\let\tikz@foreach@moveto@waiting\tikz@moveto@waiting%
\xdef\tikz@foreach@save@lastx{\the\tikz@lastx}%
\xdef\tikz@foreach@save@lasty{\the\tikz@lasty}%
\xdef\tikz@foreach@save@lastxsaved{\the\tikz@lastxsaved}%
\xdef\tikz@foreach@save@lastysaved{\the\tikz@lastysaved}%
\global\setbox\tikz@tempbox=\box\tikz@figbox%
\global\setbox\tikz@tempbox@bg=\box\tikz@figbox@bg%
\pgfutil@gobble}}%
\def\pgffor@afterhook{%
\let\tikz@moveto@waiting\tikz@foreach@moveto@waiting%
\tikz@lastx=\tikz@foreach@save@lastx%
\tikz@lasty=\tikz@foreach@save@lasty%
\tikz@lastxsaved=\tikz@foreach@save@lastxsaved%
\tikz@lastysaved=\tikz@foreach@save@lastysaved%
\let\pgffor@beginhook\relax%
\let\pgffor@endhook\relax%
\let\pgffor@afterhook\relax%
\setbox\tikz@figbox=\box\tikz@tempbox%
\setbox\tikz@figbox@bg=\box\tikz@tempbox@bg%
\tikz@scan@next@command}%
\global\setbox\tikz@tempbox=\box\tikz@figbox%
\global\setbox\tikz@tempbox@bg=\box\tikz@figbox@bg%
\xdef\tikz@foreach@save@lastx{\the\tikz@lastx}%
\xdef\tikz@foreach@save@lasty{\the\tikz@lasty}%
\xdef\tikz@foreach@save@lastxsaved{\the\tikz@lastxsaved}%
\xdef\tikz@foreach@save@lastysaved{\the\tikz@lastysaved}%
\global\let\tikz@foreach@moveto@waiting\tikz@moveto@waiting%
\foreach}%
テスト可能なコード:
\documentclass{article}
%\url{https://tex.stackexchange.com/q/706591/86}
\usepackage{tikz}
\makeatletter
\def\tikz@patched@foreach{%
\def\pgffor@beginhook{%
\global\let\tikz@moveto@waiting\tikz@foreach@moveto@waiting%
\tikz@lastx=\tikz@foreach@save@lastx%
\tikz@lasty=\tikz@foreach@save@lasty%
\tikz@lastxsaved=\tikz@foreach@save@lastxsaved%
\tikz@lastysaved=\tikz@foreach@save@lastysaved%
\setbox\tikz@figbox=\box\tikz@tempbox%
\setbox\tikz@figbox@bg=\box\tikz@tempbox@bg%
\expandafter\tikz@scan@next@command\pgfutil@firstofone}%
\def\pgffor@endhook{\pgfextra{%
\global\let\tikz@foreach@moveto@waiting\tikz@moveto@waiting%
\xdef\tikz@foreach@save@lastx{\the\tikz@lastx}%
\xdef\tikz@foreach@save@lasty{\the\tikz@lasty}%
\xdef\tikz@foreach@save@lastxsaved{\the\tikz@lastxsaved}%
\xdef\tikz@foreach@save@lastysaved{\the\tikz@lastysaved}%
\global\setbox\tikz@tempbox=\box\tikz@figbox%
\global\setbox\tikz@tempbox@bg=\box\tikz@figbox@bg%
\pgfutil@gobble}}%
\def\pgffor@afterhook{%
\let\tikz@moveto@waiting\tikz@foreach@moveto@waiting%
\tikz@lastx=\tikz@foreach@save@lastx%
\tikz@lasty=\tikz@foreach@save@lasty%
\tikz@lastxsaved=\tikz@foreach@save@lastxsaved%
\tikz@lastysaved=\tikz@foreach@save@lastysaved%
\let\pgffor@beginhook\relax%
\let\pgffor@endhook\relax%
\let\pgffor@afterhook\relax%
\setbox\tikz@figbox=\box\tikz@tempbox%
\setbox\tikz@figbox@bg=\box\tikz@tempbox@bg%
\tikz@scan@next@command}%
\global\setbox\tikz@tempbox=\box\tikz@figbox%
\global\setbox\tikz@tempbox@bg=\box\tikz@figbox@bg%
\xdef\tikz@foreach@save@lastx{\the\tikz@lastx}%
\xdef\tikz@foreach@save@lasty{\the\tikz@lasty}%
\xdef\tikz@foreach@save@lastxsaved{\the\tikz@lastxsaved}%
\xdef\tikz@foreach@save@lastysaved{\the\tikz@lastysaved}%
\global\let\tikz@foreach@moveto@waiting\tikz@moveto@waiting%
\foreach}%
\DeclareDocumentCommand \FixForeach {}
{
\let\tikz@foreach=\tikz@patched@foreach
}
\makeatother
\begin{document}
\begin{tikzpicture}
\node[draw] (A) at (0,0) {};
\node[draw] (B) at (1,1) {};
\node[draw] (C) at (2,0) {};
\draw (A) -- (B) -- (C);
\end{tikzpicture}
\begin{tikzpicture}
\node[draw] (A) at (0,0) {};
\node[draw] (B) at (1,1) {};
\node[draw] (C) at (2,0) {};
\draw (A) \foreach \nd in {B,C} { -- (\nd) };
\end{tikzpicture}
\FixForeach
\begin{tikzpicture}
\node[draw] (A) at (0,0) {};
\node[draw] (B) at (1,1) {};
\node[draw] (C) at (2,0) {};
\draw (A) \foreach \nd in {B,C} { -- (\nd) };
\end{tikzpicture}
\end{document}
更新:Qrrbrbirlbelは(現在は削除されたコメントで) の使用にはまだ問題があると指摘しto
、githubの問題はforeach の座標 [turn] オプションでも同じ問題が発生しますturn
。これらをすべて修正し、保存する必要のある項目の追加を少し簡単にするコードを以下に示します。
\documentclass{article}
%\url{https://tex.stackexchange.com/q/706591/86}
\usepackage{tikz}
\makeatletter
\def\pgffor@smugglers@cove{}
\let\tikztostart=\relax
\def\pgffor@smuggle@macro#1{%
\pgfutil@ifx#1\relax{}{%
\expandafter
\pgfutil@g@addto@macro
\expandafter
\pgffor@smugglers@cove
\expandafter
{%
\expandafter\def\expandafter#1\expandafter{#1}%
}%
}%
}
\def\pgffor@smuggle@dimen#1{%
\pgfutil@ifx#1\relax{}{%
\expandafter
\pgfutil@g@addto@macro
\expandafter
\pgffor@smugglers@cove
\expandafter
{%
\expandafter#1\expandafter=\the#1\relax%
}%
}%
}
\def\pgffor@smugglers@loot{%
%
\pgffor@smuggle@macro\tikz@moveto@waiting%
\pgffor@smuggle@macro\tikztostart%
\pgffor@smuggle@macro\tikz@tangent%
%
\pgffor@smuggle@dimen\tikz@lastx%
\pgffor@smuggle@dimen\tikz@lasty%
\pgffor@smuggle@dimen\tikz@lastxsaved%
\pgffor@smuggle@dimen\tikz@lastysaved%
%
}
\def\tikz@patched@foreach{%
\def\pgffor@beginhook{%
\pgffor@smugglers@cove
\gdef\pgffor@smugglers@cove{}%
\setbox\tikz@figbox=\box\tikz@tempbox%
\setbox\tikz@figbox@bg=\box\tikz@tempbox@bg%
\expandafter\tikz@scan@next@command\pgfutil@firstofone}%
\def\pgffor@endhook{\pgfextra{%
\pgffor@smugglers@loot%
\global\setbox\tikz@tempbox=\box\tikz@figbox%
\global\setbox\tikz@tempbox@bg=\box\tikz@figbox@bg%
\pgfutil@gobble}}%
\def\pgffor@afterhook{%
\pgffor@smugglers@cove
\gdef\pgffor@smugglers@cove{}%
\let\pgffor@beginhook\relax%
\let\pgffor@endhook\relax%
\let\pgffor@afterhook\relax%
\setbox\tikz@figbox=\box\tikz@tempbox%
\setbox\tikz@figbox@bg=\box\tikz@tempbox@bg%
\tikz@scan@next@command}%
\global\setbox\tikz@tempbox=\box\tikz@figbox%
\global\setbox\tikz@tempbox@bg=\box\tikz@figbox@bg%
\pgffor@smugglers@loot%
\foreach}%
\DeclareDocumentCommand \FixForeach {}
{
\let\tikz@foreach=\tikz@patched@foreach
}
\makeatother
\begin{document}
\begin{tabular}{rl}
Paths: &
\begin{tikzpicture}
\node[draw] (A) at (0,0) {};
\node[draw] (B) at (1,1) {};
\node[draw] (C) at (2,0) {};
\draw (A) -- (B) -- (C);
\end{tikzpicture} \\
%
Foreach Paths: &
\begin{tikzpicture}
\node[draw] (A) at (0,0) {};
\node[draw] (B) at (1,1) {};
\node[draw] (C) at (2,0) {};
\draw (A) \foreach \nd in {B,C} { -- (\nd) };
\end{tikzpicture} \\
%
Tos: &
\begin{tikzpicture}
\node[draw] (A) at (0,0) {};
\node[draw] (B) at (1,1) {};
\node[draw] (C) at (2,0) {};
\draw (A) to (B) to (C);
\end{tikzpicture} \\
%
Foreach Tos: &
\begin{tikzpicture}
\node[draw] (A) at (0,0) {};
\node[draw] (B) at (1,1) {};
\node[draw] (C) at (2,0) {};
\draw (A) \foreach \nd in {B,C} { to (\nd) };
\end{tikzpicture} \\
%
Turns: &
\begin{tikzpicture}
\draw[red] (0,0) -- (1,.2) -- ([turn]90:1cm) -- ([turn]90:1cm) -- ([turn]90:1cm) -- ([turn]90:1cm);
\end{tikzpicture} \\
%
Foreach Turns: &
\begin{tikzpicture}
\draw (0,0) -- (1,.2) foreach \i in {1,2,3,4} { -- ([turn]90:1cm) };
\end{tikzpicture} \\
%
Fixed Foreach Paths: &
\begin{tikzpicture}
\FixForeach
\node[draw] (A) at (0,0) {};
\node[draw] (B) at (1,1) {};
\node[draw] (C) at (2,0) {};
\draw (A) \foreach \nd in {B,C} { -- (\nd) };
\end{tikzpicture} \\
%
Fixed Foreach Tos: &
\begin{tikzpicture}
\FixForeach
\node[draw] (A) at (0,0) {};
\node[draw] (B) at (1,1) {};
\node[draw] (C) at (2,0) {};
\draw (A) \foreach \nd in {B,C} { to (\nd) };
\end{tikzpicture} \\
%
Fixed Foreach Turns: &
\begin{tikzpicture}
\FixForeach
\draw (0,0) -- (1,.2) foreach \i in {1,2,3,4} { -- ([turn]90:1cm) };
\end{tikzpicture}
\end{tabular}
\end{document}
答え2
免責事項:これは、何らかの問題を解決するという意味での本当の答えではなく、なぜこの問題を質問よりも正確に説明します (コード分析なし)。
あなたの使用法では\foreach
、次の線分の参照点は変更されないので、「(Y)への線のみ」を描画するのではなく、代わりに、、、の3つの線を描画し(X) -- (nA)
ます(nA) -- (nB)
。(nB) -- (Y)
は、ノードの衝突回避のためのポイントです(Tiの正しい用語がわかりません)。けZ/ はpgf
再計算されないので、 の左下端から(nA)
の左下端まで(nB)
半円を横切って描画することになります。
それでも、\foreach
描画の内側には奇妙な副作用がある場合があります。開始点と終了点がcoordinate
s ではなくnode
s でもある場合、次の行の開始の参照点はまったく移動されず、開始点が同じ 3 本の線が残ります。
全体的に、3 回目の使用は避けたほうがよいでしょう。結果がどうなるかは予測できないようです。ただし、これがバグであるかどうかはわかりません。
\documentclass{article}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}[mynode/.style={minimum size=4.3pt, inner sep=0pt, draw=red}]
\path
(0,0) node[mynode](X){}
(1,0) node[mynode](nA){}
(1,1) node[mynode](nB){}
(1,2) node[mynode](Y){}
;
% Case (3)
\draw (X) \foreach \x in {nA,nB,Y} { -- (\x)};
\path
(2,0) coordinate(X)
(3,0) node[mynode](nA){}
(3,1) node[mynode](nB){}
(3,2) coordinate(Y)
;
% Case (3)
\draw (X) \foreach \x in {nA,nB,Y} { -- (\x)};
\path
(4,0) coordinate(X)
(5,0) coordinate(nA)
(5,1) coordinate(nB)
(5,2) coordinate(Y)
;
\draw (X) \foreach \x in {nA,nB,Y} { -- (\x)};
\end{tikzpicture}
\end{document}
あなたにはこれに対する強引な解決策があるので、私はもっときれいな解決策(Tiを使わない)を提供できるかもしれません。けZ 以外の手段もあります。以下では、座標ペアのリストを指定しなくても正しい結果が得られます。
\documentclass[border=3.14,tikz]{standalone}
\ExplSyntaxOn
\cs_new_protected:Npn \tocoordinatelist { \exp:w \__tocoordinatelist:w }
\NewExpandableDocumentCommand \__tocoordinatelist:w { O{--} m }
{ \exp_end: #1 ( \clist_use:nn {#2} { ) #1 ( }) }
\ExplSyntaxOff
\begin{document}
\begin{tikzpicture}[mynode/.style={minimum size=4.3pt, inner sep=0pt, draw=red}]
\path
(0,0) coordinate(X)
(1,0) node[mynode](nA){}
(1,1) node[mynode](nB){}
(1,2) coordinate(Y)
;
\draw (X) \tocoordinatelist{nA,nB,Y};
\end{tikzpicture}
\end{document}