TikZ:ストロークを拡大
私はいくつかのスパイラルを描くことを試みており、数日前にこの質問をしました。hpekristiansenの答えは素晴らしく、大いに役立ちますが、写真を見るとスパイラルが右利きか左利きかが明確ではないため、私の希望する状況では少し奇妙に見えます。今日、hpekristiansenはこのトピックについて自分で質問し、TikZlingから非常に役立つ回答を得ました。私は特に、\foreachループを使用して個々のセグメントを描画する答えが好きです。残りの問題は、doubleパスオプションが白以外の背景に表示されるため、または私のユースケースのようにスパイラルを囲むロッドを使用できないことです。
この問題の解決策は、奇数のパス(3番目のパスから開始)を偶数のパスと交差する場所でクリップすることです。残念ながら、\path [clip]in Ti k Zはパスの中心のみを使用して何かをクリップし、完全にクリップされる線幅を設定するオプションはありません。そのため、AdobeIllustratorやAffinityDesignerなどのベクターグラフィックソフトウェアで可能なように、指定された線幅のパスを図形に拡張できるかどうか疑問に思いました。
いくつかのセクション(ループの左側とループの右側)でスパイラルを描画する場合、これにより、次の例のようなコードを使用できます。
\documentclass[tikz]{standalone}
\begin{document}
\begin{tikzpicture}[even odd rule]
\newcommand{\radiusX}{0.7}
\newcommand{\radiusY}{1.5}
\newcommand{\strokeWidth}{0.1}
\newcommand{\strokeWidthExtra}{0.1}
\newcommand{\background}{({-\radiusX-1},-1) rectangle ({8+\radiusX+1},{2*\radiusY+1})}
\newcommand{\leftArc}{
(0.5, 0)
-- (0, 0)
arc (-90:-270:{\radiusX} and {\radiusY})
-- ++(0, -\strokeWidth)
arc (90:270:{\radiusX-\strokeWidth} and {\radiusY-\strokeWidth})
-- ++(0.5,0)
-- ++(0,-\strokeWidth)
-- cycle
}
\newcommand{\leftArcBig}{
({0.5+\strokeWidthExtra}, -\strokeWidthExtra)
-- ++({-0.5-\strokeWidthExtra}, 0)
arc (-90:-270:{\radiusX+\strokeWidthExtra} and {\radiusY+\strokeWidthExtra})
-- ++(0, {-\strokeWidth-2*\strokeWidthExtra})
arc (90:270:{\radiusX-\strokeWidth-\strokeWidthExtra} and {\radiusY-\strokeWidth-\strokeWidthExtra})
-- ++({0.5+\strokeWidthExtra},0)
-- ++(0,{-\strokeWidth+2*\strokeWidthExtra})
-- cycle
}
\newcommand{\rightArc}{
(-0.5,0)
-- (0,0)
arc (-90:90:{\radiusX} and {\radiusY})
-- ++(0,-\strokeWidth)
arc (90:-90:{\radiusX-\strokeWidth} and {\radiusY-\strokeWidth})
-- ++(-0.5,0)
-- ++(0,-{\strokeWidth})
-- cycle
}
\newcommand{\rightArcBig}{
(-{0.5-\strokeWidthExtra},-{\strokeWidthExtra})
-- ++({0.5+\strokeWidthExtra},0)
arc (-90:90:{\radiusX+\strokeWidthExtra} and {\radiusY+\strokeWidthExtra})
-- ++(0,{-\strokeWidth-2*\strokeWidthExtra})
arc (90:-90:{\radiusX-\strokeWidth-\strokeWidthExtra} and {\radiusY-\strokeWidth-\strokeWidthExtra})
-- ++({-0.5-\strokeWidthExtra},0)
-- ++(0,{-\strokeWidth-2*\strokeWidthExtra})
-- cycle
}
\shade[clip, top color = gray, bottom color = lightgray] \background;
\begin{scope}
\fill [black] \rightArc;
\clip \rightArcBig \background;
\fill [black] \leftArc;
\end{scope}
\begin{scope}[xshift = 2cm]
\fill [yellow] \rightArc;
\fill [yellow, fill opacity = 0.3] \rightArcBig;
\fill [red] \leftArc;
\fill [red, fill opacity = 0.3] \leftArcBig;
\end{scope}
\begin{scope}[xshift = 6cm]
\fill [black] \leftArc;
\clip \leftArcBig \background;
\fill [black] \rightArc;
\end{scope}
\begin{scope}[xshift = 8cm]
\fill [yellow] \leftArc;
\fill [yellow, fill opacity = 0.3] \leftArcBig;
\fill [red] \rightArc;
\fill [red, fill opacity = 0.3] \rightArcBig;
\end{scope}
\end{tikzpicture}
\end{document}
回答
本当に答えではありません。パスのエンベロープを構築する方法があるかどうかを尋ねています。答えは、これを実現するための組み込みの方法や簡単な方法はないということです。さらに悪いことに、単純で一般的な方法がないという分析的証明があります。証明を理解するために、すべてのTiことを思い出すのk行うことができますZは、ベジェ曲線を構築することです。これは、それほど単純ではない方法がないことを示しているわけではないことに注意してください。実際、MetaPostとその友人たちがそのためのルーチンを持っているという事実は、それが原則的に可能であることを示しています。
それを行うことができる別のツールはビューアです。OK、視聴者に汚い仕事をさせましょう。これにより、別の方法で問題を解決できます。これは、概念的にはこの投稿と同じです:フェージング。あまり便利ではありません。少なくとも次の実装ではありませんが、原則の証明です。基本的に、グレーレベルを透明度に変換して、黒または白の線を透明にすることができます。このオブジェクトは、任意の背景の上に置くことができます。(この実装は便利ではないことをすでに述べましたか?)
\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{decorations.pathreplacing,fadings}%
\begin{document}
\begin{tikzfadingfrompicture}[name=custom fade]%
\tikzset{path decomposition/.style={%
postaction={decoration={show path construction,
lineto code={
\draw[#1] (\tikzinputsegmentfirst) -- (\tikzinputsegmentlast);
},
curveto code={
\draw[#1] (\tikzinputsegmentfirst) .. controls
(\tikzinputsegmentsupporta) and (\tikzinputsegmentsupportb)
..(\tikzinputsegmentlast) ;
},
closepath code={
\draw[#1] (\tikzinputsegmentfirst) -- (\tikzinputsegmentlast) {closepath};} }
,decorate}},
cv/.style={black, double=white,line width=0.6mm,double distance=1.2mm}}
\draw[cv,samples=201,domain=-2*pi:2*pi,smooth,
path decomposition={cv,shorten <=-0.05pt,shorten >=-0.05pt}]
plot (\x, {cos(10*\x r)} , {sin(10*\x r)} );
\end{tikzfadingfrompicture}%
\begin{tikzpicture}
\shade[clip, top color = gray!50!black, bottom color = gray!10]
(0,-2) rectangle (6,2);
\path[path fading=custom fade,fit fading=false,
fill=black] (0,-2) rectangle (8,2);
\end{tikzpicture}
\end{document}
偶然にも、私はあなたを助けるかもしれないいくつかのコードに取り組んできました。交点でパスを分割するように設計されています。
これは、パスが定義された後、修正される前にパスを操作するための構造を提供する私のspath3(ctanおよびgithub)ライブラリに基づいています。
これは間違いなく実験的なコードであり、変更される可能性がありますが、それが理にかなっているかどうか、何が役立つかについてフィードバックを得ると便利です。
\documentclass{article}
\usepackage{xparse}
\usepackage{tikz}
\usepackage{spath3}
\usetikzlibrary{intersections,hobby,patterns}
\ExplSyntaxOn
\tikzset{
append~ spath/.code={
\spath_get_current_path:n {current path}
\spath_append:nn { current path } { #1 }
\spath_set_current_path:n { current path }
},
set~ spath/.code={
\spath_set_current_path:n { #1 }
\spath_get:nnN {#1} {final point} \l__spath_tmpa_tl
\tl_set:Nx \l__spath_tmpa_tl
{
\exp_not:c {tikz@lastx}=\tl_item:Nn \l__spath_tmpa_tl {1}
\exp_not:c {tikz@lasty}=\tl_item:Nn \l__spath_tmpa_tl {2}
\exp_not:c {tikz@lastxsaved}=\tl_item:Nn \l__spath_tmpa_tl {1}
\exp_not:c {tikz@lastysaved}=\tl_item:Nn \l__spath_tmpa_tl {2}
}
\tl_use:N \l__spath_tmpa_tl
},
shorten~spath~at~end/.code~ 2~ args={
\spath_shorten:nn {#1} {#2}
},
shorten~spath~at~start/.code~ 2~ args ={
\spath_reverse:n {#1}
\spath_shorten:nn {#1} {#2}
\spath_reverse:n {#1}
},
shorten~spath~both~ends/.code~ 2~ args={
\spath_shorten:nn {#1} {#2}
\spath_reverse:n {#1}
\spath_shorten:nn {#1} {#2}
\spath_reverse:n {#1}
},
globalise~ spath/.code={
\spath_globalise:n {#1}
},
translate~ spath/.code~ n~ args={3}{
\spath_translate:nnn {#1}{#2}{#3}
},
split~ at~ self~ intersections/.code~ 2~ args={
\use:c {tikz@addmode}{
\group_begin:
\spath_get_current_path:n {spath split tmpa}
\spath_split_at_self_intersections:nnn {spath split tmpa} {#1} {#2}
\group_end:
}
},
split~ at~ intersections/.code~ n~ args={5}{
\spath_split_at_intersections:nnnnn {#1}{#2}{#3}{#4}{#5}
}
}
\tl_new:N \l__spath_shorten_fa_tl
\tl_new:N \l__spath_shorten_path_tl
\tl_new:N \l__spath_shorten_last_tl
\int_new:N \l__spath_shorten_int
\fp_new:N \l__spath_shorten_x_fp
\fp_new:N \l__spath_shorten_y_fp
\cs_new_nopar:Npn \spath_shorten:nn #1#2
{
\group_begin:
\spath_get:nnN {#1} {final action} \l__spath_shorten_fa_tl
\spath_get:nnN {#1} {path} \l__spath_shorten_path_tl
\tl_reverse:N \l__spath_shorten_path_tl
\tl_clear:N \l__spath_shorten_last_tl
\tl_if_eq:NNTF \l__spath_shorten_fa_tl \g__spath_curveto_tl
{
\int_set:Nn \l__spath_shorten_int {3}
}
{
\int_set:Nn \l__spath_shorten_int {1}
}
\prg_replicate:nn { \l__spath_shorten_int }
{
\tl_put_right:Nx \l__spath_shorten_last_tl
{
{\tl_head:N \l__spath_shorten_path_tl}
}
\tl_set:Nx \l__spath_shorten_path_tl {\tl_tail:N \l__spath_shorten_path_tl}
\tl_put_right:Nx \l__spath_shorten_last_tl
{
{\tl_head:N \l__spath_shorten_path_tl}
}
\tl_set:Nx \l__spath_shorten_path_tl {\tl_tail:N \l__spath_shorten_path_tl}
\tl_put_right:Nx \l__spath_shorten_last_tl
{
\tl_head:N \l__spath_shorten_path_tl
}
\tl_set:Nx \l__spath_shorten_path_tl {\tl_tail:N \l__spath_shorten_path_tl}
}
\tl_put_right:Nx \l__spath_shorten_last_tl
{
{\tl_item:Nn \l__spath_shorten_path_tl {1}}
{\tl_item:Nn \l__spath_shorten_path_tl {2}}
}
\tl_put_right:NV \l__spath_shorten_last_tl \g__spath_moveto_tl
\tl_reverse:N \l__spath_shorten_path_tl
\fp_set:Nn \l__spath_shorten_x_fp
{
\dim_to_fp:n {\tl_item:Nn \l__spath_shorten_last_tl {4}}
-
\dim_to_fp:n {\tl_item:Nn \l__spath_shorten_last_tl {1}}
}
\fp_set:Nn \l__spath_shorten_y_fp
{
\dim_to_fp:n {\tl_item:Nn \l__spath_shorten_last_tl {5}}
-
\dim_to_fp:n {\tl_item:Nn \l__spath_shorten_last_tl {2}}
}
\fp_set:Nn \l__spath_shorten_len_fp
{
sqrt( \l__spath_shorten_x_fp * \l__spath_shorten_x_fp + \l__spath_shorten_y_fp * \l__spath_shorten_y_fp )
}
\fp_set:Nn \l__spath_shorten_len_fp
{
(\l__spath_shorten_len_fp - #2)/ \l__spath_shorten_len_fp
}
\tl_reverse:N \l__spath_shorten_last_tl
\tl_if_eq:NNTF \l__spath_shorten_fa_tl \g__spath_curveto_tl
{
\fp_set:Nn \l__spath_shorten_len_fp
{
1 - (1 -\l__spath_shorten_len_fp)/3
}
\spath_split_curve:VVNN \l__spath_shorten_len_fp \l__spath_shorten_last_tl
\l__spath_shorten_lasta_tl
\l__spath_shorten_lastb_tl
}
{
\spath_split_line:VVNN \l__spath_shorten_len_fp \l__spath_shorten_last_tl
\l__spath_shorten_lasta_tl
\l__spath_shorten_lastb_tl
}
\prg_replicate:nn {3}
{
\tl_set:Nx \l__spath_shorten_lasta_tl {\tl_tail:N \l__spath_shorten_lasta_tl}
}
\tl_put_right:NV \l__spath_shorten_path_tl \l__spath_shorten_lasta_tl
\tl_gset_eq:NN \l__spath_smuggle_tl \l__spath_shorten_path_tl
\group_end:
\spath_clear:n {#1}
\spath_put:nnV {#1} {path} \l__spath_smuggle_tl
}
\cs_generate_variant:Nn \spath_shorten:nn {Vn, VV}
\cs_generate_variant:Nn \spath_reverse:n {V}
\cs_generate_variant:Nn \spath_append_no_move:nn {VV}
\cs_generate_variant:Nn \spath_prepend_no_move:nn {VV}
\cs_new_nopar:Npn \spath_intersect:nn #1#2
{
\spath_get:nnN {#1} {path} \l__spath_tmpa_tl
\spath_get:nnN {#2} {path} \l__spath_tmpb_tl
\pgfintersectionofpaths%
{%
\pgfsetpath\l__spath_tmpa_tl
}{%
\pgfsetpath\l__spath_tmpb_tl
}
}
\cs_generate_variant:Nn \spath_intersect:nn {VV, Vn}
\cs_new_nopar:Npn \spath_split_line:nnNN #1#2#3#4
{
\group_begin:
\tl_gclear:N \l__spath_smuggle_tl
\tl_set_eq:NN \l__spath_tmpa_tl \g__spath_moveto_tl
\tl_put_right:Nx \l__spath_tmpa_tl {
{\tl_item:nn {#2} {2}}
{\tl_item:nn {#2} {3}}
}
\tl_put_right:NV \l__spath_tmpa_tl \g__spath_lineto_tl
\tl_put_right:Nx \l__spath_tmpa_tl
{
{\fp_to_dim:n
{
(1 - #1) * \tl_item:nn {#2} {2} + (#1) * \tl_item:nn {#2} {5}
}}
{\fp_to_dim:n
{
(1 - #1) * \tl_item:nn {#2} {3} + (#1) * \tl_item:nn {#2} {6}
}}
}
\tl_gset_eq:NN \l__spath_smuggle_tl \l__spath_tmpa_tl
\group_end:
\tl_set_eq:NN #3 \l__spath_smuggle_tl
\group_begin:
\tl_gclear:N \l__spath_smuggle_tl
\tl_set_eq:NN \l__spath_tmpa_tl \g__spath_moveto_tl
\tl_put_right:Nx \l__spath_tmpa_tl
{
{\fp_to_dim:n
{
(1 - #1) * \tl_item:nn {#2} {2} + (#1) * \tl_item:nn {#2} {5}
}}
{\fp_to_dim:n
{
(1 - #1) * \tl_item:nn {#2} {3} + (#1) * \tl_item:nn {#2} {6}
}}
}
\tl_put_right:NV \l__spath_tmpa_tl \g__spath_lineto_tl
\tl_put_right:Nx \l__spath_tmpa_tl {
{\tl_item:nn {#2} {5}}
{\tl_item:nn {#2} {6}}
}
\tl_gset_eq:NN \l__spath_smuggle_tl \l__spath_tmpa_tl
\group_end:
\tl_set_eq:NN #4 \l__spath_smuggle_tl
}
\cs_generate_variant:Nn \spath_split_line:nnNN {nVNN, VVNN}
\int_new:N \l__spath_split_int
\int_new:N \l__spath_splitat_int
\fp_new:N \l__spath_split_fp
\bool_new:N \l__spath_split_bool
\tl_new:N \l__spath_split_path_tl
\tl_new:N \l__spath_split_patha_tl
\tl_new:N \l__spath_split_pathb_tl
\tl_new:N \l__spath_split_intoa_tl
\tl_new:N \l__spath_split_intob_tl
\dim_new:N \l__spath_splitx_dim
\dim_new:N \l__spath_splity_dim
\cs_new_nopar:Npn \spath_split_at:nnnn #1#2#3#4
{
\group_begin:
\int_set:Nn \l__spath_splitat_int {\fp_to_int:n {floor(#2) + 1}}
\fp_set:Nn \l__spath_split_fp {#2 - floor(#2)}
\int_zero:N \l__spath_split_int
\bool_set_true:N \l__spath_split_bool
\spath_get:nnN {#1} {path} \l__spath_split_path_tl
\tl_clear:N \l__spath_split_patha_tl
\dim_zero:N \l__spath_splitx_dim
\dim_zero:N \l__spath_splity_dim
\bool_until_do:nn {
\tl_if_empty_p:N \l__spath_split_path_tl
||
\int_compare_p:n { \l__spath_splitat_int == \l__spath_split_int }
}
{
\tl_set:Nx \l__spath_tmpc_tl {\tl_head:N \l__spath_split_path_tl}
\tl_set:Nx \l__spath_split_path_tl {\tl_tail:N \l__spath_split_path_tl }
\tl_case:Nn \l__spath_tmpc_tl
{
\g__spath_lineto_tl
{
\int_incr:N \l__spath_split_int
}
\g__spath_curvetoa_tl
{
\int_incr:N \l__spath_split_int
}
}
\int_compare:nT { \l__spath_split_int < \l__spath_splitat_int }
{
\tl_put_right:NV \l__spath_split_patha_tl \l__spath_tmpc_tl
\tl_put_right:Nx \l__spath_split_patha_tl
{{ \tl_head:N \l__spath_split_path_tl }}
\dim_set:Nn \l__spath_splitx_dim {\tl_head:N \l__spath_split_path_tl}
\tl_set:Nx \l__spath_split_path_tl {\tl_tail:N \l__spath_split_path_tl }
\tl_put_right:Nx \l__spath_split_patha_tl
{{ \tl_head:N \l__spath_split_path_tl }}
\dim_set:Nn \l__spath_splity_dim {\tl_head:N \l__spath_split_path_tl}
\tl_set:Nx \l__spath_split_path_tl {\tl_tail:N \l__spath_split_path_tl }
}
}
\tl_clear:N \l__spath_split_pathb_tl
\tl_put_right:NV \l__spath_split_pathb_tl \g__spath_moveto_tl
\tl_put_right:Nx \l__spath_split_pathb_tl
{
{\dim_use:N \l__spath_splitx_dim}
{\dim_use:N \l__spath_splity_dim}
}
\tl_case:Nn \l__spath_tmpc_tl
{
\g__spath_lineto_tl
{
\tl_put_right:NV \l__spath_split_pathb_tl \l__spath_tmpc_tl
\tl_put_right:Nx \l__spath_split_pathb_tl
{{ \tl_head:N \l__spath_split_path_tl }}
\tl_set:Nx \l__spath_split_path_tl {\tl_tail:N \l__spath_split_path_tl }
\tl_put_right:Nx \l__spath_split_pathb_tl
{{ \tl_head:N \l__spath_split_path_tl }}
\tl_set:Nx \l__spath_split_path_tl {\tl_tail:N \l__spath_split_path_tl }
\spath_split_line:VVNN \l__spath_split_fp \l__spath_split_pathb_tl
\l__spath_split_intoa_tl
\l__spath_split_intob_tl
\prg_replicate:nn {3} {
\tl_set:Nx \l__spath_split_intoa_tl {\tl_tail:N \l__spath_split_intoa_tl}
}
\tl_put_right:NV \l__spath_split_patha_tl \l__spath_split_intoa_tl
\tl_put_right:NV \l__spath_split_intob_tl \l__spath_split_path_tl
}
\g__spath_curvetoa_tl
{
\tl_put_right:NV \l__spath_split_pathb_tl \l__spath_tmpc_tl
\tl_put_right:Nx \l__spath_split_pathb_tl
{{ \tl_head:N \l__spath_split_path_tl }}
\tl_set:Nx \l__spath_split_path_tl {\tl_tail:N \l__spath_split_path_tl }
\tl_put_right:Nx \l__spath_split_pathb_tl
{{ \tl_head:N \l__spath_split_path_tl }}
\tl_set:Nx \l__spath_split_path_tl {\tl_tail:N \l__spath_split_path_tl }
\prg_replicate:nn {2} {
\tl_put_right:Nx \l__spath_split_pathb_tl
{ \tl_head:N \l__spath_split_path_tl }
\tl_set:Nx \l__spath_split_path_tl {\tl_tail:N \l__spath_split_path_tl }
\tl_put_right:Nx \l__spath_split_pathb_tl
{{ \tl_head:N \l__spath_split_path_tl }}
\tl_set:Nx \l__spath_split_path_tl {\tl_tail:N \l__spath_split_path_tl }
\tl_put_right:Nx \l__spath_split_pathb_tl
{{ \tl_head:N \l__spath_split_path_tl }}
\tl_set:Nx \l__spath_split_path_tl {\tl_tail:N \l__spath_split_path_tl }
}
\spath_split_curve:VVNN \l__spath_split_fp \l__spath_split_pathb_tl
\l__spath_split_intoa_tl
\l__spath_split_intob_tl
\prg_replicate:nn {3} {
\tl_set:Nx \l__spath_split_intoa_tl {\tl_tail:N \l__spath_split_intoa_tl}
}
\tl_put_right:NV \l__spath_split_patha_tl \l__spath_split_intoa_tl
\tl_put_right:NV \l__spath_split_intob_tl \l__spath_split_path_tl
}
}
\spath_gclear_new:n {#3}
\spath_gput:nnV {#3} {path} \l__spath_split_patha_tl
\spath_gclear_new:n {#4}
\spath_gput:nnV {#4} {path} \l__spath_split_intob_tl
\group_end:
}
\cs_generate_variant:Nn \spath_split_at:nnnn {VVnn, Vnnn}
\cs_new_nopar:Npn \spath_explode_into_list:nn #1#2
{
\tl_clear_new:c {l__spath_list_#2}
\int_zero:N \l__spath_tmpa_int
\spath_map_segment_inline:nn {#1} {
\tl_if_eq:NNF ##1 \g__spath_moveto_tl
{
\spath_clear_new:n {#2 _ \int_use:N \l__spath_tmpa_int}
\spath_put:nnV {#2 _ \int_use:N \l__spath_tmpa_int} {path} ##2
\tl_put_right:cx {l__spath_list_#2} {{#2 _ \int_use:N \l__spath_tmpa_int}}
\int_incr:N \l__spath_tmpa_int
}
}
}
\tl_new:N \spathselfintersectioncount
\tl_new:N \l__spath_split_tmpa_tl
\tl_new:N \l__spath_split_path_a_tl
\tl_new:N \l__spath_split_path_b_tl
\tl_new:N \l__spath_split_join_a_tl
\tl_new:N \l__spath_split_join_b_tl
\tl_new:N \l__spath_split_first_tl
\tl_new:N \l__spath_split_second_tl
\tl_new:N \l__spath_split_one_tl
\tl_set:Nn \l__spath_split_one_tl {1}
\tl_new:N \l__spath_split_I_tl
\tl_set:Nn \l__spath_split_I_tl {I}
\int_new:N \l__spath_split_count_int
\int_new:N \l__spath_split_intersection_int
\seq_new:N \l__spath_split_segments_seq
\seq_new:N \l__spath_split_segments_processed_seq
\seq_new:N \l__spath_split_segments_middle_seq
\seq_new:N \l__spath_split_joins_seq
\seq_new:N \l__spath_split_joins_processed_seq
\seq_new:N \l__spath_split_joins_middle_seq
\seq_new:N \l__spath_split_intersections_seq
\bool_new:N \l__spath_split_join_bool
% We'll run this on each segment
%
% Arguments:
% 1. Path to split
% 2. Prefix for name of new paths
% 3. List of how to split at intersections
% A - don't split first path at intersection
% B - don't split second path at intersection
% C - split both paths at intersection
%
\cs_new_nopar:Npn \spath_split_at_self_intersections:nnn #1#2#3
{
\group_begin:
% The third argument says whether to rejoin segments at the intersections
\seq_set_split:Nnn \l__spath_split_intersections_seq {} {#3}
% Clone the path as we'll mess around with it
\spath_clone:nn {#1} {spath split tmp}
% Clear the sequence of joining information
% The join information says whether to rejoin a segment to its predecessor
\seq_clear:N \l__spath_split_joins_seq
% Check the last action to see if it is a close path
\spath_get:nnN {spath split tmp} {final action} \l__spath_split_tmpa_tl
\tl_if_eq:NNTF \l__spath_split_tmpa_tl \g__spath_closepath_tl
{
% Last action is a close, so mark it as needing rejoining
\seq_put_right:Nn \l__spath_split_joins_seq {1}
}
{
% Last action is not a close, so mark it as needing rejoining
\seq_put_right:Nn \l__spath_split_joins_seq {0}
}
% Remove close paths
\spath_open_path:n {spath split tmp}
% Separate into segments (creates a token list)
\spath_explode_into_list:nn {spath split tmp}{split segments}
% so convert to a sequence
\seq_set_split:NnV \l__spath_split_segments_seq {} \l__spath_list_splitsegments
% Iterate over the number of terms in the sequence, adding the
% rejoining information
\int_step_inline:nnnn {1} {1} {\seq_count:N \l__spath_split_segments_seq - 1}
{
\seq_put_right:Nn \l__spath_split_joins_seq {1}
}
% Clear a couple of auxiliaries
\seq_clear:N \l__spath_split_segments_processed_seq
\seq_clear:N \l__spath_split_joins_processed_seq
\int_zero:N \l__spath_split_count_int
\int_zero:N \l__spath_split_intersection_int
% Iterate over the sequence
\bool_while_do:nn
{
!\seq_if_empty_p:N \l__spath_split_segments_seq
}
{
% Remove the left-most items for consideration
\seq_pop_left:NN \l__spath_split_segments_seq \l__spath_split_path_a_tl
\seq_pop_left:NN \l__spath_split_joins_seq \l__spath_split_join_a_tl
% Clear some sequences, these will hold any pieces we create from splitting our path under consideration except for the first piece
\seq_clear:N \l__spath_split_segments_middle_seq
\seq_clear:N \l__spath_split_joins_middle_seq
% Put the rejoining information in the processed sequence
\seq_put_right:NV \l__spath_split_joins_processed_seq \l__spath_split_join_a_tl
% Iterate over the rest of the segments
\int_step_inline:nnnn {1} {1} {\seq_count:N \l__spath_split_segments_seq}
{
% Store the next segment for intersection
\tl_set:Nx \l__spath_split_path_b_tl {\seq_item:Nn \l__spath_split_segments_seq {##1}}
% Get the next joining information
\tl_set:Nx \l__spath_split_join_b_tl {\seq_item:Nn \l__spath_split_joins_seq {##1}}
% And put it onto our saved stack of joins
\seq_put_right:NV \l__spath_split_joins_middle_seq \l__spath_split_join_b_tl
% Sort intersections along the first path
\pgfintersectionsortbyfirstpath
% Find the intersections of these segments
\spath_intersect:VV \l__spath_split_path_a_tl \l__spath_split_path_b_tl
% If we get intersections
\int_compare:nTF {\pgfintersectionsolutions > 0}
{
% Find the times of the first intersection (which will be the first along the segment we're focussing on)
\pgfintersectiongetsolutiontimes{1}{\l__spath_split_first_tl}{\l__spath_split_second_tl}
% Ignore intersections that are very near end points
\bool_if:nT {
\fp_compare_p:n {
\l__spath_split_first_tl < .99
}
&&
\fp_compare_p:n {
\l__spath_split_first_tl > .01
}
&&
\fp_compare_p:n {
\l__spath_split_second_tl < .99
}
&&
\fp_compare_p:n {
\l__spath_split_second_tl > .01
}
}
{
% We have a genuine intersection
\int_incr:N \l__spath_split_intersection_int
}
% Do we split the first path?
\bool_if:nT {
\fp_compare_p:n {
\l__spath_split_first_tl < .99
}
&&
\fp_compare_p:n {
\l__spath_split_first_tl > .01
}
}
{
% Split the first path at the intersection
\spath_split_at:VVnn \l__spath_split_path_a_tl \l__spath_split_first_tl {split \int_use:N \l__spath_split_count_int}{split \int_eval:n { \l__spath_split_count_int + 1}}
% Put the latter part into our temporary sequence
\seq_put_left:Nx \l__spath_split_segments_middle_seq {split \int_eval:n{ \l__spath_split_count_int + 1}}
% Mark this intersection in the joining information
% Label the breaks as "IA#" and "IB#"
\seq_put_left:Nx \l__spath_split_joins_middle_seq {IA \int_use:N \l__spath_split_intersection_int }
% Replace our segment under consideration by the initial part
\tl_set:Nx \l__spath_split_path_a_tl {split \int_use:N \l__spath_split_count_int }
% Increment our counter
\int_incr:N \l__spath_split_count_int
\int_incr:N \l__spath_split_count_int
}
% Do we split the second path?
\bool_if:nTF {
\fp_compare_p:n {
\l__spath_split_second_tl < .99
}
&&
\fp_compare_p:n {
\l__spath_split_second_tl > .01
}
}
{
% Split the second segment at the intersection point
\spath_split_at:VVnn \l__spath_split_path_b_tl \l__spath_split_second_tl {split \int_use:N \l__spath_split_count_int}{split \int_eval:n { \l__spath_split_count_int + 1}}
% Add these segments to our list of segments we've considered
\seq_put_right:Nx \l__spath_split_segments_middle_seq {split \int_eval:n{ \l__spath_split_count_int}}
\seq_put_right:Nx \l__spath_split_segments_middle_seq {split \int_eval:n{ \l__spath_split_count_int + 1}}
\seq_put_right:Nx \l__spath_split_joins_middle_seq {IB \int_use:N \l__spath_split_intersection_int}
% Increment the counter
\int_incr:N \l__spath_split_count_int
\int_incr:N \l__spath_split_count_int
}
{
% If we didn't split the second segment, we just put the second segment on the list of segments we've considered
\seq_put_right:NV \l__spath_split_segments_middle_seq \l__spath_split_path_b_tl
}
}
{
% If we didn't split the second segment, we just put the second segment on the list of segments we've considered
\seq_put_right:NV \l__spath_split_segments_middle_seq \l__spath_split_path_b_tl
}
}
% Having been through the loop for our segment under consideration, we replace the segment list since some of them might have been split and add any remainders of the segment under consideration
\seq_set_eq:NN \l__spath_split_segments_seq \l__spath_split_segments_middle_seq
\seq_set_eq:NN \l__spath_split_joins_seq \l__spath_split_joins_middle_seq
% We add the initial segment to our sequence of dealt with segments
\seq_put_right:NV \l__spath_split_segments_processed_seq \l__spath_split_path_a_tl
}
\seq_clear:N \l__spath_split_segments_seq
\tl_set:Nx \l__spath_split_path_a_tl {\seq_item:Nn \l__spath_split_segments_processed_seq {1}}
\int_step_inline:nnnn {2} {1} {\seq_count:N \l__spath_split_segments_processed_seq}
{
% Get the next path and joining information
\tl_set:Nx \l__spath_split_path_b_tl {\seq_item:Nn \l__spath_split_segments_processed_seq {##1}}
\tl_set:Nx \l__spath_split_join_b_tl {\seq_item:Nn \l__spath_split_joins_processed_seq {##1}}
% Do we join this to our previous path?
\bool_set_false:N \l__spath_split_join_bool
% If it came from when we split the original path, join them
\tl_if_eq:NNT \l__spath_split_join_b_tl \l__spath_split_one_tl
{
\bool_set_true:N \l__spath_split_join_bool
}
% Is this a labelled intersection?
\tl_set:Nx \l__spath_split_tmpa_tl {\tl_head:N \l__spath_split_join_b_tl}
\tl_if_eq:NNT \l__spath_split_tmpa_tl \l__spath_split_I_tl
{
% Strip off the "I" prefix
\tl_set:Nx \l__spath_split_tmpa_tl {\tl_tail:N \l__spath_split_join_b_tl}
% Next letter is "A" or "B"
\tl_set:Nx \l__spath_split_join_b_tl {\tl_head:N \l__spath_split_tmpa_tl}
% Remainder is the intersection index
\int_compare:nTF {\tl_tail:N \l__spath_split_tmpa_tl <= \seq_count:N \l__spath_split_intersections_seq}
{
\tl_set:Nx \l__spath_split_join_a_tl {\seq_item:Nn \l__spath_split_intersections_seq {\tl_tail:N \l__spath_split_tmpa_tl}}
}
{
% Default is to rejoin neither segment
\tl_set:Nn \l__spath_split_join_a_tl {C}
}
\tl_if_eq:NNT \l__spath_split_join_a_tl \l__spath_split_join_b_tl
{
\bool_set_true:N \l__spath_split_join_bool
}
}
\bool_if:NTF \l__spath_split_join_bool
{
% Yes, so append it
\spath_append_no_move:VV \l__spath_split_path_a_tl \l__spath_split_path_b_tl
}
{
% No, so put the first path onto the stack
\seq_put_right:NV \l__spath_split_segments_seq \l__spath_split_path_a_tl
% Swap out the paths
\tl_set_eq:NN \l__spath_split_path_a_tl \l__spath_split_path_b_tl
}
}
% Do we need to add the first path to the last?
\tl_set:Nx \l__spath_split_join_a_tl {\seq_item:Nn \l__spath_split_joins_processed_seq {1}}
\tl_if_eq:NNTF \l__spath_split_join_a_tl \l__spath_split_one_tl
{
\tl_set:Nx \l__spath_split_path_b_tl {\seq_item:Nn \l__spath_split_segments_processed_seq {1}}
\spath_prepend_no_move:VV \l__spath_split_path_b_tl \l__spath_split_path_a_tl
}
{
\seq_put_right:NV \l__spath_split_segments_seq \l__spath_split_path_a_tl
}
% Put our paths into a list
\int_zero:N \l__spath_split_count_int
\seq_map_inline:Nn \l__spath_split_segments_seq
{
\int_incr:N \l__spath_split_count_int
\spath_gclone:nn {##1} {#2~\int_use:N \l__spath_split_count_int}
}
\tl_gset:NV \spathselfintersectioncount \l__spath_split_count_int
\group_end:
}
\ExplSyntaxOff
\begin{document}
\begin{tikzpicture}[use Hobby shortcut]
\shade[left color=cyan, right color=magenta, shading angle=90] (-.5,-.2) rectangle (7.5,2.2);
\fill[pattern=bricks, pattern color=white] (-.5,-.2) rectangle (7.5,2.2);
\path
[
split at self intersections={coil}{AAAAAAAAAAAAAAAA}
] ([out angle=0]0,0)
.. +(.85,1) .. +(.25,2) .. +(-.35,1) .. ++(.5,0)
.. +(.85,1) .. +(.25,2) .. +(-.35,1) .. ++(.5,0)
.. +(.85,1) .. +(.25,2) .. +(-.35,1) .. ++([in angle=180].5,0)
;
\foreach \k in {1,..., \spathselfintersectioncount} {
\tikzset{shorten spath both ends={coil \k}{2pt}, globalise spath=coil \k}
}
\foreach \k in {1,..., 4} {
\draw[set spath=coil \k];
}
\foreach[evaluate=\l as \xshift using \l*.5cm] \l in {0,...,10} {
\foreach \k in {5,..., 9} {
\draw[translate spath={coil \k}{\xshift pt}{0pt},set spath=coil \k];
}
}
\draw[translate spath={coil 10}{5cm}{0pt},set spath=coil 10];
\end{tikzpicture}
\end{document}
明らかに、その大部分は最終的にspath3パッケージに組み込まれ、重要な部分はtikzpicture最後にあります。これが行うことは、基本的なパスを取り、それが自己交差する場所でそれを分割することです。次に、これらの部分を短くしてギャップを作成します。これらの部品は、コイルを作成するために(変換を使用して)再利用できます。結果は次の画像doubleです。背景には、ここでトリックが行われていないことが示されています。