我在两点之间画了一条缩短的曲线。
我想获取该缩短线的起点和终点的坐标。
我尝试使用下面的代码
A
和B
来定义弯曲线的坐标(红点),
我希望C
和D
(黑点)是缩短线的起点和终点的坐标,但目前A
和C
是相同的坐标。B
和也一样,D
如屏幕截图所示
\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{
arrows.meta,calc,decorations.markings,math,arrows.meta,positioning,automata}
\begin{document}
\begin{tikzpicture}
\coordinate (A) at (3.5,3.5);
\coordinate (B) at (5.1,2.);
\draw[color=red,fill=red] (A) circle (0.15cm);
\draw[color=red,fill=red] (B) circle (0.15cm);
\draw (A) edge[out=-20,in=140,shorten >=15pt, shorten <=15pt] coordinate[pos=0.] (C) coordinate[pos=1] (D) (B);
\draw[black,fill=black] (C) circle (0.05cm);
\draw[black,fill=black] (D) circle (0.05cm);
\end{tikzpicture}
\end{document}
更一般地,我希望
pos = 0
对应于缩短线的开头pos = 1
对应于缩短线的末尾
这样我就可以将点放置在缩短线上的任何我想要的位置(例如缩短线的 25%)。
有人知道解决办法吗?非常感谢
答案1
这里的问题是,缩短路径是在路径构建的最后阶段进行的,因此诸如定位节点之类的操作不会注意到缩短。我怀疑这是因为缩短是shorten >=15pt
通过与添加箭头尖端相同的机制完成的,通常这不会影响路径上事物的定位。
因此,实现此目的的一种方法是强制缩短时间。理想情况下,人们会在计算定位内容之前执行此操作,但我不知道在哪里执行此操作,而且我碰巧知道在定义路径后指定路径上点的另一种方法,所以我改用这种方法。
使用 会使生活变得稍微复杂一些edge
,因为它会创建一个在它自己的范围内绘制的单独路径,因此我们需要使用一些global
s 来将事物从这些范围中取出。
这里有两种方法可以实现您的目标。第一种方法,我们保存缩短的路径,然后使用spath3
库将圆圈放置在路径的末端。第二种方法,我们保存原始路径,然后使用库spath3
将其缩短 - 这样做的好处是缩短是真正沿着路径进行的(每张图片中的绿线都是原始的未缩短路径)。
在这两种情况下,我们都使用spath3
TikZ 库,然后使用语法访问缩短路径上位置的坐标(spath cs:<path name> <position>)
。
\documentclass{article}
%\url{https://tex.stackexchange.com/q/652865/86}
\usepackage{tikz}
\usetikzlibrary{spath3,intersections}
\makeatletter
\tikzset{
shorten path early/.code={
\tikz@addmode{%
\pgf@prepare@end@of@path
\pgf@prepare@start@of@path
\pgfsetshortenstart{0pt}%
\pgfsetshortenend{0pt}%
}%
},
shorten then name path/.style={
shorten path early,
spath/save global=#1
}
}
\makeatother
\begin{document}
\begin{tikzpicture}
% Version 1: save the shortened path
\begin{scope}
\coordinate (A) at (3.5,3.5);
\coordinate (B) at (5.1,2.);
\draw[color=red,fill=red] (A) circle[radius=0.15cm];
\draw[color=red,fill=red] (B) circle[radius=0.15cm];
\draw[ultra thick, green] (A) edge[out=-20,in=140] (B);
\draw (A) edge[out=-20,in=140,shorten >=15pt, shorten <=15pt, shorten then name path=short] coordinate[pos=0] (C) coordinate[pos=1] (D) (B);
\draw[black,fill=black] (C) circle[radius=0.05cm];
\draw[black,fill=black] (D) circle[radius=0.05cm];
\fill[magenta] (spath cs:short 0) circle[radius=0.05cm];
\fill[magenta] (spath cs:short 1) circle[radius=0.05cm];
\end{scope}
% Version 2: save the path and shorten it afterwards
\begin{scope}[xshift=3cm]
\coordinate (A) at (3.5,3.5);
\coordinate (B) at (5.1,2.);
\draw[color=red,fill=red] (A) circle[radius=0.15cm];
\draw[color=red,fill=red] (B) circle[radius=0.15cm];
\draw (A) edge[ultra thick, green, out=-20,in=140, spath/save global=short] coordinate[pos=0] (C) coordinate[pos=1] (D) (B);
\tikzset{
spath/shorten at both ends={short}{15pt}
}
\draw[spath/use=short];
\draw[black,fill=black] (C) circle[radius=0.05cm];
\draw[black,fill=black] (D) circle[radius=0.05cm];
\fill[magenta] (spath cs:short 0) circle[radius=0.05cm];
\fill[magenta] (spath cs:short 1) circle[radius=0.05cm];
\end{scope}
\end{tikzpicture}
\end{document}
经过思考,我认为spath3
图书馆应该在保存路径之前应用缩短。我在开发版本中实现了它(在github
)。使用该版本,可以进行以下操作:
\begin{tikzpicture}
\coordinate (A) at (3.5,3.5);
\coordinate (B) at (5.1,2.);
\draw[color=red,fill=red] (A) circle[radius=0.15cm];
\draw[color=red,fill=red] (B) circle[radius=0.15cm];
\draw (A) edge[ultra thick, green, out=-20,in=140,shorten >=15pt, shorten <=15pt, spath/save global=short] coordinate[pos=0] (C) coordinate[pos=1] (D) (B);
\draw[black,fill=black] (C) circle[radius=0.05cm];
\draw[black,fill=black] (D) circle[radius=0.05cm];
\fill[magenta] (spath cs:short 0) circle[radius=0.05cm];
\fill[magenta] (spath cs:short 1) circle[radius=0.05cm];
\end{tikzpicture}
答案2
现在我只能想到使用markings
如下方式访问这些点:
\documentclass[tikz, border=1cm]{standalone}
\usetikzlibrary{decorations.markings}
\begin{document}
\begin{tikzpicture}
\coordinate (A) at (3.5,3.5);
\coordinate (B) at (5.1,2.);
\fill[red] (A) circle[radius=0.15cm];
\fill[red] (B) circle[radius=0.15cm];
\draw[
shorten >=15pt, shorten <=15pt,
postaction=decorate,
decoration={markings,
mark=at position 0 with {\coordinate (C) at (15pt,0);},
mark=at position 1 with {\coordinate (D) at (-15pt,0);}
},
] (A) to[out=-20,in=140] (B);
\fill (C) circle[radius=0.05cm];
\fill (D) circle[radius=0.05cm];
\end{tikzpicture}
\end{document}
答案3
这是一个有趣的问题(并且15pt
有很多缩短)!
最好创建一个自定义to path
的,只移动起始和目标/结束坐标确定的量,而不使用实际的缩短量。可能还有一种方法可以做到这一点decorations.markings
图书馆。
基本上,所有沿路径的节点定位均由 TikZ 完成。每次使用路径运算符(如
(a move to
)、--
(a line to
)(也-|
和|-
)或 )时.. controls … ..
,TikZ 都会保存起始坐标、结束坐标以及(在后者情况下)控制点。
是的,到最后所有这些bend
s、out
s 和s 都只是一条道路。in
.. controls (<p1>) and (<p2>) ..
但我们可以改变这个计时器。然而:计时器不知道它的路径是在路径的起点(或移动之后)还是在路径的终点(移动之前)。
这行
\draw[shorten <=15pt] (0,0) node{S} -- (1,0)
to[out=90, in=30] node[at start]{x} (2,0);
将从 开始(15pt,0)
,节点x
仍应位于。这就是为什么我将提供实际激活新计时器的(1,0)
密钥。shortening position
当然,如果您只使用具有一个段的路径,则可以将此键赋予范围。
代码
\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\tikzset{en/.style={edge node={coordinate[pos=#1/10] (c-#1)}}}
\makeatletter
\def\tikz@timer@curve@shorten{% tikz.code.tex, l 4947
\pgftransformcurveattime{\tikz@time}
{\pgfpointlineatdistance{\pgf@shorten@start@additional}{\tikz@timer@start}{\tikz@timer@cont@one}}
{\tikz@timer@cont@one}{\tikz@timer@cont@two}
{\pgfpointlineatdistance{\pgf@shorten@end@additional}{\tikz@timer@end}{\tikz@timer@cont@two}}}
\tikzset{shortening position/.code=\let\tikz@timer@curve\tikz@timer@curve@shorten}
\makeatother
\begin{document}
\begin{tikzpicture}[radius=.15cm]
\filldraw[red] (3.5,3.5) coordinate (A) circle[]
(5.1,2.0) coordinate (B) circle[];
\draw (A) edge[out=-20,in=140,shorten >=15pt, shorten <=15pt, en/.list={0,...,10}, shortening position] (B);
\foreach \pos in {0,...,10}
\draw[gray, shorten <=2pt] (c-\pos) --
node[right,at end,inner sep=0pt,sloped,font=\tiny]
{\pgfmathprint{!mod(\pos,2)?\pos:""}} +(45:.2);
% the shortening is done at (0,0), the curve timer doesn't know about this
\draw[shorten <=15pt] (0,0) node{S} -- (1,0) to[out=90, in=30] node[at start]{x} (2,0);
\end{tikzpicture}
\end{document}
代码
答案4
这是关于路径操作的一个有趣问题。我认为 TikZ 可以做到这一点,只需付出一些努力,请参阅上述答案。
这里我展示了 Asymptote 可以使用内置例程轻松做到这一点:arctime
,,arcpoint
subpath
见Asymptote 文档,路径和指南)。我用的是cm
单位,所以L=15pt/cm;
是15pt
改成cm
。
// Determine a point on a given curve
// via its length on the curve
// http://asymptote.ualberta.ca/
unitsize(1.5cm);
pair A=(3.5,3.5), B=(5.1,2);
path pAB=A..controls A+2dir(-20) and B+2dir(140)..B;
draw(pAB,green+1.5pt);
dot("$A$",align=W,A,red);
dot("$B$",align=E,B,red);
real L=15pt/cm;
real tC=arctime(pAB,L);
pair C=arcpoint(pAB,L);
real tD=arctime(pAB,arclength(pAB)-L);
pair D=arcpoint(pAB,arclength(pAB)-L);
path pCD=subpath(pAB,tC,tD); // a subpath of pAB
draw(pCD);
dot("$C$",align=NE,C);
dot("$D$",align=SW,D);
write("L is ",L);
write("The length of pAB is ",arclength(pAB));
write("The length of pCD is ",arclength(pCD));
write("Checking that 2*L+arclength(pCD) ",2*L+arclength(pCD));
write("that is arclength(pAB) with error 10^{-12}");
shipout(bbox(5mm,invisible));
Asymptote 可以在交互模式下打印出它的计算结果,如下所示,用于检查C
和D
是否15pt
来自初始曲线的两个端点pAB
。
附录通过其相对时间(路径上相对于其弧长的分数 l 的时间)其中point
和subpath
。
// Determine a point on a given curve
// via its relative time
// http://asymptote.ualberta.ca/
unitsize(1.5cm);
pair A=(3.5,3.5), B=(5.1,2);
path pAB=A..controls A+2dir(-20) and B+2dir(140)..B;
draw(pAB,green+1.5pt);
dot("$A$",align=W,A,red);
dot("$B$",align=E,B,red);
real tC=.05, tD=.88; // relative times in [0,1]
pair C=point(pAB,tC);
pair D=point(pAB,tD);
path pCD=subpath(pAB,tC,tD); // a subpath of pAB
draw(pCD);
dot("$C$",align=NE,C);
dot("$D$",align=SW,D);
shipout(bbox(5mm,invisible));