Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reverse arrow disturbs smooth paths (draw plot) #1290

Open
tcpaiva opened this issue Nov 4, 2023 · 3 comments
Open

Reverse arrow disturbs smooth paths (draw plot) #1290

tcpaiva opened this issue Nov 4, 2023 · 3 comments

Comments

@tcpaiva
Copy link

tcpaiva commented Nov 4, 2023

Brief outline of the bug

The curve of the line for
\draw[]plot[smooth]coordinates{(0,1)(1,0)(2,1)}
is the same for
\draw[->]plot[smooth]coordinates{(0,1)(1,0)(2,1)}
but it is not the same for
\draw[<-]plot[smooth]coordinates{(0,1)(1,0)(2,1)}

Minimal working example (MWE)

\documentclass{standalone}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
  \draw[->]plot[smooth]coordinates{(0,1)(1,0)(2,1)};
  \draw[<-, blue, opacity=0.3]plot[smooth]coordinates{(0,1)(1,0)(2,1)}; %% unexpected
\end{tikzpicture}
\end{document}
@muzimuzhi
Copy link
Member

muzimuzhi commented Nov 4, 2023

diff --git a/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex b/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex
index 5a9c5fc2..a29609f9 100644
--- a/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex
+++ b/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex
@@ -622,7 +626,7 @@
 % Line shortening for straight lines:
 %
 \def\pgf@do@shorten@straightstart{%
-  \edef\pgfprocessresultpathsuffix{\pgfsubpathfirsttoken{\the\pgf@xa}{\the\pgf@ya}\pgfsubpathsecondtoken{\the\pgf@xc}{\the\pgf@yc}}%
+  \edef\pgfprocessresultpathsuffix{\pgfsubpathfirsttoken{\the\pgf@xa}{\the\pgf@ya}\pgfsubpathsecondtoken{\the\pgf@xa}{\the\pgf@ya}}%
   \expandafter\expandafter\expandafter\def%
   \expandafter\expandafter\expandafter\pgfprocessresultpathsuffix%
   \expandafter\expandafter\expandafter{\expandafter\pgfprocessresultpathsuffix\pgfsubpath
end}%
Full example

\documentclass{article}
\usepackage{tikz}
\usepackage{xpatch}

\makeatletter
\xpatchcmd\pgf@do@shorten@straightstart
  {\pgfsubpathsecondtoken{\the\pgf@xc}{\the\pgf@yc}}
  {\pgfsubpathsecondtoken{\the\pgf@xa}{\the\pgf@ya}}
  {}{\PatchFailed}
\makeatother

\begin{document}
\begin{pgfpicture}
  % all control points
  \foreach \i/\j in { 0pt/20pt, 14.45pt/0pt, 20pt/0pt, 25.55pt/0pt, 40pt/20pt }
    {
      \pgfpathcircle{\pgfqpoint{\i}{\j}}{1pt}
    }
  % new path start after shortening
  \pgfpathcircle{\pgfqpoint{0.26936pt}{19.62717pt}}{1pt}
  \pgfusepath{stroke}
  
  \pgfpathmoveto{\pgfqpoint{0pt}{20pt}}
  \pgfpathcurveto
    {\pgfqpoint{0.0pt}{20.0pt}}{\pgfqpoint{14.45007pt}{0.0pt}}{\pgfqpoint{20.0pt}{0.0pt}}
  \pgfpathcurveto 
    {\pgfqpoint{25.54993pt}{0.0pt}}{\pgfqpoint{40.0pt}{20.0pt}}{\pgfqpoint{40.0pt}{20.0pt}}
  \pgfgetpath{\mypath}
  \pgfusepath{stroke}

  \pgfsetstrokecolor{red}
  \pgfsetarrows{->}
  \pgfsetpath{\mypath}
  \pgfusepath{stroke}

  \pgfsetstrokecolor{blue}
  \pgfsetarrows{<-}
  \pgfsetpath{\mypath}
  \pgfusepath{stroke}
\end{pgfpicture}

% based on original example
\begin{tikzpicture}[every plot/.append style={smooth}]
  \draw
    plot coordinates {(0,1) (1,0) (2,1)};
  \draw[->, yshift=2pt, red, opacity=.3]
    plot coordinates {(0,1) (1,0) (2,1)};
  \draw[<-, yshift=-2pt, blue, opacity=.3]
    plot coordinates {(0,1) (1,0) (2,1)}; %% unexpected
\end{tikzpicture}
\end{document}

Before after
image image

@muzimuzhi
Copy link
Member

Ah, my patch was wrong (or too simple), when the starting and first support points of curve-to is not identical.

What really helpful is to load bending library, which will turn on precise path shortening (needed when path has arrow tips). Without bending a curve-to is treated like a straight line, which is bound to give unsatisfied shortening results in certain cases.

image

Full example 2

\documentclass{article}
\usepackage{tikz}

\usepackage{xpatch}

\begin{document}
\def\tests{
\begin{tikzpicture}[scale=3]
\def\pathP{ (0, 1) .. controls (0,1) and (.7, 0) .. (1,0) }
\def\pathQ{[xshift=1cm] (0,1) .. controls (0,.5) and (.7, 0) .. (1,0) }
\def\pathR{[xshift=2cm] (0,1) .. controls (0.5,1) .. (1,0) }
\def\pathS{[xshift=3cm] (0,1) .. controls (0,.5) and (.7, 0) .. (.7,0) }

\foreach \i in {\pathP, \pathQ, \pathR, \pathS}
  {
    \draw \i;
    \draw[->, shift={(1pt, 1pt)}, red,  opacity=.3] \i;
    \draw[<-, shift={(-1pt, -1pt)}, blue, opacity=.3] \i;
  }

\end{tikzpicture}}

\subsection*{Before (pgf-tikz v3.1.10)}
\tests

\subsection*{+ wront patch}
{
\makeatletter
\xpatchcmd\pgf@do@shorten@straightstart
{\pgfsubpathsecondtoken{\the\pgf@xc}{\the\pgf@yc}}
{\pgfsubpathsecondtoken{\the\pgf@xa}{\the\pgf@ya}}
{}{\PatchFailed}
\makeatother
\tests
}

\subsection*{+ bending library}
\usetikzlibrary{bending}
\tests
\end{document}

@muzimuzhi
Copy link
Member

Ah, my patch was wrong (or too simple), when the starting and first support points of curve-to is not identical.

An improved patch. More knowledge about Bezier curve than what I got is needed to make up more test cases.

diff --git a/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex b/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex
index 5a9c5fc2..a9d7d4f1 100644
--- a/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex
+++ b/tex/generic/pgf/basiclayer/pgfcorepathusage.code.tex
@@ -622,7 +622,13 @@
 % Line shortening for straight lines:
 %
 \def\pgf@do@shorten@straightstart{%
-  \edef\pgfprocessresultpathsuffix{\pgfsubpathfirsttoken{\the\pgf@xa}{\the\pgf@ya}\pgfsubpathsecondtoken{\the\pgf@xc}{\the\pgf@yc}}%
+  \edef\pgfprocessresultpathsuffix{%
+    \pgfsubpathfirsttoken{\the\pgf@xa}{\the\pgf@ya}%
+    \ifx\pgfpointfirstonpath\pgfpointsecondonpath
+      \pgfsubpathsecondtoken{\the\pgf@xa}{\the\pgf@ya}%
+    \else
+      \pgfsubpathsecondtoken{\the\pgf@xc}{\the\pgf@yc}%
+    \fi}%
   \expandafter\expandafter\expandafter\def%
   \expandafter\expandafter\expandafter\pgfprocessresultpathsuffix%
   \expandafter\expandafter\expandafter{\expandafter\pgfprocessresultpathsuffix\pgfsubpath
end}%

image
image

Full example 3 (containing patch v2)

\documentclass{article}
\usepackage{tikz}

\usepackage{xpatch}

\begin{document}
\def\tests{
  \begin{tikzpicture}[scale=2]
    \def\pathP{ (0, 1) .. controls (0,1) and (.7, 0) .. (1,0) }
    \def\pathQ{[xshift=1cm] (0,1) .. controls (0,.5) and (.7, 0) .. (1,0) } 
    \def\pathR{[xshift=2cm] (0,1) .. controls (0.7,.5) .. (1,0) }
    \def\pathS{[xshift=2.2cm] (1,0) .. controls (1.3,0) and (2,1) .. (2,1) }
  
    \foreach \i in {\pathP, \pathQ, \pathR, \pathS}
      {
        \draw \i;
        \draw[->, shift={(1pt, 1pt)}, red,  opacity=.3] \i;
        \draw[<-, shift={(-1pt, -1pt)}, blue, opacity=.3] \i;
      }
  \end{tikzpicture}}

\subsection*{Before (pgf-tikz v3.1.10)}
\tests

\subsection*{+ wront patch (v1)}
{
  \makeatletter
  \xpatchcmd\pgf@do@shorten@straightstart
    {\pgfsubpathsecondtoken{\the\pgf@xc}{\the\pgf@yc}}
    {\pgfsubpathsecondtoken{\the\pgf@xa}{\the\pgf@ya}}
    {}{\PatchFailed}
  \makeatother
  \tests
}

\subsection*{+ revised patch (v2)}
{
  \makeatletter
  % perhaps a symmetric patch can be applied in \pgf@do@shorten@straightend
  \xpatchcmd\pgf@do@shorten@straightstart
    {\pgfsubpathsecondtoken{\the\pgf@xc}{\the\pgf@yc}}
    {%
     % use the same condition in \pgf@prep@straightstart
     \ifx\pgfpointfirstonpath\pgfpointsecondonpath
       \pgfsubpathsecondtoken{\the\pgf@xa}{\the\pgf@ya}%
     \else
       \pgfsubpathsecondtoken{\the\pgf@xc}{\the\pgf@yc}%
     \fi
    }
    {}{\PatchFailed}
  \makeatother
  \tests
}

\subsection*{+ bending library}
\usetikzlibrary{bending}
\tests
\end{document}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants