TIKZ 中的宏/封装

TIKZ 中的宏/封装

我正在考虑开始使用 TIKZ,并试图弄清楚它是否能满足我的要求以及如何实现。

我有一份包含许多贝塞尔曲线图片的大型文档。对于每张图,我想绘制曲线本身、其控制点处的四个点以及连接这四个点的三条线。我目前正在使用 Powerpoint 宏绘制这些图。它们看起来像这样:

在此处输入图片描述

我想将这个东西“封装”在一个函数或宏中,以便于实例化,并确保不同图片之间的一致性。我设想函数的输入将是四个点的坐标和三种颜色(一个用于曲线,一个用于点,一个用于线)。

我读过关于“tikzstyle”功能的文章。听起来不错,因为我可以将“style”定义放在外部,并在我的所有图片中重复使用它,这为我提供了一致性和轻松的全局更改。但我不确定“输入参数”(坐标)如何工作。

tikzstyle 是正确的方法吗,或者有没有更好的方法(使用 TIKZ 或其他类似工具)?

答案1

这是一个可能的解决方案。这里,一个宏定义mybeziertikzpicture如下所示的环境中,该宏带有 4 个参数:P_0、P_a、P_b、P_1。然后调用\mybezier{p0}{pa}{pb}{p1}以绘制。

