本问题来自这个问题的评论区,为了避免有趣的实现思路被掩盖在其他问题的评论区中,另开一个问题。
\input{centroid_label.tex}
\LabelPts{A, B, E, M}
这个命令的思路是, 给某些点加标签时, 先计算它们的质心, 根据各个点与质心的角度, 决定标签的方位.
相信大家都有过需要写大量的语句来实现对多个坐标点添加标签的位置控制,这并不优雅,也比较麻烦。
tkz-euclide
,只要能准确控制「标号位置」、「标号距离」即可。例子中用tkz
也只是因为代码行数短,同时定义多个点更方便。如下MWE,我不希望用6行来控制他们的标签位置
\documentclass[tikz,border=5mm]{standalone}
\usepackage{tkz-euclide}
\begin{document}
\begin{tikzpicture}
\tkzDefPoints{-1/-1/A, 1/-2/B, 3/0/C, 2/4/D, 0/5/E,-3/2/F}
\tkzDrawPolygons[thick](A,...,F)
\tkzLabelPoints(A,...,F)
\end{tikzpicture}
\end{document}
\documentclass[tikz,border=5pt]{standalone}
\usepackage{tkz-euclide}
\ExplSyntaxOn\makeatletter
\pgfmathdeclarefunction{pointveclen}{3}{
\begingroup
\tikz@scan@one@point\pgfutil@firstofone(#1)\relax
\pgf@xx\pgf@x \pgf@xy\pgf@y
\tikz@scan@one@point\pgfutil@firstofone(#2)\relax
\pgf@yx\pgf@x \pgf@yy\pgf@y
\pgfmathparse{veclen(\the\pgf@xx+#3\the\pgf@yx,\the\pgf@xy+#3\the\pgf@yy)}
\pgfmath@smuggleone\pgfmathresult
\endgroup
}
\NewDocumentCommand\tkzAutoLabelPolygons{O{} r()} {
\group_begin:
\seq_gclear:N \g_tmpa_seq
\foreach \x in {#2} { \seq_gput_right:Ne \g_tmpa_seq { \x } }
\seq_get_left:NN \g_tmpa_seq \l_tmpa_tl
\seq_get_right:NN \g_tmpa_seq \l_tmpb_tl
\seq_gput_left:NV \g_tmpa_seq \l_tmpb_tl
\seq_gput_right:NV \g_tmpa_seq \l_tmpa_tl
\int_step_inline:nnn { 2 } { \seq_count:N \g_tmpa_seq - 1 }
{
\seq_gpop_left:NN \g_tmpa_seq \l_tmpa_tl
\tl_set:Ne \l_tmpb_tl { \seq_item:Nn \g_tmpa_seq { 1 } }
\tl_set:Ne \l_tmpc_tl { \seq_item:Nn \g_tmpa_seq { 2 } }
\pgfinterruptboundingbox
\coordinate (tempa) at ($1cm/pointveclen("\l_tmpa_tl","\l_tmpb_tl","-")
*($(\l_tmpa_tl)-(\l_tmpb_tl)$)$);
\coordinate (tempb) at ($1cm/pointveclen("\l_tmpc_tl","\l_tmpb_tl","-")
*($(\l_tmpc_tl)-(\l_tmpb_tl)$)$);
\coordinate (tempc) at ($0.3*1cm/pointveclen("tempa","tempb","+")
*($(tempa)+(tempb)$)$);
\endpgfinterruptboundingbox
\path (\l_tmpb_tl) -- ($(\l_tmpb_tl)-(tempc)$)
node[label~style,anchor=center,#1] {$\l_tmpb_tl$};
}
\group_end:
}
\ExplSyntaxOff\makeatother
\begin{document}
\begin{tikzpicture}
\tkzDefPoints{-1/-1/A, 1/-2/B, 3/0/C, 2/4/D, 0/5/E,-3/2/F}
\tkzDrawPolygons[thick](A,...,F)
\tkzAutoLabelPolygons(A,...,F)
\end{tikzpicture}
\begin{tikzpicture}
\tkzDefPoints{-1/-1/A, 1/-2/B, 0.5/0/C, 2/4/D, 0/5/E,-3/2/F}
\tkzDrawPolygons[thick](A,...,F)
\tkzAutoLabelPolygons(A,...,F)
\end{tikzpicture}
\end{document}
一个基于tkz-euclide
的可能方案,有大量瑕疵:
\tkzAutoLabelPoints
来调整dist
也并不完美,点A
,B
,C
,F
偏远;点D
与E
偏近\tkzDefBarycentricPoint(A=1,B=1,C=1,D=1,E=1,F=1)
语法冗杂,而\tkzCentroid(A,B,C)
仅支持三个点\documentclass[tikz,border=5mm]{standalone}
\usepackage{tkz-euclide}
\begin{document}
\begin{tikzpicture}
\tkzDefPoints{-1/-1/A, 1/-2/B, 3/0/C, 2/4/D, 0/5/E,-3/2/F}
\tkzDrawPolygons[thick](A,...,F)
% \tkzLabelPoints(A,...,F) % No!
% \tkzCentroid(A,B,C) %怎么只支持三个点呢..真可惜
\tkzDefBarycentricPoint(A=1,B=1,C=1,D=1,E=1,F=1)
\tkzGetPoint{centroid}
\tkzDrawPoint(centroid)
\tkzAutoLabelPoints[center = centroid,dist= .12](A,...,F)
\end{tikzpicture}
\end{document}
抛砖引玉了~ 期待@u817 老师提供更general方案的补充
使用\path
和pos
..对位置的控制相对好一些了,但是却不能很好的融入tkz-euclide
的框架里...
这里的各个label和点的距离是「路径(centeroid--\x
)的长度*0.1」,因为不好找到精准的最小覆盖圆(因为多点情况下这不存在),长度不相等,这是利用pos
的瑕疵...
\documentclass[tikz,border=5mm]{standalone}
\usepackage{tkz-euclide}
\begin{document}
\begin{tikzpicture}
\tkzDefPoints{-1/-1/A, 1/-2/B, 3/0/C, 2/4/D, 0/5/E,-3/2/F}
\tkzDrawPolygons[thick](A,...,F)
% \tkzLabelPoints(A,...,F) % No!
% \tkzCentroid(A,B,C) %怎么只支持三个点呢..真可惜
\tkzDefBarycentricPoint(A=1,B=1,C=1,D=1,E=1,F=1)
\tkzGetPoint{centroid}
\tkzDrawPoint(centroid)
% \tkzAutoLabelPoints[center = centroid,dist= .12](A,...,F)
\foreach \x in {A,...,F}{
\path (centroid) -- (\x) node [text=magenta,pos=1.1] {\x};
}
\end{tikzpicture}
\end{document}
用纯 tikz 给一个弱的解答。思路是先求出多边形的重心,然后在重心到顶点射线的方向上的合适位置放置标签,用 calc 库计算坐标。
\documentclass[tikz,border=1cm]{standalone}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}
\foreach \x/\y in {%
(0,0)/a,
(1,1)/b,
(3,2)/c,
(4,-1)/d,
(2,-2)/e}
{ \node[fill, circle, inner sep =0pt, minimum size = 3pt] (\y) at \x {}; }
\draw plot coordinates {(a) (b) (c) (d) (e) (a)};
\coordinate (O) at ($0.2*(a)+ 0.2*(b)+ 0.2*(c)+ 0.2*(d)+ 0.2*(e)$);
\fill[red] (O) circle(2pt);% 重心
\foreach \x in {a,c,d,e}
{\node at ($(O)!1.15!(\x)$){$\x$};}
\node at ($(O)!1.22!(b)$){$b$};
\end{tikzpicture}
\end{document}
不难看到,用重心作为射线的起点并不完美,上图中的 b 标签就不能合适放置,故而只能单独绘制。
对于在圆上的凸多边形,存在到所有顶点距离相等的点,用该点作为射线起点比较合适,但对应一般的凸多边形不存在这样的点,所以仅仅用重心并不合适。
以上操作,本质上和逐个绘制标签没有区别,仅仅是用 foreach 简化操作。
另一个实现在这个链接
放在优角的角平分线上应该比较合适,离两条边的距离适中。