如何将(爱好)路径一分为二

如何将(爱好)路径一分为二

我正在尝试制作一个结图,并可视化结的变形,我想从某个点分割路径并使其沿两个方向延伸。我尝试通过在某个位置用另一个 Hobby 路径装饰结路径来获得我想要的结果,如下所示:

\documentclass[a4paper]{article}
\usepackage{tikz}
\usetikzlibrary{hobby,decorations.markings}

\begin{document}
        \begin{tikzpicture}[scale=2,use Hobby shortcut]
        \draw[line width=3,white,double=black,double distance=1,postaction={decorate,decoration={markings,mark=at position 0.8 with {
        \draw[use Hobby shortcut,dashed,line width=1,black] (0,0) .. (0.5,-0.5) .. (0,-1) .. (-3.3,0.3);
        }}},closed]
(180:1) .. (240:0.9)
        .. ([blank=soft]300:0.3) .. (120:0.3)
        .. ([blank=soft]60:0.9) .. (0:1) .. (300:0.9)   .. (240:0.3)
        .. ([blank=soft,]60:0.3) .. (120:0.9) [save Hobby path={trefoil}];
        \draw[line width=3,white,double=black,double distance=1,restore and use Hobby path={trefoil}{invert soft blanks,disjoint}];
        \end{tikzpicture}
\end{document}

现在的情况是这样的:

