loopspace / spath3

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

Lines intersection in a flowchart (feature request) #23

Open Andrew15-5 opened 1 year ago

Andrew15-5 commented 1 year ago

I found 2 "solutions" for what I need:

But then I (don't remember how) discovered spath3 and knots packages. I can see from the documentation that with them, I can cut one line in place of an intersection to prevent the physical intersection itself. But I was hoping that I can change the way the intersection is redrawn to make one line an arc and leave the other one untouched, or apply the same "cutting" effect (but at the arc intersection).

The given examples are manual work, unlike the knots package, and therefore not desirable.

Andrew15-5 commented 1 year ago

I've missed the spath3 documentation, where it shows the example of doing just that.

Link: http://mirrors.ctan.org/graphics/pgf/contrib/spath3/spath3_code.pdf (page 22-23) from: https://ctan.org/pkg/spath3

Andrew15-5 commented 1 year ago

I understand that I can do it with spath3, as it is a core (lower level API) to knots tikz library. But can the same thing be done in knots library? By that, I mean less code to write, but with the same result. If it can't be done, can I request a new feature? It goes like this:

Add an option to knot environment to choose to which intersection(s) should this style (arc path) be applied. Additional options should be provided (from documentation; this is just an example) to give the user some control of the basic settings:

The last thing is: add the ability to change the direction of the arc, e.g., use one of these paths (over/up arc and under/down/reverse arc):

\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]; }

It will be very useful for everyone who would need to plot any flowchart, specifically if it's just so happen that in the flowchart there is 1 intersection or more. In other words, user can plot as much \strands as they want but in case of intersection they can just write one option to knot environment specifying which strands need to be treated as an arc intersection(s).

This will obviously save a lot of time as opposed to using spath3 directly and will shrink down the size of a preamble.

Andrew15-5 commented 1 year ago

I've modified a bit the example from the documentation (I don't know yet how to reuse the same code with tikz):

...
\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}
...
\begin{tikzpicture}
  ...
  \path[spath/save=ab] ...;
  \path[spath/save=cd] ...;
  \tikzset{
    bridging span/.initial=8pt, % any other value
    bridging gap/.initial=4pt, % any other value
    bridge={ab}{cd},
    % or
    rbridge={ab}{cd},
  }
  \draw[spath/use=ab];
  \draw[spath/use=cd];
  ...
\end{tikzpicture}
...
\end{document}

That's a lot of code for a small flowchart IMO, I hope that it can be integrated in knots library. Or at the very least, it can be integrated in spath3.

loopspace commented 1 year ago

The knots library and the knot style in the spath3 library work in very different ways. The knot environment works by painting over the intersections and then redrawing the over-strand. So while it might be possible to re-jig the over-strand with a bump, doing the splice and then re-doing the intersection is actually more complicated than the knot environment was designed to cope with. The spath3 library works by actually breaking the path at the intersection point, which is a more powerful method and which makes it possible to do the splicing.

So the two are not meant to be used together and trying to make them work together will be tricky.

The spath3 code comes in two parts: there is the underlying code provided by the spath3 package but then there is also a higher level api from the spath3 TikZ library which is intended to be used by a document writer. This is relatively new so with things like the bridge style that you've found then I haven't integrated that into the package itself as yet because I don't know that it is in its final form. Indeed, it looks like you would like an easy way to flip the arc and that seems quite a reasonable thing to want to do. So while code is still very fluid, I would rather not cement it into the package.

Andrew15-5 commented 1 year ago

