3

I want to get the output of the following macro stored in a variable:

\foreach \m in {1,...,9}{\csname h\m \endcsname};

I tried to do:

\def\lkj{
  \foreach \m in {1,...,9}{\csname h\m \endcsname};
}

It doesn't work due to it storing the macro itself, not the output that it produces.

Could someone help me find out how to make the output of it get stored inside a variable?


Explanation of what I'm trying to accomplish

1 Minimal working example

\documentclass{standalone}
\usepackage{xargs,tikz}
\usetikzlibrary{decorations.markings,hobby}

\tikzset{ mark pos/.style args={#1(#2)}{ postaction={ decorate, decoration={ markings, mark=at position #1 with \coordinate (#2); } } } }

\newcommandx\arccal[6][6]{% \draw[#6,domain=#5] plot ({#1+#3cos(\x)}, {#2+#4*sin(\x)}); }

\foreach \m in {1,...,9}{ \expandafter\xdef\csname h\m \endcsname{mark pos=0.\m(m\m),} }

\def\lkj{ \foreach \m in {1,...,9}{\csname h\m \endcsname}; }

\begin{document} \begin{tikzpicture}

\arccal{-2.8}{0}{0.6}{1.2}{270:90}[samples=50,ultra thick, mark pos=0.05(w1), mark pos=0.1(w2), mark pos=0.15(w3), mark pos=0.2(w4), mark pos=0.25(w5), mark pos=0.3(w6), mark pos=0.35(w7), mark pos=0.4(w8), mark pos=0.45(w9), mark pos=0.5(w10), mark pos=0.55(w11), mark pos=0.6(w12), mark pos=0.65(w13), mark pos=0.7(w14), mark pos=0.75(w15), mark pos=0.8(w16), mark pos=0.85(w17), mark pos=0.9(w18), mark pos=0.95(w19), ]; \draw[ultra thick] plot[smooth] coordinates {(w19)(w18)(w17)(w16)(w15)(w14)(w13)(w12)(w11)(w10)(w9)(w8)(w7)(w6)(w5)(w4)(w3)(w2)(w1)};

\end{tikzpicture} \end{document}

2 Commentary

I have a tikz style named "mark pos", which is used to place coordinates through lines created with "plot".

There are specific cases where I use "plot[smooth]" or "plot[smooth cycle]" to make shapes that have specific semicircular shapes that need to be managed/closed smoothly in a variety of patterns.

I then have to define a series of coordinates for it to go to a certain way I want, in this case a use a equation that forms circular/eliptical forms and use the "mark pos" to set the coordinates.

In some cases I have to define a considerable amount of coordinates for it to form a homogeneous enough curve, so I wanted to create a loop with "foreach" to make these quickly without having to define each one by hand.

I need to place the output of the foreach macro inside a "\draw[x]", I can't do it with the raw macro itself.


Thanks.


Exemplification I commented about with Qrrbrbirlbel

1 Case one: What I can get now with Qrrbrbirlbel's help

\newcommandx*\arccalpath[6][6]{%
  \path[#6,domain=#5] plot ({#1+#3*cos(\x)}, {#2+#4*sin(\x)});
}

\arccalpath{-3}{0}{0.9}{1.2}{90:-90}[samples=50,ultra thick, mark positions={0.05}{z}]; \arccalpath{-2.8}{0}{0.6}{1.2}{270:90}[samples=50,ultra thick, mark positions={0.05}{w}]

\draw[ultra thick] plot[smooth,samples at={19,...,1}] (z\x); \draw[ultra thick] plot[smooth,samples at={19,...,1}] (w\x);

Produces: enter image description here

2 Case two: What I intend to get, but without having to type all these points (z1, z2, z3...) in the plot coordinates.

\newcommandx*\arccalpath[6][6]{%
  \path[#6,domain=#5] plot ({#1+#3*cos(\x)}, {#2+#4*sin(\x)});
}

\arccalpath{-3}{0}{0.9}{1.2}{90:-90}[samples=50,ultra thick, mark positions={0.05}{z}]; \arccalpath{-2.8}{0}{0.6}{1.2}{270:90}[samples=50,ultra thick, mark positions={0.05}{w}]

\draw[ultra thick] plot[smooth cycle,samples at={19,...,1}] coordinates {(w19)(w18)(w17)(w16)(w15)(w14)(w13)(w12)(w11)(w10)(w9)(w8)(w7)(w6)(w5)(w4)(w3)(w2)(w1)(z19)(z18)(z17)(z16)(z15)(z14)(z13)(z12)(z11)(z10)(z9)(z8)(z7)(z6)(z5)(z4)(z3)(z2)(z1)};

Produces: enter image description here

  • 2
    Welcome to TeX.se. Please don't post fragments of code. Instead put the fragments into a compilable document that people can play with. It would also be helpful if you would explain the larger problem you're trying to solve with your code. – Alan Munn Jan 06 '24 at 22:17
  • Hi, thanks. I updated my post with the details. – name_to_display Jan 06 '24 at 23:13
  • I'm not quite sure what exactly you are trying to achieve but yeah, that's not how \foreach works (other looping mechanism would be better here). Though, you don't really need all that you can just use the mark=between positions … and … step … with … version. Thirdly, are you just trying to (re)draw a part of an arc (circular or elliptical)? There are other solutions available. You don't even need plots for such arcs. – Qrrbrbirlbel Jan 07 '24 at 00:21
  • I believe a combination of the libraries intersections with either calc or spath3 is the right approach because you're combining curves between their intersections. I'll update my answer later. – Qrrbrbirlbel Jan 07 '24 at 13:20

1 Answers1

3

I'd approach this differently.

Multiple markings in a fixed separation can be placed with the mark=between positions … and … step … with … syntax. The manual explains all the details. The value of the /pgf/decoration/mark info/sequence number key provides an incrementing counter.


Furthermore, I'll provide an arccal style that you can on a TikZ path which just uses two argument: the center and the radii in TikZ format <x> and <y>.

The third TikZ picture draws the elliptical arc as an elliptical arc instead of a plot.

The last picture doesn't use the markings library but just draws two arcs, the second with different angles. If you really need the arcs to be shortened by partial amount of the total length of the arc, the approach is a bit more complicated (the total length is only provided by the decoration module of PGF/TikZ and isn't easily extracable) but still better than placing dozens of markings and plotting a line through them.


There are also nicer ways to draw arcs around a center but that's not relevant to your question.

Code

\documentclass{standalone}
\usepackage{xargs,tikz}
\usetikzlibrary{decorations.markings}
\tikzset{
  mark positions/.style 2 args={
    postaction=decorate,
    decoration={
      name=markings, % PGFMath isn't precise, cheat with 1-0.001
      mark=between positions #1 and 1-0.001 step #1 with \coordinate
           (#2\pgfkeysvalueof{/pgf/decoration/mark info/sequence number});}}}
\newcommandx*\arccal[6][6]{%
  \draw[#6,domain=#5] plot ({#1+#3*cos(\x)}, {#2+#4*sin(\x)});
}
\tikzset{arccal/.style n args=2{insert path={plot([shift={(#1)}]\x:#2)}}}
\begin{document}
\begin{tikzpicture}
\arccal{-2.8}{0}{0.6}{1.2}{270:90}[
  samples=50, ultra thick,
  mark positions={0.05}{w}
];
\draw[thick, green] plot[smooth, samples at={19, ..., 1}] (w\x);
\end{tikzpicture}

\begin{tikzpicture} \draw[ ultra thick, samples=50, domain=270:90, mark positions={0.05}{w}, arccal={-2.8, 0}{.6 and 1.2}]; \draw[thick, green] plot[smooth, samples at={19, ..., 1}] (w\x); \end{tikzpicture}

\begin{tikzpicture} \draw[ultra thick, shift={(-2.8, 0)}, mark positions={0.05}{w}] (270:.6 and 1.2) arc[start angle=270, end angle=90, x radius=.6, y radius=1.2]; \draw[thick, green] plot[smooth, samples at={19, ..., 1}] (w\x); \end{tikzpicture}

\begin{tikzpicture} \draw[ultra thick, shift={(-2.8, 0)}] (270:.6 and 1.2) arc[start angle=270, end angle= 90, x radius=.6, y radius=1.2]; \draw[thick, green, shift={(-2.8, 0)}] (255:.6 and 1.2) arc[start angle=255, end angle=105, x radius=.6, y radius=1.2]; \end{tikzpicture} \end{document}

Output

enter image description here


As I see it, you want to combine arcs between the intersections.

Here are three approaches, all use the intersections library to find the intersection between the two paths that make up the arcs/semiellipses.

The first one uses the calc library's let … in syntax to calculate the angle of the intersections to thte ellipse's centers for another set of arcs. The m1 and m2 coordinates are used to be able to get the x radius of the ellipses in the canvas coordinate system without having to do the transformations ourselves.

The second one uses the ext.paths.arcto library of my tikz-ext package which allows to draw an arc to a point, the angles will be calculated by PGF/TikZ.

The third solution uses the spath3 library which makes it possible to split paths at intersections with other paths. We just need to specify which components of splitted paths shall be drawn.


Since the mathematical evaluations of solutions 1 and 2 aren't very precise you'd get annoying artefacts when closing the paths:
enter image description here

This can be fixed by using spath3's adjust and close key or hidden by using line join=round.

Overall, I prefer the arc to approaches because

  • the calc approach uses a lot of manual calculations but also because the acos function isn't unambiguously (two angles for one value) and this will need adjustments and
  • the spath3 solution needs you to specify the components which can get tricky because arcs are constructed out of up to four Bézier curves (which each correspond to a component).

Either way, in the code below, all solutions are presented. I'm also using my answer's arc starts=after moveto to make it easier to draw arcs around a center.

Code

\documentclass[border=5pt]{standalone}
\usepackage{tikz}
%% https://tex.stackexchange.com/a/123189
\usepackage{etoolbox}
\makeatletter
\patchcmd{\tikz@arc@opt}{\xdef}{\tikz@arc@do\xdef}{}{}\let\tikz@arc@do\relax
\tikzset{arc starts/.cd,.is choice, at last point/.code=\let\tikz@arc@do\relax,after moveto/.code=\tikz@arc@do@\pgfpathmoveto,after lineto/.code=\tikz@arc@do@\pgfpathlineto}
\def\tikz@arc@do@#1{\def\tikz@arc@do{\tikz@@@parse@polar{\tikz@arc@do@@#1}(\tikz@s:\pgfkeysvalueof{/tikz/x radius} and \pgfkeysvalueof{/tikz/y radius})}}
\def\tikz@arc@do@@#1#2{#1{\pgfpointadd{#2}{\tikz@last@position@saved}}}
\makeatother

\usetikzlibrary{intersections} % solutions 1, 2, 3 \usetikzlibrary{ calc, % solution 1 ext.paths.arcto, % solution 2 spath3 % solutions (1, 2b,) 3 } \tikzset{cycle/.style=/tikz/spath/adjust and close} \begin{document}

%%% 1. calc (doing our own math) \begin{tikzpicture}[ arc starts=after moveto, e1/.style={x radius=.6, y radius=1.2}, e2/.style={x radius=.9, y radius=1.2}, label position=center, line join=round, ] % the coordinate m1 and m2 are used to find the x radius in the canvas (w/ units) \path[name path=e1] (-2.8, 0) coordinate (c1) arc[start angle=90, delta angle= 180, e1] coordinate[midway] (m1); \path[name path=e2] (-3.0, 0) coordinate(c2) arc[start angle=90, delta angle=-180, e2] coordinate[midway] (m2);

\draw[ ultra thick, arc starts=at last point, name intersections={of=e1 and e2}] % work in the coordinate system of the first ellipse: [shift=(c1), e1] let \p0=(intersection-1), \p1=(m1), \n0={180-acos(\x0/\x1)} in (intersection-1) arc[start angle=\n0, end angle=360-\n0] % work in the coordinate system of the second ellipse: [shift=(c2), e2] let \p0=(intersection-2), \p1=(m2), \n0={-acos(\x0/\x1)} in arc[start angle=\n0, end angle=-\n0] [cycle]; \end{tikzpicture}

%%% 2a. arc to + round line join \begin{tikzpicture}[ arc starts=after moveto, line join=round, e1/.style={x radius=.6, y radius=1.2}, e2/.style={x radius=.9, y radius=1.2}, ] \path[name path=e1] (-2.8, 0) arc[start angle=90, delta angle= 180, e1]; \path[name path=e2] (-3.0, 0) arc[start angle=90, delta angle=-180, e2];

\draw[name intersections={of=e1 and e2}, ultra thick] (intersection-1) arc to[/tikz/e1] (intersection-2) arc to[/tikz/e2] (intersection-1) -- cycle; \end{tikzpicture}

%%% 2b. arc to + spath3 \begin{tikzpicture}[ arc starts=after moveto, e1/.style={x radius=.6, y radius=1.2}, e2/.style={x radius=.9, y radius=1.2}, ] \path[name path=e1] (-2.8, 0) arc[start angle=90, delta angle= 180, e1]; \path[name path=e2] (-3.0, 0) arc[start angle=90, delta angle=-180, e2];

\draw[name intersections={of=e1 and e2}, ultra thick] (intersection-1) arc to[/tikz/e1] (intersection-2) arc to[/tikz/e2] (intersection-1) [cycle]; \end{tikzpicture}

% 3. spath \begin{tikzpicture}[ arc starts=after moveto, e1/.style={x radius=.6, y radius=1.2}, e2/.style={x radius=.9, y radius=1.2}, label position=center, ] \path[name path=e1] (-2.8, 0) coordinate (c1) arc[start angle=90, delta angle= 180, e1]; \path[name path=e2] (-3.0, 0) coordinate(c2) arc[start angle=90, delta angle=-180, e2];

\draw[ultra thick, spath/.cd, split at intersections={e1}{e2}, remove components={e1}{2,4}, remove components={e2}{1,2,4}, use=e1, append reverse=e2, ] -- cycle; \end{tikzpicture} \end{document}

Output

enter image description here

Qrrbrbirlbel
  • 119,821
  • Hi, thanks a lot for the help, the multiple markings mechanism and the counter within it are very helpful. What I would need now is a method to make a continuous plot with for example two curves, I'll edit my post to add what I'm talking about with images. – name_to_display Jan 07 '24 at 03:38
  • I just edited my post and added it in the bottom; again, thanks for helping me. – name_to_display Jan 07 '24 at 04:02
  • Wonderful, you're the best; that's solves everything. For the third time, thanks for helping me. – name_to_display Jan 07 '24 at 21:05