![TikZ 标注:如何防止尖锐的指针尖端变圆?](https://linux22.com/image/440959/TikZ%20%E6%A0%87%E6%B3%A8%EF%BC%9A%E5%A6%82%E4%BD%95%E9%98%B2%E6%AD%A2%E5%B0%96%E9%94%90%E7%9A%84%E6%8C%87%E9%92%88%E5%B0%96%E7%AB%AF%E5%8F%98%E5%9C%86%EF%BC%9F.png)
绘制圆角标注框时,指针尖端也是圆的。
以下是 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}
输出始终是: