loopspace / spath3

TikZ/PGF package for manipulating soft paths, includes the knots and calligraphy TikZ libraries.
16 stars 2 forks source link

4 intersections with 4 lines in a flowchart + 1 redundant arc/intersection (bug) #24

Closed Andrew15-5 closed 1 year ago

Andrew15-5 commented 1 year ago

With my setup: https://github.com/loopspace/spath3/issues/23#issuecomment-1444223270 I can do simple stuff like this: arc, arc arc, rarc

But I failed to do the petty "pattern" (it probably has a name): each of 4 lines go over only 1 of the other lines: 4 arcs + 1 redundant

But somehow a new 5th arc (red number 1) is introduced when I "requested" only 4.

source code ```latex \documentclass{article} \usepackage{tikz} \usepackage{spath3} \usetikzlibrary{positioning,arrows.meta,knots} \tikzset{ bridging path/.initial=intersection arc, bridging rpath/.initial=intersection reverse arc, bridging span/.initial=8pt, bridging gap/.initial=4pt, bridge/.style 2 args={ spath/split at intersections with={#1}{#2}, spath/insert gaps after components={#1}{\pgfkeysvalueof{/tikz/bridging span}}, spath/join components upright with={#1}{\pgfkeysvalueof{/tikz/bridging path}}, spath/split at intersections with={#2}{#1}, spath/insert gaps after components={#2}{\pgfkeysvalueof{/tikz/bridging gap}}, }, rbridge/.style 2 args={ spath/split at intersections with={#1}{#2}, spath/insert gaps after components={#1}{\pgfkeysvalueof{/tikz/bridging span}}, spath/join components upright with={#1}{\pgfkeysvalueof{/tikz/bridging rpath}}, spath/split at intersections with={#2}{#1}, spath/insert gaps after components={#2}{\pgfkeysvalueof{/tikz/bridging gap}}, }, } \AtBeginDocument{% \tikz[overlay] { \path[spath/save global=intersection arc] (0,0) arc[ radius=1cm, start angle=180, delta angle=-180]; }% \tikz[overlay] { \path[spath/save global=intersection reverse arc] (0,0) arc[ radius=1cm, start angle=-180, delta angle=180]; }% } \begin{document} \fontsize{14}{14} \def\offset{8pt} \begin{tikzpicture}[ node distance=4cm, arrow/.style={-Triangle, line width=1pt}, ] \node (c) {}; \node[draw,above=of c] (above) {above}; \node[draw,left=of c] (left) {left}; \node[draw,below=of c] (below) {below}; \node[draw,right=of c] (right) {right}; \path[spath/save=abovebelow] ([xshift=-\offset]above.south) -- ([xshift=-\offset]below.north) node[start, above, rotate=90] {above--below}; \path[spath/save=belowabove] ([xshift=\offset]below.north) -- ([xshift=\offset]above.south) node[start, below, rotate=90] {below--above}; \path[spath/save=leftright] ([yshift=-\offset] left.east) -- ([yshift=-\offset] right.west) node[start, below] {left--right}; \path[spath/save=rightleft] ([yshift=\offset] right.west) -- ([yshift=\offset] left.east) node[start, above] {right--left}; \tikzset{ bridging span/.initial=8pt, bridging gap/.initial=4pt, bridge={abovebelow}{rightleft}, bridge={leftright}{abovebelow}, bridge={belowabove}{leftright}, bridge={rightleft}{belowabove}, % bridge={abovebelow}{rightleft}, % bridge={abovebelow}{leftright}, % rbridge={belowabove}{rightleft}, % rbridge={belowabove}{leftright}, } \draw[arrow, spath/use=abovebelow]; \draw[arrow, spath/use=belowabove]; \draw[arrow, spath/use=leftright]; \draw[arrow, spath/use=rightleft]; \end{tikzpicture} \end{document} ```
loopspace commented 1 year ago

I can see what's causing this, and will need to think a bit about how to circumvent it. The issue is that the bridge code inserts the hoop into every gap in the path, but when you break the right-left path at the intersection of the below-above path then the issue is that the right-left path is already broken where it crosses the above-below path and the code inserts a hoop at that break as well as the intended break.

So the goal will be to figure out how to patch the code to ensure that the bridge is only inserted at the new break and not at the pre-existing breaks. I'll need to think a bit about how to do that.

loopspace commented 1 year ago

