TikZ 标注:如何防止尖锐的指针尖端变圆?

TikZ 标注:如何防止尖锐的指针尖端变圆?

绘制圆角标注框时,指针尖端也是圆的。

以下是 MWE:

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes.callouts}
\begin{document}
    \begin{tikzpicture}
        \node [anchor=south west] at (0, 0) (cartoon) {\includegraphics[width=.15\textwidth,height=.15\textwidth]{example-image-a}};
        \node [anchor=north west,rectangle callout,draw=black,
        callout absolute pointer=(cartoon.east), 
        rounded corners=3pt,text width=0.7\textwidth, inner sep=2ex] at (.19\textwidth,.125\textwidth) {This is an example.};
    \end{tikzpicture}
\end{document}

结果

我想要的是这样的:

预期结果

如果rounded corners=3pt关闭,指针尖端将始终保持锋利。

所以我猜想这个选项就是原因。

但是,当将指针位置移得更远时,例如callout absolute pointer=(cartoon.west),指针尖端会变得尖锐(更准确地说,它仍然是圆的,但几乎无法明显检测到)。

我查阅了 TikZ 手册,发现除了指针宽度之外,很少能控制指针的形状。

有什么简单的方法来控制这个吗?

答案1

边界路径在节点形状中定义。因此,您可以根据 定义自己的节点形状rectangle callout,使尖端处的角变尖。由于原始标注已经有两个始终尖锐的角,因此您只需通过在本地组中再添加一个角来重复已经完成的操作\pgfsetcornersarced{\pgfqpoint{0pt}{0pt}}。我用 标记了额外的线条<-

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes.callouts}
\makeatletter
\pgfdeclareshape{sharp rectangle callout}{%
    \savedmacro\rectanglecalloutpoints{%
        %
        \pgfmathsetlength\pgf@x{\pgfkeysvalueof{/pgf/inner xsep}}%
        \advance\[email protected]\wd\pgfnodeparttextbox%
        \pgfmathsetlength\pgf@xa{\pgfkeysvalueof{/pgf/minimum width}}%
        \ifdim\pgf@x<.5\pgf@xa%
            \[email protected]\pgf@xa%
        \fi%
        \edef\xtemp{\the\pgf@x}%
        \pgfmathaddtolength\pgf@x{\pgfkeysvalueof{/pgf/outer xsep}}%
        %
        \pgfmathsetlength\pgf@y{\pgfkeysvalueof{/pgf/inner ysep}}%
        \advance\[email protected]\ht\pgfnodeparttextbox%
        \advance\[email protected]\dp\pgfnodeparttextbox%
        \pgfmathsetlength\pgf@ya{\pgfkeysvalueof{/pgf/minimum height}}%
        \ifdim\pgf@y<.5\pgf@ya%
            \[email protected]\pgf@ya%
        \fi%
        \edef\ytemp{\the\pgf@y}%
        \pgfmathaddtolength\pgf@y{\pgfkeysvalueof{/pgf/outer ysep}}%
        %
        \edef\xlength{\the\pgf@x}%
        \edef\ylength{\the\pgf@y}%
        \addtosavedmacro\xlength%
        \addtosavedmacro\ylength%
        %
        \pgfmathsetlengthmacro\pointerwidth{\pgfkeysvalueof{/pgf/callout pointer width}}%
        \addtosavedmacro\pointerwidth%
        %
        \pgfextract@process\centerpoint{%
            \[email protected]\wd\pgfnodeparttextbox%
            \[email protected]\ht\pgfnodeparttextbox%
            \advance\[email protected]\dp\pgfnodeparttextbox%
        }%
        %
        % Process the relative callout pointer.
        %
        \ifpgf@lib@callout@absolutepointer%
        \else%
            \pgfextract@process\calloutpointer{%
                \pgfextract@process\borderpoint{%
                    \expandafter\pgfpointborderrectangle\expandafter{\pgf@lib@callout@relativepointer}%
                        {\pgfqpoint{\xtemp}{\ytemp}}%
                }%
                \pgfmathanglebetweenpoints{\pgfpointorigin}{\borderpoint}%
                \let\pointerangle\pgfmathresult%
                \expandafter\pgf@process\expandafter{\pgf@lib@callout@relativepointer}%
                \pgfmathveclen@{\pgfmath@tonumber{\pgf@x}}{\pgfmath@tonumber{\pgf@y}}%
                \edef\pointerradius{\pgfmathresult pt}%
                \pgfpointadd{\borderpoint}{\pgfqpointpolar{\pointerangle}{\pointerradius}}%
                \pgf@xa\pgf@x%
                \pgf@ya\pgf@y%
                \centerpoint%
                \advance\pgf@x\pgf@xa%
                \advance\pgf@y\pgf@ya%
            }%
            \pgf@lib@callouts@shortenpointer%
            \addtosavedmacro\calloutpointer%
            \pgf@lib@rectanglecallout@pointer%
            \addtosavedmacro\calloutpointeranchor%
            \addtosavedmacro\beforecalloutpointer%
            \addtosavedmacro\aftercalloutpointer%
            \addtosavedmacro\firstpoint%
            \addtosavedmacro\secondpoint%
            \addtosavedmacro\thirdpoint%
            \addtosavedmacro\fourthpoint%
        \fi%
    }%
    \savedanchor\centerpoint{%
        \[email protected]\wd\pgfnodeparttextbox%
        \[email protected]\ht\pgfnodeparttextbox%
        \advance\[email protected]\dp\pgfnodeparttextbox%
    }%
    \savedanchor\basepoint{%
        \[email protected]\wd\pgfnodeparttextbox%
        \pgf@y0pt\relax%
    }%
    \savedanchor\midpoint{%
        \[email protected]\wd\pgfnodeparttextbox%
        \pgfmathsetlength\pgf@y{+.5em}%
    }%
    \anchor{center}{\centerpoint}%
    \anchor{mid}{\midpoint}%
    \anchor{mid east}{%
        \rectanglecalloutpoints%
        \midpoint%
        \advance\pgf@x\xlength\relax%
    }%
    \anchor{mid west}{%
        \rectanglecalloutpoints%
        \midpoint%
        \advance\pgf@x-\xlength\relax%
    }%
    \anchor{base}{\basepoint}%
    \anchor{base east}{%
        \rectanglecalloutpoints%
        \basepoint%
        \advance\pgf@x\xlength\relax%
    }%
    \anchor{base west}{%
        \rectanglecalloutpoints%
        \basepoint%
        \advance\pgf@x-\xlength\relax%
    }%
    \anchor{north}{%
        \rectanglecalloutpoints%
        \centerpoint%
        \advance\pgf@y\ylength\relax%
    }%
    \anchor{south}{%
        \rectanglecalloutpoints%
        \centerpoint%
        \advance\pgf@y-\ylength\relax%
    }%
    \anchor{east}{%
        \rectanglecalloutpoints%
        \centerpoint%
        \advance\pgf@x\xlength\relax%
    }%
    \anchor{west}{%
        \rectanglecalloutpoints%
        \centerpoint%
        \advance\pgf@x-\xlength\relax%
    }%
    \anchor{north east}{%
        \rectanglecalloutpoints%
        \centerpoint%
        \advance\pgf@x\xlength\relax%
        \advance\pgf@y\ylength\relax%
    }%
    \anchor{south west}{%
        \rectanglecalloutpoints%
        \centerpoint%
        \advance\pgf@x-\xlength\relax%
        \advance\pgf@y-\ylength\relax%
    }%
    \anchor{south east}{%
        \rectanglecalloutpoints%
        \centerpoint%
        \advance\pgf@x\xlength\relax%
        \advance\pgf@y-\ylength\relax%
    }%
    \anchor{north west}{%
        \rectanglecalloutpoints%
        \centerpoint%
        \advance\pgf@x-\xlength\relax%
        \advance\pgf@y\ylength\relax%
    }%
    \anchor{pointer}{%
        \rectanglecalloutpoints%
        \calloutpointeranchor%
    }%
    \backgroundpath{%
        \rectanglecalloutpoints%
        \pgf@x\xlength\relax%
        \pgf@y\ylength\relax%
        \pgfmathaddtolength\pgf@x{-\pgfkeysvalueof{/pgf/outer xsep}}%
        \pgfmathaddtolength\pgf@y{-\pgfkeysvalueof{/pgf/outer ysep}}%
        \edef\xtemp{\the\pgf@x}%
        \edef\ytemp{\the\pgf@y}%
        %
        % The absolute pointer must be calculated here because the
        % anchor of the shape (which is calculated after the saved
        % macros and points) affects how the pointer joins the
        % main rectangle.
        %
        \ifpgf@lib@callout@absolutepointer%
            \pgfextract@process\calloutpointer{%
                \pgfpointanchor{pgf@lib@callout@pointer}{center}%
            }%
            \pgf@lib@callouts@shortenpointer%
            \pgfmathsetlengthmacro\pointerwidth{\pgfkeysvalueof{/pgf/callout pointer width}}%
            \pgf@lib@rectanglecallout@pointer%
            %
            % \pgf@node@name = the shape name (from \pgfmultipartnode)
            %
            \ifx\pgf@node@name\pgfutil@empty%
            \else%
                %
                % Now hack an extra saved anchor \calloutpointeranchor,
                % with the new anchor for the callout pointer.
                %
                \edef\pgf@sh@@temp{\noexpand\expandafter\noexpand\pgfutil@g@addto@macro\noexpand\csname pgf@sh@np@\pgf@node@name\noexpand\endcsname}%
                \edef\pgf@sh@@@temp{%
                    \noexpand\def\noexpand\calloutpointeranchor{%
                        \noexpand\pgf@x\the\pgf@x%
                        \noexpand\pgf@y\the\pgf@y%
                    }%
                }%
                \expandafter\pgf@sh@@temp\expandafter{\pgf@sh@@@temp}%
            \fi%
        \fi%
        {%
            \pgfsetcornersarced{\pgfqpoint{0pt}{0pt}}%
            \pgfpathmoveto{\beforecalloutpointer}%
        }%
            {% <-
            \pgfsetcornersarced{\pgfqpoint{0pt}{0pt}}% <-
            \pgfpathlineto{\calloutpointer}%
            }% <-
        {%
            \pgfsetcornersarced{\pgfqpoint{0pt}{0pt}}%
            \pgfpathlineto{\aftercalloutpointer}%
        }%
        {%
            \pgftransformshift{\centerpoint}%
                \pgfpathlineto{\firstpoint}%
            \pgfpathlineto{\secondpoint}%
            \pgfpathlineto{\thirdpoint}%
                \pgfpathlineto{\fourthpoint}%
            {%
                \pgfsetcornersarced{\pgfqpoint{0pt}{0pt}}%
                \pgfpathclose%
            }%
        }
    }%
    \anchorborder{%
        \pgfextract@process\externalpoint{}%
        \rectanglecalloutpoints%
        \pgfpointadd{\centerpoint}%
        {%
            \pgfpointborderrectangle{\pgfpointadd{\centerpoint}{\externalpoint}}%
            {\pgfqpoint{\xlength}{\ylength}}%
        }%
    }%
}%
\makeatother
\begin{document}
    \begin{tikzpicture}
        \node [anchor=south west] at (0, 0) (cartoon) {\includegraphics[width=.15\textwidth,height=.15\textwidth]{example-image-a}};
        \node [anchor=north west,sharp rectangle callout,draw=black,
        callout absolute pointer=(cartoon.east), 
        rounded corners=3pt,text width=0.7\textwidth, 
        inner sep=2ex] at (.19\textwidth,.125\textwidth) {This is an example.};
    \end{tikzpicture}
