这里有图片。
PSTricks 有一个非常简洁的命令\pscurve
。手册给出了示例
\pscurve[showpoints=true]{<->}(0,1.3)(0.7,1.8) (3.3,0.5)(4,1.6)(0.4,0.4)
得出
有人知道如何在 TikZ 中获得相同的行为吗?
我尝试过各种方法,包括
\tikz \draw plot[smooth] coordinates {(0,1.3) (0.7,1.8) (3.3,0.5) (4,1.6) (0.4,0.4)};
这让我
(我确实尝试过控制张力!)最终的曲线看起来总是像一连串带有圆角的直线段。
谢谢你的积分!
答案1
据我所知,这不能“开箱即用”。但是,只要进行一点编程,就没有理由做不到。具体需要什么编程取决于问题的确切规格:是准确复制 pstricks 的行为,还是只是做一些类似的事情?如果是前者,那么您需要深入研究 pstricks 代码并提取它如何决定在哪里绘制线条的公式。如果是后者,那么进行一点实验就可以产生合理的结果。
我不能保证下面的方法会通过任意点族产生一条平滑的曲线,但这是一种合理的方法。绘图命令由指定点之间的贝塞尔曲线完成。困难在于找出合适的控制点。为此,需要向前看下一个点并找出一对合适的控制点。我选择做的是想象一条连接前一个点和下一个点的线,取它的中点,然后将其连接到当前点。通过当前点取与此线正交的线给了我控制点的方向。长度与当前点和前一个/下一个点之间的距离成比例(视情况而定)。
对于指定的控制点,这给我:
\documentclass{minimal}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}
\let\pcoord\relax
\let\tcoord\relax
\foreach [count=\num] \coord in {
(0,1.3),
(0.7,1.8),
(3.3,0.5),
(4,1.6),
(0.4,0.4)
} {
\ifx\pcoord\relax
\global\let\pcoord\coord
\path \pcoord coordinate (c1);
\else
\ifx\tcoord\relax
\global\let\tcoord\coord
\else
\path \pcoord coordinate (p);
\path \tcoord coordinate (t);
\path \coord coordinate (n);
\path ($(p)!.75!(n)$) coordinate (m);
\path ($(t)!1cm!90:(m)$) coordinate (r);
\path ($(t)-(p)$);
\pgfgetlastxy{\xx}{\yy}
\pgfmathsetmacro{\len}{.5*veclen(\xx,\yy)}
\path ($(t)!(p)!(r)$) coordinate (rp);
\path ($(t)!\len pt!(rp)$) coordinate (c2);
\draw (p) .. controls (c1) and (c2) .. (t);
\path ($(t)-(n)$);
\pgfgetlastxy{\xx}{\yy}
\pgfmathsetmacro{\len}{.5*veclen(\xx,\yy)}
\path ($(t)!(n)!(r)$) coordinate (rn);
\path ($(t)!\len pt!(rn)$) coordinate (c1);
\global\let\pcoord\tcoord
\global\let\tcoord\coord
\fi
\fi
}
\draw (t) .. controls (c1) and (n) .. (n);
\end{tikzpicture}
\end{document}
(如果“愤怒地”完成,人们可能应该担心边界框。我没有。)
- 最好使用外接圆的切线来计算控制点的方向(这会使第一个看起来更好)。
- 将整个事物设为一条路径而不是几条路径的连接会更好。
答案2
我尝试将@Andrew的想法重新创建为一个情节处理程序。但是出于某种原因,我得到了不同的结果。也许有人可以告诉我哪里出了问题(已经晚了……)。无论如何,这是情节处理程序:
\makeatletter
\def\pgfplothandlermycurveto{%
\def\pgf@plotstreamstart{%
\global\let\pgf@plotstreampoint=\pgf@plot@mycurveto@handler@initial%
\global\let\pgf@plotstreamspecial=\pgfutil@gobble%
\global\let\pgf@plotstreamend=\pgf@plot@mycurveto@handler@finish%
\global\pgf@plot@startedfalse%
}%
}
\def\pgf@plot@mycurveto@handler@initial#1{%
\pgf@process{#1}%
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
\pgf@plot@first@action{\pgfqpoint{\pgf@xa}{\pgf@ya}}%
\xdef\pgf@plot@mycurveto@first{\noexpand\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}%
\global\let\pgf@plot@mycurveto@first@support=\pgf@plot@mycurveto@first%
\global\let\pgf@plotstreampoint=\pgf@plot@mycurveto@handler@second%
}
\def\pgf@plot@mycurveto@handler@second#1{%
\pgf@process{#1}%
\xdef\pgf@plot@mycurveto@second{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}%
\global\let\pgf@plotstreampoint=\pgf@plot@mycurveto@handler@third%
\global\pgf@plot@startedtrue%
}
\def\pgf@plot@mycurveto@handler@third#1{%
\pgf@process{#1}%
\xdef\pgf@plot@mycurveto@current{\noexpand\pgfqpoint{\the\pgf@x}{\the\pgf@y}}%
% compute midpoint:
\pgf@xa=\pgf@x%
\pgf@ya=\pgf@y%
\pgf@process{\pgf@plot@mycurveto@first}
\advance\pgf@xa by\pgf@x%
\advance\pgf@ya by\pgf@y%
\pgf@xa=0.5\pgf@xa%
\pgf@ya=0.5\pgf@ya%
\pgf@xb=\pgf@xa%
\pgf@yb=\pgf@ya%
% vector from second to midpoint:
\pgf@process{\pgf@plot@mycurveto@second}
\advance\pgf@xa by-\pgf@x%
\advance\pgf@ya by-\pgf@y%
% vector from first to midpoint:
\pgf@process{\pgf@plot@mycurveto@first}
\advance\pgf@xb by-\pgf@x%
\advance\pgf@yb by-\pgf@y%
% normalize
\pgfmathparse{\pgf@plottension / veclen(\pgf@xa,\pgf@ya)}%
\let\pgf@l@dir=\pgfmathresult
\pgfmathsetlength{\pgf@xa}{\pgf@xa * \pgf@l@dir}%
\pgfmathsetlength{\pgf@ya}{\pgf@ya * \pgf@l@dir}%
% compute orientation
\pgfmathparse{\pgf@xa * \pgf@yb - \pgf@ya * \pgf@xb}%
\pgfmathparse{-greater(\pgfmathresult,0) + less(\pgfmathresult,0)}
\pgf@xa=\pgfmathresult\pgf@xa%
\pgf@ya=\pgfmathresult\pgf@ya%
% load second point
\pgf@process{\pgf@plot@mycurveto@second}%
\pgf@xb=\pgf@x%
\pgf@yb=\pgf@y%
\pgf@xc=\pgf@x%
\pgf@yc=\pgf@y%
% compute lengths
\pgf@process{\pgf@plot@mycurveto@first}%
\pgfmathparse{veclen((\pgf@xb-\pgf@x),(\pgf@yb-\pgf@y))}%
\let\pgf@l@first=\pgfmathresult%
\pgf@process{\pgf@plot@mycurveto@current}%
\pgfmathparse{veclen(\pgf@xb-\pgf@x,\pgf@yb-\pgf@y)}%
\let\pgf@l@second=\pgfmathresult%
% first marshal:
\pgfmathaddtolength{\pgf@xb}{-\pgf@l@first * \pgf@ya}%
\pgfmathaddtolength{\pgf@yb}{\pgf@l@first * \pgf@xa}%
\pgfmathaddtolength{\pgf@xc}{\pgf@l@second * \pgf@ya}%
\pgfmathaddtolength{\pgf@yc}{-\pgf@l@second * \pgf@xa}%
\edef\pgf@marshal{\noexpand\pgfpathcurveto{\noexpand\pgf@plot@mycurveto@first@support}%
{\noexpand\pgfqpoint{\the\pgf@xb}{\the\pgf@yb}}{\noexpand\pgf@plot@mycurveto@second}}%
{\pgf@marshal}%
% Prepare next:
\global\let\pgf@plot@mycurveto@first=\pgf@plot@mycurveto@second%
\global\let\pgf@plot@mycurveto@second=\pgf@plot@mycurveto@current%
\xdef\pgf@plot@mycurveto@first@support{\noexpand\pgfqpoint{\the\pgf@xc}{\the\pgf@yc}}%
}
\def\pgf@plot@mycurveto@handler@finish{%
\ifpgf@plot@started%
\pgfpathcurveto{\pgf@plot@mycurveto@first@support}{\pgf@plot@mycurveto@second}{\pgf@plot@mycurveto@second}%
\fi%
}
\tikzoption{mysmooth}[]{\let\tikz@plot@handler=\pgfplothandlermycurveto}
\makeatother
为了获得绘图的单一路径,必须手动进行计算,而不使用任何其他路径。如果您在之后的某个地方包含上述代码\usepackage{tikz}
,则可以使用该mysmooth
选项启动绘图处理程序。它会考虑张力并产生单一路径。
例如:
\tikz {
\draw plot[mysmooth,mark=x] coordinates {(0,1.3)(0.7,1.8) (3.3,0.5)(4,1.6)(0.4,0.4)};
\draw[red,densely dotted] plot[mysmooth,tension=1.5] coordinates {(0,1.3)(0.7,1.8) (3.3,0.5)(4,1.6)(0.4,0.4)};
\draw[gray,dashed] plot[smooth,tension=0.5] coordinates {(0,1.3)(0.7,1.8) (3.3,0.5)(4,1.6)(0.4,0.4)};
\draw[dotted,gray] (0,0) grid (4,2);
}
生产
。
答案3
我刚刚尝试了以下解决方案,它们可以编译并给出“正确”的输出:将 PSTricks 图片放入 TikZ 节点内(这是圣诞节,奇迹比比皆是)。
\documentclass[10pt]{article}
\usepackage{pstricks,tikz}
\begin{document}
\begin{tikzpicture}
\node {%
\begin{pspicture}(4,2)
\pscurve[showpoints=true]{<->}(0,1.3)(0.7,1.8) (3.3,0.5)(4,1.6)(0.4,0.4)
\end{pspicture}%
};
\draw [red] (0,0) -- (1,1);
\end{tikzpicture}
\end{document}
这里的红线只是为了确保我们可以混合 PSTricks 对象和 Tikz 对象。
我认为如果我们尝试更奇特的事情,我们可能会陷入冲突,但至少对于简单的事情来说,这似乎是一个解决方案。
谢谢 Herbert – 正是你的问题让我有了尝试这个技巧的想法。
答案4
\documentclass[10pt]{article}
\usepackage{pstricks,tikz}
\begin{document}
\begin{tikzpicture}(4,2)
\node at (0,0){%
\pscurve[showpoints]{<->}(0,1.3)(0.7,1.8) (3.3,0.5)(4,1.6)(0.4,0.4)
};
\draw [red] (0,0) -- (1,1);
\end{tikzpicture}
\end{document}