Here's some code for you. Incidentally, one reason why the bridging code is not (yet) in the core code is because I don't use it myself so haven't experimented with all the variations. I don't want to fix the code until it's had lots of opportunity to see how the edge cases work, such as this.

Firstly, and most insignificantly, you don't need the rbridge code. Since there's an option to specify the bridging path all you need to do to get the opposite path is to change this. In the code below I include a command that sets the bridging path depending on whether a parameter is empty or not. The {t} or {} is there to signal whether to use the original or reversed path.

Secondly, the problem is that when the final path is considered then it already has a break in it. Ideally, the code should only insert the jumps at new breaks and ignore existing ones. That's possible, but a bit tricker to code. So I went for a simpler solution which was to change the order in which everything happened. Rather than do bridge, break, bridge, break, bridge, break, bridge, break then I went for bridge, bridge, bridge, bridge, break, break, break, break. That way, all the bridges are added to their over paths before any of the under paths are broken.

To condense the code, I added a few more styles. In particular, one that takes a list and calls the insert bridges on each pair in the list first, and then insert gaps afterwards.

\documentclass{article}
\usepackage{tikz}
\usepackage{spath3}
\usetikzlibrary{positioning,arrows.meta,knots}

\tikzset{
  bridging path/.initial=intersection arc,
  bridging span/.initial=8pt,
  bridging gap/.initial=4pt,
  swap bridging path/.code={%
    \ifx#1\relax\relax
    \tikzset{bridging path=intersection arc}%
    \else
    \tikzset{bridging path=intersection reverse arc}%
    \fi
  },
  insert bridges/.style n args={3}{%
    swap bridging path={#3},
    spath/split at intersections with={#1}{#2},
    spath/insert gaps after
      components={#1}{\pgfkeysvalueof{/tikz/bridging span}},
    spath/join components upright
    with={#1}{\pgfkeysvalueof{/tikz/bridging path}},
  },
  insert gaps/.style n args={3}{%
    spath/split at intersections with={#2}{#1},
    spath/insert gaps after
      components={#2}{\pgfkeysvalueof{/tikz/bridging gap}},
  },
  process bridge pairs/.style={
    insert bridges/.list={#1},
    insert gaps/.list={#1},
  }
}

\AtBeginDocument{%
  \tikz[overlay] { \path[spath/save global=intersection arc] (0,0) arc[
      radius=1cm, start angle=180, delta angle=-180]; }%
  \tikzset{
    spath/clone global={intersection reverse arc}{intersection arc},
    spath/reverse globally=intersection reverse arc
  }
}

\begin{document}
\fontsize{14}{14}

\def\offset{8pt}
\begin{tikzpicture}[
    node distance=4cm,
    arrow/.style={-Triangle, line width=1pt},
    start/.style={pos=.2}
  ]

  \node (c) {};
  \node[draw,above=of c] (above) {above};
  \node[draw,left=of c] (left) {left};
  \node[draw,below=of c] (below) {below};
  \node[draw,right=of c] (right) {right};

  \path[spath/save=abovebelow]
    ([xshift=-\offset]above.south) --
    ([xshift=-\offset]below.north)
    node[start, above, rotate=90] {above--below};
  \path[spath/save=belowabove]
    ([xshift=\offset]below.north) --
    ([xshift=\offset]above.south)
    node[start, below, rotate=90] {below--above};

  \path[spath/save=leftright]
    ([yshift=-\offset] left.east) --
    ([yshift=-\offset] right.west)
    node[start, below] {left--right};
  \path[spath/save=rightleft]
    ([yshift=\offset] right.west) --
    ([yshift=\offset] left.east)
    node[start, above] {right--left};

  \tikzset{
    bridging span/.initial=8pt,
    bridging gap/.initial=4pt,
    process bridge pairs={
      {abovebelow}{rightleft}{t},
      {leftright}{abovebelow}{},
      {belowabove}{leftright}{},
      {rightleft}{belowabove}{}
    }
  }

  \draw[arrow, spath/use=abovebelow];
  \draw[arrow, spath/use=belowabove];
  \draw[arrow, spath/use=leftright];
  \draw[arrow, spath/use=rightleft];

\end{tikzpicture}

\end{document}

crossing lines with bridges inserted

Andrew15-5 commented 1 year ago

Please use syntax highlighting for a block of code (if specific language is used). For this copy next template:

```\ language specific code goes here ```

```latex \command ```

It's very hard to read a big listing without colors (well, even small ones). Maybe it's only hard for some of us, but definitely not for the minority of us.


As an addition, I can advise using the next template to significantly reduce the vertical size of the comment if the code (or anything else) is very long:

<details><summary>Summary</summary>

previous template for a block of code goes here (above blank line is neccessary; summary tag is optional)

</details>

P.S. I used it in my first comment because the source code is not the main topic of the comment and it's very long too.

Andrew15-5 commented 1 year ago

To make it more pleasing, I changed the "switches":

image

Some commands have a `*` options, so I changed `t` to `*`. ```latex \tikzset{ bridging span/.initial=8pt, bridging gap/.initial=4pt, process bridge pairs={ {abovebelow}{rightleft}{}, {leftright}{abovebelow}{}, {belowabove}{leftright}{}, {rightleft}{belowabove}{*} } } ```

or "outer" pattern:

image

Some commands have a `*` options, so I changed `t` to `*`. ```latex \tikzset{ bridging span/.initial=8pt, bridging gap/.initial=4pt, process bridge pairs={ {abovebelow}{rightleft}{*}, {leftright}{abovebelow}{*}, {belowabove}{leftright}{*}, {rightleft}{belowabove}{} } } ```
Andrew15-5 commented 1 year ago

Unfortunately (for comparison with previous code) I made a lot of small changes. Overall I improved code structure, readability, and size. Some key points:

Some notes:

source code ```latex \documentclass{article} \usepackage{ spath3, tikz, } \usetikzlibrary{ arrows.meta, knots, positioning, } \tikzset{ arc/.initial=intersection arc, arc size/.initial=8pt, gap size/.initial=4pt, flip arc/.code={ \ifx#1\relax\relax \tikzset{arc=intersection arc} \else \tikzset{arc=intersection reverse arc} \fi }, insert arcs/.style n args={3}{ flip arc={#3}, spath/split at intersections with={#1}{#2}, spath/insert gaps after components={#1}{\pgfkeysvalueof{/tikz/arc size}}, spath/join components upright with={#1}{\pgfkeysvalueof{/tikz/arc}}, }, insert gaps/.style n args={2}{ spath/split at intersections with={#2}{#1}, spath/insert gaps after components={#2}{\pgfkeysvalueof{/tikz/gap size}}, }, flowchart intersections/.style={ insert arcs/.list={#1}, insert gaps/.list={#1}, }, } \AtBeginDocument{ \tikz[overlay]{ \path[spath/save global=intersection arc] (0,0) arc[ radius=1pt, % Value doesn't matter, therefore it's the shortest & smallest start angle=0, % More readable values for angles delta angle=180 ]; } \tikzset{ spath/clone global={intersection reverse arc}{intersection arc}, spath/reverse globally=intersection reverse arc } } \DeclareDocumentCommand\spath{O{} m +m}{\path[#1, spath/save=#2] #3} \DeclareDocumentCommand\sdraw{O{} m}{\draw[#1, spath/use=#2]} \begin{document} \fontsize{14}{14} \def\offset{8pt} \begin{tikzpicture}[ node distance=4cm, arrow/.style={-Triangle, line width=1pt}, every node/.style={near start}, ] \node (c) {}; \node[draw, above=of c] (above) {above}; \node[draw, left=of c] (left) {left}; \node[draw, below=of c] (below) {below}; \node[draw, right=of c] (right) {right}; % \spath[...] {abovebelow}{ \spath {abovebelow} ([xshift=-\offset]above.south) -- ([xshift=-\offset]below.north) node[above, rotate=90] {above--below}; \spath {belowabove} ([xshift=\offset]below.north) -- ([xshift=\offset]above.south) node[below, rotate=90] {below--above}; \spath {leftright} ([yshift=-\offset] left.east) -- ([yshift=-\offset] right.west) node[below] {left--right}; \spath {rightleft} ([yshift=\offset] right.west) -- ([yshift=\offset] left.east) node[above] {right--left}; \tikzset{ % arc size/.initial=8pt, % gap size/.initial=4pt, flowchart intersections={ {abovebelow}{rightleft}{*}, {leftright}{abovebelow}{*}, {belowabove}{leftright}{*}, {rightleft}{belowabove}{} } } % \sdraw {abovebelow}; \sdraw[arrow] {abovebelow}; \sdraw[arrow] {belowabove}; \sdraw[arrow] {leftright}; \sdraw[arrow] {rightleft}; \end{tikzpicture} \end{document} ```
Andrew15-5 commented 1 year ago

P.S. I want to express how impressed I am by your knowledge of tikz syntax. Because I had no idea about things like \pgfkeysvalueof, /.list, /.code={...#1...}, \ifx#1\relax\relax (last one I knew but don't know how to use properly). And together all this creates a very flexible and easy to maintain code which can do pretty complex stuff. But since you are a developer of tikz library, I guess you kinda have to know this stuff. In any ways, good for you.

loopspace commented 1 year ago

I forgot about the syntax highlighting - fixed now.

Andrew15-5 commented 1 year ago

It therefore needs to absorb that third argument and discard it.

I don't know, it works just fine with 2 arguments, probably auto discarding the 3rd one automatically.


Which names are you referring to with the protection via @?

The once that are in \tikzset command (e.g., arc is too simple and needs to be protected if it would be added to library)


Your \spath and \sdraw commands

The names for the commands were chosen "randomly" and are here just to demonstrate my example of wrappers.


By "renaming", do you mean arc for bridge?

Mostly yes (I added a lot or very small changes, including renaming). If we use geometric terms, then arc is more accurately represents the shape. Bridge is an abstraction or an example of an arc. Bridge can be a straight line and therefore brings confusion to the code. (Bridge in my head always comes in a form of any real bridge with some kind of details: color, shape, fences — this further confuses my brain.)


You've loaded the spath3 package. You should be loading the spath3 tikz library.

You see! You have 2 different (for you) things with the same name. For me, who only lightly used your packages/libraries, they are the same thing (and they do the same thing in my code).

But I have one more thing: in neovim+texlab it shows me that I can use spath3 package, but it doesn't show spath3 and knots tikz library (it shows a lot of others). I only found out through some issues on GitHub that I need to use library via \usetikzlibrary command (either show me where in doc it is stated, or please add this because I didn't find it).

And after all this, I thought that spath3 is a package and knots is a tikz library. I had no idea that there is a package and tikz library with the same name. I think you should consider this "confusion" and maybe rename one or the other, or simply drop one of them (as I said they do the same thing for me and therefore one is a redundancy). Of course, they perhaps have different things, but I don't know this.

If I change knots library to spath3 then I have to also add intersections.

```latex \usepackage{tikz} \usetikzlibrary{ arrows.meta, intersections, positioning, spath3, } ```

If knots library automatically imports spath3 and intersections, then why spath3 can't automatically import intersections? This doesn't seem logical. They both should import intersections the same way IMO. Wait, it is because not always intersections is used with spath3. So in my example, knots is just a shortcut for importing 2 other libraries that I use.

loopspace commented 1 year ago

I can make the distinction between the spath3 package and spath3 library clearer in the documentation, but the naming overlap is deliberate since the package defines a slew of commands that aren't available to a user and the library makes them available. There is no redundancy between them.

I have no idea what "neovim+texlab" is so can't comment on what it makes available or not. If there's something straightforward I can do with how my package is configured then I'll consider it, but I'm not going to delve into systems that I have no experience of to find that out.

Andrew15-5 commented 1 year ago

FYI, Neovim is a terminal text editor which can be turned into a very decent IDE and more. texlab is language server (implementation of LSP for tex). I can use texlab in neovim to give me some guidance when making latex documents (It doesn't really do much except autosuggestion/autocompletion, unlike LS for other languages). It is also can be used in VS Code. So I was referring to them just to describe the situation (not all tikz libraries are autosuggested), which made me think that there are no knots and spath3 libraries.

I want to say this again that I know very little about raw tex syntax and package/library development. I only use lualatex command (and latexmk helper) for my documents, as I use latex syntax and sometime Lua code. With that said, I can't objectively judge about things like renaming package/library as you said it yourself:

the naming overlap is deliberate since the package defines a slew of commands that aren't available to a user and the library makes them available. There is no redundancy between them.

This information is completely new to me. I can only say opinion from a user perspective. For me, it is confusing that 2 different things use the same name, and you can either import one or the other (roughly speaking) because I had never seen such naming overlapping. (Although I clearly still in the process of searching for new useful packages.)

My "mission" is to bring attention to some issues a user can stumble upon and yours is... well, it includes many things, but the point is that you know your stuff better. If you can address this misunderstanding in the documentation, it would be lovely.

loopspace commented 1 year ago

The naming makes sense to me since the package doesn't expose any user commands and the TikZ library does, so someone just doing \usepackage{spath3} will very quickly realise that something's wrong. However, I can see that I should make this clearer in the documentation. Thank you for your perspective.