\end{document}

在此处输入图片描述

一个可能“更好”的方法是修改标注形状以包含一个用于尖角的开关。如果您添加其他开关,并且不想为每个开关组合定义一个新形状,这种方法会很有效。它可以被“激活”,例如

\tikzset{/pgf/callout pointer sharp}

或者在您想要在调出指针处赋予尖角的节点中使用此键。该\tikzset命令还允许您一劳永逸地安装尖角。(原则上,此处定义的形状可以替换当前版本,因为它向后兼容。)

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes.callouts}
\makeatletter
\newif\ifpgfcalloutpointersharp
\pgfkeys{/pgf/callout pointer sharp/.is if=pgfcalloutpointersharp,
/pgf/callout pointer sharp/.default=true,
/pgf/callout pointer sharp=false}
\pgfdeclareshape{rectangle callout}{%
    \savedmacro\rectanglecalloutpoints{%
        %
        \pgfmathsetlength\pgf@x{\pgfkeysvalueof{/pgf/inner xsep}}%
        \advance\[email protected]\wd\pgfnodeparttextbox%
        \pgfmathsetlength\pgf@xa{\pgfkeysvalueof{/pgf/minimum width}}%
        \ifdim\pgf@x<.5\pgf@xa%
            \[email protected]\pgf@xa%
        \fi%
        \edef\xtemp{\the\pgf@x}%
        \pgfmathaddtolength\pgf@x{\pgfkeysvalueof{/pgf/outer xsep}}%
        %
        \pgfmathsetlength\pgf@y{\pgfkeysvalueof{/pgf/inner ysep}}%
        \advance\[email protected]\ht\pgfnodeparttextbox%
        \advance\[email protected]\dp\pgfnodeparttextbox%
        \pgfmathsetlength\pgf@ya{\pgfkeysvalueof{/pgf/minimum height}}%
        \ifdim\pgf@y<.5\pgf@ya%
            \[email protected]\pgf@ya%
        \fi%
        \edef\ytemp{\the\pgf@y}%
        \pgfmathaddtolength\pgf@y{\pgfkeysvalueof{/pgf/outer ysep}}%
        %
        \edef\xlength{\the\pgf@x}%
        \edef\ylength{\the\pgf@y}%
        \addtosavedmacro\xlength%
        \addtosavedmacro\ylength%
        %
        \pgfmathsetlengthmacro\pointerwidth{\pgfkeysvalueof{/pgf/callout pointer width}}%
        \addtosavedmacro\pointerwidth%
        %
        \pgfextract@process\centerpoint{%
            \[email protected]\wd\pgfnodeparttextbox%
            \[email protected]\ht\pgfnodeparttextbox%
            \advance\[email protected]\dp\pgfnodeparttextbox%
        }%
        %
        % Process the relative callout pointer.
        %
        \ifpgf@lib@callout@absolutepointer%
        \else%
            \pgfextract@process\calloutpointer{%
                \pgfextract@process\borderpoint{%
                    \expandafter\pgfpointborderrectangle\expandafter{\pgf@lib@callout@relativepointer}%
                        {\pgfqpoint{\xtemp}{\ytemp}}%
                }%
                \pgfmathanglebetweenpoints{\pgfpointorigin}{\borderpoint}%
                \let\pointerangle\pgfmathresult%
                \expandafter\pgf@process\expandafter{\pgf@lib@callout@relativepointer}%
                \pgfmathveclen@{\pgfmath@tonumber{\pgf@x}}{\pgfmath@tonumber{\pgf@y}}%
                \edef\pointerradius{\pgfmathresult pt}%
                \pgfpointadd{\borderpoint}{\pgfqpointpolar{\pointerangle}{\pointerradius}}%
                \pgf@xa\pgf@x%
                \pgf@ya\pgf@y%
                \centerpoint%
                \advance\pgf@x\pgf@xa%
                \advance\pgf@y\pgf@ya%
            }%
            \pgf@lib@callouts@shortenpointer%
            \addtosavedmacro\calloutpointer%
            \pgf@lib@rectanglecallout@pointer%
            \addtosavedmacro\calloutpointeranchor%
            \addtosavedmacro\beforecalloutpointer%
            \addtosavedmacro\aftercalloutpointer%
            \addtosavedmacro\firstpoint%
            \addtosavedmacro\secondpoint%
            \addtosavedmacro\thirdpoint%
            \addtosavedmacro\fourthpoint%
        \fi%
    }%
    \savedanchor\centerpoint{%
        \[email protected]\wd\pgfnodeparttextbox%
        \[email protected]\ht\pgfnodeparttextbox%
        \advance\[email protected]\dp\pgfnodeparttextbox%
    }%
    \savedanchor\basepoint{%
        \[email protected]\wd\pgfnodeparttextbox%
        \pgf@y0pt\relax%
    }%
    \savedanchor\midpoint{%
        \[email protected]\wd\pgfnodeparttextbox%
        \pgfmathsetlength\pgf@y{+.5em}%
    }%
    \anchor{center}{\centerpoint}%
    \anchor{mid}{\midpoint}%
    \anchor{mid east}{%
        \rectanglecalloutpoints%
        \midpoint%
        \advance\pgf@x\xlength\relax%
    }%
    \anchor{mid west}{%
        \rectanglecalloutpoints%
        \midpoint%
        \advance\pgf@x-\xlength\relax%
    }%
    \anchor{base}{\basepoint}%
    \anchor{base east}{%
        \rectanglecalloutpoints%
        \basepoint%
        \advance\pgf@x\xlength\relax%
    }%
    \anchor{base west}{%
        \rectanglecalloutpoints%
        \basepoint%
        \advance\pgf@x-\xlength\relax%
    }%
    \anchor{north}{%
        \rectanglecalloutpoints%
        \centerpoint%
        \advance\pgf@y\ylength\relax%
    }%
    \anchor{south}{%
        \rectanglecalloutpoints%
        \centerpoint%
        \advance\pgf@y-\ylength\relax%
    }%
    \anchor{east}{%
        \rectanglecalloutpoints%
        \centerpoint%
        \advance\pgf@x\xlength\relax%
    }%
    \anchor{west}{%
        \rectanglecalloutpoints%
        \centerpoint%
        \advance\pgf@x-\xlength\relax%
    }%
    \anchor{north east}{%
        \rectanglecalloutpoints%
        \centerpoint%
        \advance\pgf@x\xlength\relax%
        \advance\pgf@y\ylength\relax%
    }%
    \anchor{south west}{%
        \rectanglecalloutpoints%
        \centerpoint%
        \advance\pgf@x-\xlength\relax%
        \advance\pgf@y-\ylength\relax%
    }%
    \anchor{south east}{%
        \rectanglecalloutpoints%
        \centerpoint%
        \advance\pgf@x\xlength\relax%
        \advance\pgf@y-\ylength\relax%
    }%
    \anchor{north west}{%
        \rectanglecalloutpoints%
        \centerpoint%
        \advance\pgf@x-\xlength\relax%
        \advance\pgf@y\ylength\relax%
    }%
    \anchor{pointer}{%
        \rectanglecalloutpoints%
        \calloutpointeranchor%
    }%
    \backgroundpath{%
        \rectanglecalloutpoints%
        \pgf@x\xlength\relax%
        \pgf@y\ylength\relax%
        \pgfmathaddtolength\pgf@x{-\pgfkeysvalueof{/pgf/outer xsep}}%
        \pgfmathaddtolength\pgf@y{-\pgfkeysvalueof{/pgf/outer ysep}}%
        \edef\xtemp{\the\pgf@x}%
        \edef\ytemp{\the\pgf@y}%
        %
        % The absolute pointer must be calculated here because the
        % anchor of the shape (which is calculated after the saved
        % macros and points) affects how the pointer joins the
        % main rectangle.
        %
        \ifpgf@lib@callout@absolutepointer%
            \pgfextract@process\calloutpointer{%
                \pgfpointanchor{pgf@lib@callout@pointer}{center}%
            }%
            \pgf@lib@callouts@shortenpointer%
            \pgfmathsetlengthmacro\pointerwidth{\pgfkeysvalueof{/pgf/callout pointer width}}%
            \pgf@lib@rectanglecallout@pointer%
            %
            % \pgf@node@name = the shape name (from \pgfmultipartnode)
            %
            \ifx\pgf@node@name\pgfutil@empty%
            \else%
                %
                % Now hack an extra saved anchor \calloutpointeranchor,
                % with the new anchor for the callout pointer.
                %
                \edef\pgf@sh@@temp{\noexpand\expandafter\noexpand\pgfutil@g@addto@macro\noexpand\csname pgf@sh@np@\pgf@node@name\noexpand\endcsname}%
                \edef\pgf@sh@@@temp{%
                    \noexpand\def\noexpand\calloutpointeranchor{%
                        \noexpand\pgf@x\the\pgf@x%
                        \noexpand\pgf@y\the\pgf@y%
                    }%
                }%
                \expandafter\pgf@sh@@temp\expandafter{\pgf@sh@@@temp}%
            \fi%
        \fi%
        {%
            \pgfsetcornersarced{\pgfqpoint{0pt}{0pt}}%
            \pgfpathmoveto{\beforecalloutpointer}%
        }%
        {% <-
            \ifpgfcalloutpointersharp% <-
                \pgfsetcornersarced{\pgfqpoint{0pt}{0pt}}% <-
            \fi%
            \pgfpathlineto{\calloutpointer}%
        }% <-
        {%
            \pgfsetcornersarced{\pgfqpoint{0pt}{0pt}}%
            \pgfpathlineto{\aftercalloutpointer}%
        }%
        {%
            \pgftransformshift{\centerpoint}%
                \pgfpathlineto{\firstpoint}%
            \pgfpathlineto{\secondpoint}%
            \pgfpathlineto{\thirdpoint}%
                \pgfpathlineto{\fourthpoint}%
            {%
                \pgfsetcornersarced{\pgfqpoint{0pt}{0pt}}%
                \pgfpathclose%
            }%
        }
    }%
    \anchorborder{%
        \pgfextract@process\externalpoint{}%
        \rectanglecalloutpoints%
        \pgfpointadd{\centerpoint}%
        {%
            \pgfpointborderrectangle{\pgfpointadd{\centerpoint}{\externalpoint}}%
            {\pgfqpoint{\xlength}{\ylength}}%
        }%
    }%
}%
\makeatother
\begin{document}
    \begin{tikzpicture}
        \node [anchor=south west] at (0, 0) (cartoon) {\includegraphics[width=.15\textwidth,height=.15\textwidth]{example-image-a}};
        \node [anchor=north west,
        /pgf/callout pointer sharp,%<- 
        rectangle callout,draw=black,
        callout absolute pointer=(cartoon.east), 
        rounded corners=3pt,text width=0.7\textwidth, 
        inner sep=2ex,
        ] at (.19\textwidth,.125\textwidth) {This is an example.};
    \end{tikzpicture}