\newcommand\mybezier[4]{
    \begin{tikzpicture}
    \draw [green]          (#1)--(#2)-- (#3)--(#4);
    \draw[very thick,blue] (#1).. controls (#2) and (#3) .. (#4);
    \draw [fill=red,draw=black]    (#1)node[left] {$P_0$} circle (2pt);
    \draw [fill=red,draw=black]    (#2)node[left] {$P_a$} circle (2pt);
    \draw [fill=red,draw=black]    (#3)node[right]{$P_b$} circle (2pt);
    \draw [fill=red,draw=black]    (#4)node[right]{$P_1$} circle (2pt);
    \end{tikzpicture}
}

在此处输入图片描述

代码

\documentclass[]{article}
\usepackage{tikz}
\usetikzlibrary{positioning}
\newcommand\mybezier[4]{
    \begin{tikzpicture}
    \draw [green]          (#1)--(#2)-- (#3)--(#4);
    \draw[very thick,blue] (#1).. controls (#2) and (#3) .. (#4);
    \draw [fill=red,draw=black]    (#1)node[left] {$P_0$} circle (2pt);
    \draw [fill=red,draw=black]    (#2)node[left] {$P_a$} circle (2pt);
    \draw [fill=red,draw=black]    (#3)node[right]{$P_b$} circle (2pt);
    \draw [fill=red,draw=black]    (#4)node[right]{$P_1$} circle (2pt);
    \end{tikzpicture}
}
\begin{document}
\mybezier{1,0}{2,3}{5,4}{6,1}
\end{document}

答案2

另一种方法是尝试show path construction装饰。设置需要一点时间(例如,自动“很好地”放置控件标签,同时还允许自定义放置),但之后使用它就相当容易了,而且可以使用 TikZ 样式进行控制:

\documentclass[tikz,border=5]{standalone}
\usetikzlibrary{decorations.pathreplacing,shapes.misc,calc}
\tikzset{show bezier controls/.style={decoration={show path construction,
  curveto code={
    \tikzset{#1}    
    \path [hull/.try] 
      (\tikzinputsegmentfirst)    -- (\tikzinputsegmentsupporta) -- 
      (\tikzinputsegmentsupportb) -- (\tikzinputsegmentlast);
    \path [curve/.try] (\tikzinputsegmentfirst) .. controls
      (\tikzinputsegmentsupporta) and (\tikzinputsegmentsupportb)
       .. (\tikzinputsegmentlast);
    \path let \p1=(\tikzinputsegmentfirst), \p2=(\tikzinputsegmentsupporta),
      \p3=(\tikzinputsegmentsupportb), \p4=(\tikzinputsegmentlast),
      % Ugh. All this just to get the mid-angles of the relevant lines
      \n1={atan2(\y2-\y1,\x2-\x1)}, \n2={atan2(\y2-\y3,\x2-\x3)}, 
      \n2={abs(\n2-\n1)<180 ? (\n2 + \n1)/2+180 : (\n2 + \n1)/2},
      \n3={atan2(\y3-\y2,\x3-\x2)}, \n4={atan2(\y3-\y4,\x3-\x4)},
      \n3={abs(\n4-\n3)<180 ? (\n4 + \n3)/2+180 : (\n4 + \n3)/2}
      in 
      \foreach \l [count=\i] in \tikzbezierlabels{ (\p\i) 
        node [inner sep=0pt, marking/.try, marking=\i/.try] {}
        node [anchor=\n\i, marking label/.try, marking label \i/.try] {$\l$}
      };
}}, decorate},
  bezier labels/.store in=\tikzbezierlabels, bezier labels={,,,}
}
\begin{document}
\begin{tikzpicture}[
   hull/.style={draw=green},
   curve/.style={thick, draw=blue},
   marking/.style={circle, draw=black, fill=red, minimum size=2.5pt},
   marking label/.style={shape=circle},
   bezier labels={P_0, P_a, P_b, P_1}
]
\path [show bezier controls] (1,0)  .. controls (2,3) and (5,4) .. (6,1);
\path [show bezier controls, bezier labels={P_0, P_1, P_2, P_3}] 
  (0,4)  .. controls (1,8) and (4,5) .. (6,10);
\path [show bezier controls={hull/.style={fill=gray!20, draw=black, dotted}, 
   curve/.style={draw=black},
   marking/.style={solid,shape=cross out, draw=black!50, thick, minimum size=3pt}, 
   marking label/.append style={outer sep=.25cm, fill=blue!20}, 
   marking label 2/.style={text=red}, bezier labels={P,Q,R,S}}, shift=(90:10)]
   (0,0) .. controls ++(4,4) and ++(-4,3) .. (2,0);
\end{tikzpicture}
\end{document}

在此处输入图片描述

答案3

这是一个pic版本。

\documentclass[]{article}
\usepackage{tikz}
\usetikzlibrary{positioning}
\tikzset{
pics/.cd,
mybezier/.style args={#1#2#3#4#5}{
code={
\draw[very thick,#5] (#1).. controls (#2) and (#3) .. (#4);
\draw [gray]          (#1)--(#2)-- (#3)--(#4);
\draw [fill=green,draw=black]    (#1)node[above] {$P₀$} circle (2pt);
\draw [fill=green,draw=black]    (#2)node[above] {$P_a$} circle (2pt);
\draw [fill=green,draw=black]    (#3)node[right] {$P_b$} circle (2pt);
\draw [fill=green,draw=black]    (#4)node[right] {$P₁$} circle (2pt);
}},
}


\begin{document}
\begin{tikzpicture}
\pic (first) {mybezier={1,4}{4,4}{7,2}{8,0}{blue}};
\pic (second) {mybezier={0,3}{3,3}{5,2}{6,0}{red}};
\end{tikzpicture}
\end{document}

在此处输入图片描述

答案4

为了方便参考(我自己参考,也许其他人也参考),下面是最终代码,包括评论中提到的改进,还有一些其他内容。感谢 Jesse 完成了大部分工作。

\documentclass[12pt]{article}
\usepackage{tikz}
\usetikzlibrary{positioning}

\newcommand\poleDiam{1.8pt}

\newcommand\mybezier[5]{
\draw[very thick,#5] (#1).. controls (#2) and (#3) .. (#4);
\draw [gray]          (#1)--(#2)-- (#3)--(#4);
\draw [fill=green,draw=black]    (#1)node[above] {$P_0$} circle (\poleDiam);
\draw [fill=green,draw=black]    (#2)node[above] {$P_a$} circle (\poleDiam);
\draw [fill=green,draw=black]    (#3)node[right] {$P_b$} circle (\poleDiam);
\draw [fill=green,draw=black]    (#4)node[right] {$P_1$} circle (\poleDiam);
}

\begin{document}
Here is some text before the picture.

\begin{center}
\begin{tikzpicture}
\mybezier{1,4}{4,4}{7,2}{8,0}{blue}
\mybezier{0,3}{3,3}{5,2}{6,0}{red}
\end{tikzpicture}
\end{center}

And here is some text after the picture.
\end{document}

输出结果如下:

在此处输入图片描述

除了图形一致性和易于修改之外,标签还有另一个好处。在 Powerpoint 中制作计算机现代标签很繁琐。

相关内容