很漂亮,但不是我想要的 :(

这其实已经接近我想要的了(我只需要将虚线连接到结的左下部分)!但是,寻找绘制虚线的正确坐标的反复试验过程非常繁琐。

以下是我所寻找的内容,具体如下:

  • 我是否可以从其他选择的位置将路径一分为二?
  • 是否可以使分割平滑(即虚线的切线与结的切线相匹配)?
  • 是否有可能以某种方式检索某个点处的结点路径的角度?然后,也许可以绘制一条从结点开始以某个角度开始的 Hobby 路径。
  • 如果其他方法都失败了,我决定只画一条路径作为装饰(就像我现在所做的那样),是否可以使用“全局”坐标来绘制装饰?现在,装饰的坐标 (0,0) 表示结上的当前位置。有没有办法改变它,使它表示原点?

这可能要求太多了。另一方面,所有这些可能都有一个非常简单的解决方案,而我忽略了或找不到。我非常感谢任何帮助实现这一点的帮助(此外,如果有些事情不清楚,我很乐意澄清)。

PS 在寻找答案时,我突然意识到这可能不是一个爱好问题,因此标题中带有括号......

答案1

几乎所有的碎片都在 TikZ 和业余爱好代码的某个地方,只需要稍微组装一下拼图碎片并修复业余爱好代码中的两个错误。错误修复将(最终)进入业余爱好代码。其余的也可能在某些地方非常有用(我对结库有一些想法)。

简而言之,新节点可以放置在交叉点仿佛已指定键pos。这意味着节点可以旋转以位于该点处的曲线上(使用交​​点库提供的坐标不允许最后一点)。然后其东/西锚点位于该点处与曲线相切的线上,可用于定义新曲线的控制点。

交叉点库提供了所有必要的信息,关键在于提取这些信息。请参阅下面的代码以了解如何做到这一点。

业余爱好错误与将节点放置在业余爱好生成的曲线的部分有关。这些问题现在已在业余爱好版本中得到修复github(运行tex hobby.dtx以生成文件)所以我从下面的代码中删除了它们。

\documentclass[a4paper]{article}
%\url{https://tex.stackexchange.com/q/372089/86}
\usepackage{tikz}
\usetikzlibrary{hobby,intersections,calc}

\makeatletter
\tikzset{
  place at intersection with code/.code={%
    % Test to see if the named path exists, if not display a message
    \pgfutil@ifundefined{tikz@intersect@path@name@#1}{\message{Path `#1' not found.}}{\find@intersection@point{#1}}
  },
  place at intersection with/.style={
    % This option defines the intersection point
    place at intersection with code=#1,
    node contents={},
    % The next two mean that the placed node will align itself with the curve
    sloped,
    allow upside down=true
  }
}

\def\find@intersection@point#1{%
  % Do everything inside a group so as not to upset things
  \begingroup
  % First step is to reconstruct the last path segment, using the \tikz@timer stuff
  \def\intersection@pathsegment{}%
  % Save the current path
  \pgfsyssoftpath@getcurrentpath\intersection@temppath
  % And initialise with an empty path
  \pgfsyssoftpath@setcurrentpath\intersection@pathsegment%
  % Move to the starting point
  \pgfpathmoveto{\tikz@timer@start}%
  % The rest depends on the type of segment we just had
  \ifx\tikz@timer\tikz@timer@curve
  % Bezier curve
  \pgfpathcurveto{\tikz@timer@cont@one}{\tikz@timer@cont@two}{\tikz@timer@end}%
  \else
  \ifx\tikz@timer@line
  % Straight line
  \pgfpathlineto{\tikz@timer@end}%
  \else
  \ifx\tikz@timer\tikz@timer@hvline
  % Horizontal-Vertical line
  \tikz@timer@start
  \pgf@ya=\pgf@y
  \tikz@timer@end
  \pgf@xa=\pgf@x
  \pgfpathlineto{\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}%
  \pgfpathlineto{\tikz@timer@end}%
  \else
  \ifx\tikz@timer\tikz@timer@vhline
  % Vertical-horizontal line
  \tikz@timer@start
  \pgf@xa=\pgf@x
  \tikz@timer@end
  \pgf@ya=\pgf@y
  \pgfpathlineto{\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}%
  \pgfpathlineto{\tikz@timer@end}%
  \else
  \ifx\tikz@timer\tikz@timer@arc
  % Need to find out how to reconstruct an arc ...
  \fi
  \fi
  \fi
  \fi
  \fi
  % Get the newly created path segment
  \pgfsyssoftpath@getcurrentpath\intersection@pathsegment
  % Restore the original path
  \pgfsyssoftpath@setcurrentpath\intersection@temppath%
  % Sorting the intersections is the trigger for remembering the intersection point as a parameter along the path
  \pgfintersectionsortbyfirstpath
  % Call the intersection algorithm
  \pgfintersectionofpaths{\pgfsetpath\intersection@pathsegment}{\expandafter\pgfsetpath\csname tikz@intersect@path@name@#1\endcsname}%
  %
  \ifnum\pgfintersectionsolutions>0\relax
  % If we got an intersection, store the parameter corresponding to the first one
  \xdef\intersection@time{\csname pgf@g@intersect@solution@1@time@a\endcsname}%
  \else
  % If not, say so and default to the start of the segment
  \message{No intersection found}%
  \gdef\intersection@time{0}%
  \fi
  \endgroup
  % Set the position of the current node to the found parameter
  \tikzset{pos=\intersection@time}%
}
\makeatother

\begin{document}

\begin{tikzpicture}[scale=2,use Hobby shortcut]
% This is the path we'll use to define our cutting points.  The 'overlay' option means that it doesn't affect the bounding box
\path[name path=c,overlay] (-.3,0) -- +(0,1) -- +(0,-1);
% Define our hobby curve
\draw[line width=3,white,double=black,double distance=1,closed]
(180:1) .. (240:0.9)
% The 'place at intersection with=c' key puts this node at the intersection of this segment with path 'c'
% Even though we've specified `node contents={}` in the style, we still need the trailing {} due to how nodes are collected on paths
.. node[place at intersection with=c,name=dpt] {}  ([blank=soft]300:0.3) .. (120:0.3) 
.. ([blank=soft]60:0.9) .. (0:1) .. (300:0.9)   .. (240:0.3)
.. ([blank=soft,]60:0.3) .. node[place at intersection with=c,name=upt] {}  (120:0.9) [save Hobby path={trefoil}];
\draw[line width=3,white,double=black,double distance=1,restore and use Hobby path={trefoil}{invert soft blanks,disjoint}];
% Once we have our nodes placed, we can use the anchors to define the 'exit' paths.  The centre node is at the intersection point and the east-west line goes along the tangent line.
\draw[dashed] (upt.center) .. controls ($(upt.center)!2cm!(upt.east)$) and +(0,2) .. (1.5,0) .. controls +(0,-2) and  ($(dpt.center)!2cm!(dpt.west)$) .. (dpt.center);
\end{tikzpicture}
\end{document}

我希望这就是你所追求的。

Reidemeister 的举动

相关内容