\end{document}

答案2

解决方法:自己画出边框。

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes.callouts}
\begin{document}
    \begin{tikzpicture}
        \node [anchor=south west] at (0, 0) (cartoon) {\includegraphics[width=.15\textwidth,height=.15\textwidth]{example-image-a}};
        \node [anchor=north west,rectangle callout,
        %draw=black, <--- do not draw the node, you draw the borders afterwards
        callout absolute pointer=(cartoon.east),
        rounded corners=3pt,
        text width=0.7\textwidth, inner sep=2ex] (mynode) at (.19\textwidth,.125\textwidth) {This is an example.};
        \draw[rounded corners=3pt] (mynode.north west) -- (mynode.north east) 
        --
        (mynode.south east)
        --
        (mynode.south west)
        to[sharp corners]
        ([yshift=-.125cm]mynode.west) to[sharp corners]
        ([xshift=-.2cm]mynode.west) to[sharp corners]
        ([yshift=.125cm]mynode.west) -- cycle
        ;
    \end{tikzpicture}
\end{document}

为了方便起见,您最终可以创建一个pic(以防您必须在文档中多次使用该节点形状):

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes.callouts}
\tikzset{
  pics/myshape/.style={
    code={
      \node [anchor=north west,rectangle callout,
        callout absolute pointer=(cartoon.east),
        rounded corners=3pt,
        text width=0.7\textwidth, inner sep=2ex] (mynode) {#1};
      \draw[rounded corners=3pt] (mynode.north west) -- (mynode.north east) --
        (mynode.south east) -- (mynode.south west)
        to[sharp corners] ([yshift=-.125cm]mynode.west) 
        to[sharp corners] ([xshift=-.2cm]mynode.west) 
        to[sharp corners] ([yshift=.125cm]mynode.west) 
        -- cycle; 
    }
  }
}
\begin{document}
  \begin{tikzpicture}
    \node [anchor=south west] at (0, 0) (cartoon) {\includegraphics[width=.15\textwidth,height=.15\textwidth]{example-image-a}};
    \pic at (.19\textwidth,.125\textwidth) {myshape=This is an example.};
  \end{tikzpicture}
\end{document}

输出始终是:

在此处输入图片描述

相关内容