tikz pgf

TikZ로 임의의 기하학적 그래프 그리기(가까운 임의의 노드 연결)

TikZ에서 임의의 기하학적 그래프를 생성하고 싶습니다. 일정한 양의 노드가 정사각형에 무작위로 던져지고 각 쌍은 거리가 최대 고정 수 r이면 연결됩니다. 임의의 노드를 생성할 수 있지만 연결하는 데 문제가 있습니다.

다음은 노드를 생성하는 코드입니다.

\begin{tikzpicture}
\foreach \x in {1,...,300}{
    % generate random position
    \pgfmathrandominteger{\a}{-490}{490}
    \pgfmathrandominteger{\b}{-490}{490}
    % draw and label
    \node (\x) at (\a*0.01,\b*0.01) {};
    \draw[fill] (\x) circle (1pt);      
};
\end{tikzpicture}

다음을 제공합니다.

이제 세그먼트로 연결된 거리가 <0.1인 각 쌍을 원한다고 가정해 보겠습니다. 그렇게하는 방법? 몇 가지 "if" 문을 가지고 놀아보았지만 구문을 올바르게 이해할 수 없습니다.



300개의 좌표를 사용하면 컴파일하는 데 시간이 오래 걸리지만 이론적으로 다음과 같이 할 수 있습니다(아이디어를 설명하기 위해 50개의 좌표만 사용했습니다).

\documentclass[border=10pt]{standalone}
\usepackage{tikz}

\begin{document}

\begin{tikzpicture}
\foreach \x in {1,...,50}{
    % generate random position
    \pgfmathrandominteger{\a}{-490}{490}
    \pgfmathrandominteger{\b}{-490}{490}
    % draw and label
    \coordinate (n\x) at (\a*0.01,\b*0.01);
    \draw[fill] (n\x) circle (1pt);      
};

\foreach \a in {1,...,50}{
    \path (n\a);
    \pgfgetlastxy{\ax}{\ay}
    \foreach \b in {\a,...,50}{
        \ifnum\a=\b\else
            \path (n\b);
            \pgfgetlastxy{\bx}{\by}
            \pgfmathtruncatemacro{\dist}{veclen((\bx - \ax),(\by - \ay))}
            \ifdim\dist pt<50pt
                \draw[green] (n\a) -- (n\b);
            \fi
        \fi
    }
}

\end{tikzpicture}

\end{document}


Qrrbrbirlbel님의 댓글 덕분에 원래 코드를 수정하여 더 빠르게 컴파일했지만 여전히 컴파일하는 데 시간이 걸립니다(300노드에 대해 Overleaf에 약 55초가 소요됨).

  1. \foreach 루프 에 두 번째를 포함합니다 . 이 지점까지 존재하는 모든 노드까지의 거리를 테스트하는 것으로 충분합니다. 루프가 적을수록 코드가 더 빨리 컴파일됩니다.
  2. veclen 을 계산하기 전에 두 노드 사이의 수평 및 수직 거리가 이미 너무 큰지 테스트하십시오. veclen 계산에는 시간이 걸리기 때문에 코드 속도가 빨라집니다.

또한 노드 수와 그릴 라인의 최대 거리 조정을 단순화하기 위해 두 개의 매크로를 추가했습니다.

\documentclass[border=10pt]{standalone}
\usepackage{tikz}

\begin{document}

\newcounter{nodecount}
\setcounter{nodecount}{300}

\newlength\maxdistance
\setlength\maxdistance{20pt}

\begin{tikzpicture}
\foreach \i in {1,...,\value{nodecount}}{
    % generate random position
    \pgfmathrandominteger{\a}{-490}{490}
    \pgfmathrandominteger{\b}{-490}{490}
    % draw and label
    \coordinate (n\i) at (\a*0.01,\b*0.01);
    \draw[fill] (n\i) circle (1pt);      

    % check distances to all other existing nodes
    \foreach \j in {1,...,\i}{
        % do not draw line if nodes are identical
        \ifnum\i=\j\else                                 
            \path (n\i);
            \pgfgetlastxy{\ix}{\iy}
            \path (n\j);
            \pgfgetlastxy{\jx}{\jy}
            \pgfmathtruncatemacro{\distx}{\ix - \jx}
            \pgfmathtruncatemacro{\disty}{\iy - \jy}
            % only draw line if x distance of both nodes is not too large
            \ifdim\distx pt<\maxdistance
                % only draw line if y distance of both nodes is not too large
                \ifdim\disty pt<\maxdistance             
                    \pgfmathtruncatemacro{\distxy}{veclen((\distx),(\disty))}
                    % only draw line if distance of both nodes is not too large
                    \ifdim\distxy pt<\maxdistance 
                        \draw[green] (n\i) -- (n\j);
                    \fi
                \fi
            \fi
        \fi
    }
}

\end{tikzpicture}

\end{document}


TeX는 그렇게 복잡하고 반복적인 계산을 위해 만들어지지 않았기 때문에 그러한 계산이 가능한 다른 프로그래밍 언어를 사용하는 것이 좋습니다. 예를 들어 LuaLaTeX를 사용하여 다음 코드를 컴파일할 수 있습니다(컴파일하는 데 약 2초 소요).

\documentclass[border=10pt]{standalone}
\usepackage{tikz}

