我对不同的图片有很多评论,例如我可以使用以下命令来完成:
\begin{tikzpicture}
\node[anchor=south west,inner sep=0] (image) at (0,0)
{\includegraphics[width=0.9\textwidth]{mypicture}};
\begin{scope}[x={(image.south east)},y={(image.north west)}]
\draw[red,ultra thick,rounded corners] (0.68,0.01) rectangle (0.97,0.85);
\node[fill=blue!10,text centered] at (0.83,0.43){Comment one};
\draw[red,ultra thick,rounded corners] (0.04,0.08) rectangle (0.30,0.55);
\node[fill=blue!10,text width=2cm,text centered] at
(0.17,0.31){zone~2 comment~2 multiline};
\end{scope}
\end{tikzpicture}
我希望实现一些自动化,因为文本始终位于边界框的中心,并且不会超出范围。
我不知道应该从哪里开始,我对 TikZ 有基本的了解,我试过使用\pgfdeclareshape
但没有成功,甚至不知道这是否是一个好的开始!如何分别指定框架和文本的装饰?
我是否应该使用如下语法:
\draw[parameters] (a,b) myshape (c,d){my comment}
或者
\node[theshape, parameters, heigh, width] at (a,b){my comment}
这远远超出了我今天的知识范围 :-(
答案1
人们可以把以前的代码抓取到新的环境中并声明几个命令来创建注释。
因此,我创建了annotatedtikzpicture
:
\NewDocumentEnvironment{annotatedtikzpicture}{O{width=0.9\textwidth} m}{
\begin{tikzpicture}
\node[anchor=south west,inner sep=0] (image) at (0,0)
{\includegraphics[#1]{#2}};
\begin{scope}[x={(image.south east)},y={(image.north west)}]
}{
\end{scope}
\end{tikzpicture}
}
就命令而言,两者都能够立即绘制边框(使用其自己的选项)和带注释的文本(使用其自己的选项);不同之处在于:
annote
对于参数的数量来说非常棘手;xannote
通过利用,更加方便用户keys
。
让我们看看定义:
\NewDocumentCommand{\annote}{o r() r() o r() m}{
\draw[#1] (#2) rectangle (#3);
\node[#4] at (#5) {#6};
}
for \annote
、while\xannote
及其自己的键:
\pgfkeys{/tikz/annotated figure/.cd,
border width/.initial=0,
border width/.get=\bwidth,
border width/.store in=\bwidth,
border height/.initial=0,
border height/.get=\bheight,
border height/.store in=\bheight,
start border pos/.initial={(0,0)},
start border pos/.get=\bpos,
start border pos/.store in=\bpos,
start text pos/.initial={(0,0)},
start text pos/.get=\tpos,
start text pos/.store in=\tpos,
border options/.code={
\tikzset{border style/.style={
#1
}
}
},
text options/.code={
\tikzset{text style/.style={
#1
}
}
},
}
\NewDocumentCommand{\xannote}{r[] m}{
\pgfkeys{/tikz/annotated figure/.cd,#1}
\node[coordinate] (x) at \bpos {};
\draw[border style] (x) rectangle ($(x)+(\bwidth,\bheight)$);
\node[text style] at \tpos {#2};
}
annotation centered
请注意,通过将键设置为,也可以简化注释文本位置true
。但是,要做到这一点,\xannote
命令必须更改为:
\NewDocumentCommand{\xannote}{r[] m}{
\pgfkeys{/tikz/annotated figure/.cd,#1}
\node[coordinate] (x) at \bpos {};
\draw[border style] (x) rectangle ($(x)+(\bwidth,\bheight)$);
\ifannotationcentered
\node[text style] at ($(x)+(0.5*\bwidth,0.5*\bheight)$) {#2};
\else
\node[text style] at \tpos {#2};
\fi
}
为了使功能更加完善,添加了以下功能:
- 提供了一个
\annoteset
命令:其功能与相同\tikzset
,这里用于将要传递给的类似选项收集到一个样式中\xannote
; - 一个
\yannote
命令,与前一个命令不同,\xannote
因为它不是先绘制一个矩形然后绘制一个节点,而是只是一个节点。定义稍后显示; - 直接计算注释宽度的键,如如何使用 pgf 计算长度并设置文本宽度;我没有在那里发布解决方案,因为它远非完美(实际上在某些假设下它不起作用)。
那么,它是如何定义的\yannote
?它只是一个已经给出最小高度和宽度的节点:
\NewDocumentCommand{\yannote}{r[] m}{
\pgfkeys{/tikz/annotated figure/.cd,#1}
\node[option style,
minimum width=\bwidth,
minimum height=\bheight] at \tpos {#2};
}
与option style
上一个相同border style
,text style
可以通过键进行自定义options
。例如:
\yannote[start text pos={(0.5,0.075)},
border height=0.7,
border width=0.3,
options={draw=red,
fill=blue!10
},
]{A Comment}
在这种情况下,start border pos
是完全没用的。
为了将相似的选项分组为一种风格,可以利用\annoteset
;定义如下:
\NewDocumentCommand{\annoteset}{m}{
\pgfkeys{/tikz/annotated figure/.cd,#1}
}
例如可以用于分组:
\annoteset{my annotation/.style={
border options={red,ultra thick,rounded corners},
annotation centered=true,
automatic text width=true,
text options={fill=blue!10,align=center,text width=2.25cm},
}
}
\xannote[start border pos={(0.68,0.01)},
border height=0.77,
border width=0.29,
my annotation
]{Comment one}
\xannote [start border pos={(0.04,0.08)},
border height=0.43,
border width=0.26,
my annotation
]{zone~2 comment~2 multiline}
注意关键的automatic text width
: ,它负责设置注释文本的宽度。宽度计算实际上是由以下宏执行的:
% based on Alain's answer:
% https://tex.stackexchange.com/a/38500/13304
% percusse's answer:
% https://tex.stackexchange.com/a/58291/13304
% and the \CalcHeight macro implemented in:
% http://www.ctan.org/pkg/smartdiagram
\makeatletter
\def\CalcWidth(#1,#2)#3{%
\pgfpointdiff{\pgfpointanchor{#1}{west}}{\pgfpointanchor{#2}{east}}
\pgfmathsetmacro{\mywidth}{veclen(\pgf@x,\pgf@y)-4\pgflinewidth}
\global\expandafter\edef\csname #3\endcsname{\mywidth}
}
\makeatother
在新的定义中\xannote
:
\NewDocumentCommand{\xannote}{r[] m}{
\pgfkeys{/tikz/annotated figure/.cd,#1}
\node[coordinate] (annote-x) at \bpos {};
\draw[border style] (annote-x) rectangle ($(annote-x)+(\bwidth,\bheight)$)coordinate(annote-y);
\coordinate(annote-z) at (annote-x.west-|annote-y.east);
\ifannotationcentered
\ifautomatictextwidth
\CalcWidth(annote-x,annote-z){widthborder}
\node[text style,minimum width=\widthborder] at ($(annote-x)+(0.5*\bwidth,0.5*\bheight)$) {#2};
\else
\node[text style] at ($(annote-x)+(0.5*\bwidth,0.5*\bheight)$) {#2};
\fi
\else
\ifautomatictextwidth
\CalcWidth(annote-x,annote-z){widthborder}
\node[text style,minimum width=\widthborder] at \tpos {#2};
\else
\node[text style] at \tpos {#2};
\fi
\fi
}
推理提示:我们知道边框的左下角点称为(annote-x)
,我们也知道边框的右上角点,用 表示(annote-y)
。要计算宽度,我们基本上需要计算前一个坐标的交点以找到边框的右下角点,(annote-z)
。
为什么我说它不完美?好吧,只需使用这种方法并设置一个高值text width
:text options
它就会失败。
完整示例:
\documentclass[11pt,a4paper]{article}
\usepackage{xparse}
\usepackage{tikz}
\usetikzlibrary{calc}
\NewDocumentEnvironment{annotatedtikzpicture}{O{width=0.9\textwidth} m}{
\begin{tikzpicture}
\node[anchor=south west,inner sep=0] (image) at (0,0)
{\includegraphics[#1]{#2}};
\begin{scope}[x={(image.south east)},y={(image.north west)}]
\draw(image.south east)rectangle(image.north west);
}{
\end{scope}
\end{tikzpicture}
}
% based on Alain's answer:
% https://tex.stackexchange.com/a/38500/13304
% percusse's answer:
% https://tex.stackexchange.com/a/58291/13304
% and the \CalcHeight macro implemented in:
% http://www.ctan.org/pkg/smartdiagram
\makeatletter
\def\CalcWidth(#1,#2)#3{%
\pgfpointdiff{\pgfpointanchor{#1}{west}}{\pgfpointanchor{#2}{east}}
\pgfmathsetmacro{\mywidth}{veclen(\pgf@x,\pgf@y)-4\pgflinewidth}
\global\expandafter\edef\csname #3\endcsname{\mywidth}
}
\makeatother
\NewDocumentCommand{\annote}{o r() r() o r() m}{
\draw[#1] (#2) rectangle (#3);
\node[#4] at (#5) {#6};
}
% predefined styles
\tikzset{border style/.style={draw=none},
text style/.style={draw=none},
option style/.style={draw=none},
}
\newif\ifannotationcentered
\newif\ifautomatictextwidth
\pgfkeys{/tikz/annotated figure/.cd,
border width/.initial=0,
border width/.get=\bwidth,
border width/.store in=\bwidth,
border height/.initial=0,
border height/.get=\bheight,
border height/.store in=\bheight,
start border pos/.initial={(0,0)},
start border pos/.get=\bpos,
start border pos/.store in=\bpos,
start text pos/.initial={(0,0)},
start text pos/.get=\tpos,
start text pos/.store in=\tpos,
border options/.code={
\tikzset{border style/.style={
#1
}
}
},
text options/.code={
\tikzset{text style/.style={
#1
}
}
},
options/.code={
\tikzset{option style/.style={
#1
}
}
},
annotation centered/.is if=annotationcentered,
annotation centered=false,
automatic text width/.is if=automatictextwidth,
automatic text width=false,
}
\NewDocumentCommand{\annoteset}{m}{
\pgfkeys{/tikz/annotated figure/.cd,#1}
}
\NewDocumentCommand{\xannote}{r[] m}{
\pgfkeys{/tikz/annotated figure/.cd,#1}
\node[coordinate] (annote-x) at \bpos {};
\draw[border style] (annote-x) rectangle ($(annote-x)+(\bwidth,\bheight)$)coordinate(annote-y);
\coordinate(annote-z) at (annote-x.west-|annote-y.east);
\ifannotationcentered
\ifautomatictextwidth
\CalcWidth(annote-x,annote-z){widthborder}
\node[text style,minimum width=\widthborder] at ($(annote-x)+(0.5*\bwidth,0.5*\bheight)$) {#2};
\else
\node[text style] at ($(annote-x)+(0.5*\bwidth,0.5*\bheight)$) {#2};
\fi
\else
\ifautomatictextwidth
\CalcWidth(annote-x,annote-z){widthborder}
\node[text style,minimum width=\widthborder] at \tpos {#2};
\else
\node[text style] at \tpos {#2};
\fi
\fi
}
\NewDocumentCommand{\yannote}{r[] m}{
\pgfkeys{/tikz/annotated figure/.cd,#1}
\node[option style,
minimum width=\bwidth,
minimum height=\bheight] at \tpos {#2};
}
% something for shadings, taken from:
% https://tex.stackexchange.com/questions/104528/tikz-shade-also-the-border-of-a-node/104541#104541
\tikzset{
shrink inner sep/.code={
\pgfkeysgetvalue{/pgf/inner xsep}{\currentinnerxsep}
\pgfkeysgetvalue{/pgf/inner ysep}{\currentinnerysep}
\pgfkeyssetvalue{/pgf/inner xsep}{\currentinnerxsep - 0.5\pgflinewidth}
\pgfkeyssetvalue{/pgf/inner ysep}{\currentinnerysep - 0.5\pgflinewidth}
}
}
\tikzset{horizontal shaded border/.style args={#1 and #2}{
append after command={
\pgfextra{%
\begin{pgfinterruptpath}
\path[rounded corners,left color=#1,right color=#2]
($(\tikzlastnode.south west)+(-\pgflinewidth,-\pgflinewidth)$)
rectangle
($(\tikzlastnode.north east)+(\pgflinewidth,\pgflinewidth)$);
\end{pgfinterruptpath}
}
}
},
vertical shaded border/.style args={#1 and #2}{
append after command={
\pgfextra{%
\begin{pgfinterruptpath}
\path[rounded corners,top color=#1,bottom color=#2]
($(\tikzlastnode.south west)+(-\pgflinewidth,-\pgflinewidth)$)
rectangle
($(\tikzlastnode.north east)+(\pgflinewidth,\pgflinewidth)$);
\end{pgfinterruptpath}
}
}
}
}
\begin{document}
\begin{annotatedtikzpicture}[width=0.8\textwidth]{foto}
\yannote[start text pos={(0.5,0.075)},
border height=0.7,
border width=0.3,
options={ultra thick,
rounded corners,
vertical shaded border=green!50 and violet!50!magenta,
top color=green!10,
bottom color=violet!40!magenta!50
},
]{A Comment}
% a style for grouping similar options
\annoteset{my annotation/.style={
border options={red,ultra thick,rounded corners},
annotation centered=true,
automatic text width=true,
text options={fill=blue!10,align=center,text width=2.25cm},
}
}
\xannote[start border pos={(0.68,0.01)},
border height=0.77,
border width=0.29,
my annotation
]{Comment one}
\xannote [start border pos={(0.04,0.08)},
border height=0.43,
border width=0.26,
my annotation
]{zone~2 comment~2 multiline}
\end{annotatedtikzpicture}
\end{document}
它给:
答案2
一种stackengine
方法。可以使用嵌套版本的\commentfig{x0 to box left}{y0 to text height}{dx-box}{dy-box}{{line 1} {line 2} {...}}{image}
\documentclass{article}
\usepackage{graphicx}
\usepackage{stackengine}
\usepackage{pbox}
\usepackage{xcolor}
\fboxrule=1.5pt
\def\stackalignment{l}
\newlength \vdiff%
\newcommand\commentfig[6]{%
\setlength\vdiff{#4}%
\savestack\TextStack{\color{black}\Longstack{#5}}%
\bottominset{\color{red}\fbox{%
\rule{0ex}{.5\vdiff}\\%
\makebox[#3]{\colorbox{white}{\belowbaseline[-.5\ht\TextStackcontent]{\TextStack}}}\\%
\rule[-.5\vdiff]{0ex}{\vdiff}}}{
#6
}{#2}{#1}
}
\begin{document}
\Large
%\commentfig{x0 to box left}{y0 to text}{dx}{dy}{{line 1} {line 2} {...}}{image}
\commentfig{.3in}{1in}{1.45in}{1.3in}{this {is a test} comment}{%
\commentfig{3.75in}{1.35in}{1.45in}{2.5in}{{Comment three}}{%
\includegraphics{image}%
}
}
\end{document}