Sorry for extending your todo list twice (#23, #24). ;)

But I am really looking forward to using the requested things in my projects/documents.

Andrew15-5 commented 1 year ago

I thought I would share my "temporary" setup, which I will be using until this feature is implemented in any package/library. In case, some would like to have/use the same functionality.

Setup ```latex \usepackage{tikz} \usetikzlibrary{spath3, intersections} \tikzset{ flowchart/arc/.initial=flowchart intersection arc, flowchart/arc size/.initial=8pt, flowchart/gap size/.initial=4pt, flowchart/flip arc/.code={ \ifx#1\relax\relax \tikzset{flowchart/arc=flowchart intersection arc} \else \tikzset{flowchart/arc=intersection reverse arc} \fi }, flowchart/insert arcs/.style n args={3}{ flowchart/flip arc={#3}, spath/split at intersections with={#1}{#2}, spath/insert gaps after components={#1}{% \pgfkeysvalueof{/tikz/flowchart/arc size}}, spath/join components upright with={#1}{% \pgfkeysvalueof{/tikz/flowchart/arc}}, }, flowchart/insert gaps/.style n args={3}{ spath/split at intersections with={#2}{#1}, spath/insert gaps after components={#2}{% \pgfkeysvalueof{/tikz/flowchart/gap size}}, }, flowchart intersections/.style={ flowchart/insert arcs/.list={#1}, flowchart/insert gaps/.list={#1}, }, } \AtBeginDocument{ \tikz[overlay]{ \path[spath/save global=flowchart intersection arc] (0,0) arc[ radius=1pt, start angle=0, delta angle=180, ]; } \tikzset{ spath/clone global={intersection reverse arc}{flowchart 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]} ```

And then it can be used like so:

\begin{tikzpicture}
  \spath {a} (0,0) -- (2,2);
  \spath {b} (0,1) -- (1,0);
  \spath {c} (0,2) -- (2,0);

  \tikzset{
    flowchart intersections={
      {a}{b}{*},
      {c}{a}{}
    }
  }

  \sdraw {a};
  \sdraw {b};
  \sdraw {c};
\end{tikzpicture}
Full code (MWE) ```latex \documentclass{article} \usepackage{tikz} \usetikzlibrary{spath3, intersections} \tikzset{ flowchart/arc/.initial=flowchart intersection arc, flowchart/arc size/.initial=8pt, flowchart/gap size/.initial=4pt, flowchart/flip arc/.code={ \ifx#1\relax\relax \tikzset{flowchart/arc=flowchart intersection arc} \else \tikzset{flowchart/arc=intersection reverse arc} \fi }, flowchart/insert arcs/.style n args={3}{ flowchart/flip arc={#3}, spath/split at intersections with={#1}{#2}, spath/insert gaps after components={#1}{% \pgfkeysvalueof{/tikz/flowchart/arc size}}, spath/join components upright with={#1}{% \pgfkeysvalueof{/tikz/flowchart/arc}}, }, flowchart/insert gaps/.style n args={3}{ spath/split at intersections with={#2}{#1}, spath/insert gaps after components={#2}{% \pgfkeysvalueof{/tikz/flowchart/gap size}}, }, flowchart intersections/.style={ flowchart/insert arcs/.list={#1}, flowchart/insert gaps/.list={#1}, }, } \AtBeginDocument{ \tikz[overlay]{ \path[spath/save global=flowchart intersection arc] (0,0) arc[ radius=1pt, start angle=0, delta angle=180, ]; } \tikzset{ spath/clone global={intersection reverse arc}{flowchart 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} \begin{tikzpicture} \spath {a} (0,0) -- (2,2); \spath {b} (0,1) -- (1,0); \spath {c} (0,2) -- (2,0); \tikzset{ flowchart intersections={ {a}{b}{*}, {c}{a}{} } } \sdraw {a}; \sdraw {b}; \sdraw {c}; \end{tikzpicture} \end{document} ```

Output:

image

For more information, see discussion in #24.

Update: Fixed issues, which are noted here: https://github.com/loopspace/spath3/issues/23#issuecomment-1457092339.

loopspace commented 1 year ago

The log for your code contains the line:

Missing character: There is no * (U+002A) in font nullfont!

which is the result of your insert gaps only taking two arguments. It needs to take three so that when you pass it {a}{b}{*} then the * gets absorbed. Otherwise there's a stray * and that's what the error message is about.

Then you also need an empty argument on the line when you don't have a *, otherwise you get:

(\end occurred when \ifx on line 64 was incomplete)
(\end occurred when \if on line 64 was incomplete)

which is the result of trying to send nothing through on the third argument. There's a difference between no argument and an empty argument. There are ways to make that optional, along the lines of the s option for NewDocumentCommand, but the current code isn't sophisticated enough for that.