\begin{document}

\begin{tikzpicture}

\directlua{

n = 300

d = 1

x = {}
y = {}

for i = 1,n do

    a = math.random(-490,490)
    b = math.random(-490,490)
    
    x[i] = a*0.01
    y[i] = b*0.01

    tex.print('\\coordinate(' .. i .. ') at (' .. x[i] ..',' .. y[i] .. ');') 
    tex.print('\\draw[fill] (' .. i .. ') circle (1pt);') 

    for j = 1,i do
        t = math.sqrt((x[i] - x[j])^2 + (y[i] - y[j])^2)
        if t > 0 and t < d then
            tex.print('\\draw[green] (' .. i .. ') -- (' .. j .. ');') 
        end
    end
    
end

}

\end{tikzpicture}

\end{document}



세 가지 솔루션이 있습니다.

  1. 길이에 대해 테스트하기 위해 PGF 및 TeX 매크로를 사용합니다.
  2. TikZ의 calc 라이브러리를 사용합니다.
  3. calcmath 라이브러리 를 모두 사용합니다 .

첫 번째 것은 약 3.5배 더 빠릅니다. 솔루션 2와 3은 거의 같은 시간이 걸립니다. (물론 수학은 항상 동일합니다.)

첫 번째는 또한 x 방향 또는 y 방향의 차이가 이미 요구된 길이( test length 값 키 를 통해 제공됨)를 초과하는 경우 더 세게 수행할 필요가 없기 때문에 실제로 veclen 계산 을 수행해야 하는지 여부를 확인합니다. 계산.
또한 매개변수가 이미 평가되어 veclen 에서 직접 처리할 수 있기 때문에 \pgfmathveclen 대신 \pgfmathveclen@ 을 사용합니다.

코드 1(PGF)

\documentclass[tikz]{standalone}
\tikzset{
  test length/.initial=5mm,
  test previous/.code 2 args={%
      \pgfpointdiff{\pgfpointanchor{dot-#1}{center}}
                   {\pgfpointanchor{dot-#2}{center}}%
      \pgfgetlastxy{\lastX}{\lastY}%
      \ifdim\lastX>\pgfkeysvalueof{/tikz/test length}\relax\else
        \ifdim\lastY>\pgfkeysvalueof{/tikz/test length}\relax\else
          \csname pgfmathveclen@\endcsname{\lastX}{\lastY}%
          \ifdim\pgfmathresult pt<\pgfkeysvalueof{/tikz/test length}\relax
            \tikzset{insert path=edge(dot-#2)}%
          \fi
        \fi
      \fi}}
\begin{document}
\pgfmathsetseed{652524}
\begin{tikzpicture}
\foreach \x in {1,...,300}{
    \pgfmathrandominteger{\a}{-490}{490}
    \pgfmathrandominteger{\b}{-490}{490}
    \node[circle, fill, inner sep=+0pt] (dot-\x) at (\a*.01, \b*.01) {}
      foreach \y in {1,...,\x} { [test previous = {\x}{\y}] };}
\end{tikzpicture}
\end{document}

코드 2( calc )

\documentclass[tikz]{standalone}
\usetikzlibrary{calc}
\makeatletter
\pgfkeys{/utils/if/.code n args={3}{%
  \pgfmathparse{#1}\ifdim\pgfmathresult pt=0pt\relax
    \expandafter\pgfutil\else\expandafter\pgfutil\fi
    {\pgfkeysalso{#3}}{\pgfkeysalso{#2}}}}
\makeatother
\tikzset{
  test length/.initial=5mm,
  test previous/.style 2 args={%
    insert path={let \p{diff} = ($(dot-#1)-(dot-#2)$) in},
    /utils/if = {veclen(\p{diff}) < \pgfkeysvalueof{/tikz/test length}}
                {insert path={(dot-#1)edge(dot-#2)}}}}
\begin{document}
\pgfmathsetseed{652524}
\begin{tikzpicture}
\foreach \x in {1,...,100}{
    \pgfmathrandominteger{\a}{-490}{490}
    \pgfmathrandominteger{\b}{-490}{490}
    \node[circle, fill, inner sep=+0pt] (dot-\x) at (\a*.01, \b*.01) {}
      foreach \y in {1,...,\x} { [test previous/.expanded = {\x}{\y}] };}
\end{tikzpicture}
\end{document}

코드 3( \tikzmath )

\documentclass[tikz]{standalone}
\usetikzlibrary{calc,math}
\tikzset{test length/.initial=5mm}
\begin{document}
\pgfmathsetseed{652524}
\begin{tikzpicture}
\foreach \x in {1,...,300}{
  \pgfmathrandominteger{\a}{-490}{490}
  \pgfmathrandominteger{\b}{-490}{490}
  \node[circle, fill, inner sep=+0pt] (dot-\x) at (\a*.01, \b*.01) {};
  \tikzmath{
    coordinate \diff; int \y;
    for \y in {1,...,\x} {
      \diff = (dot-\x)-(dot-\y);
      if veclen(\diffx, \diffy) < \pgfkeysvalueof{/tikz/test length} then {
        {\draw (dot-\x)--(dot-\y);};
      };
    };
  }
}
\end{tikzpicture}
\end{document}

Output