6

This MWE draws multiple bicycle chain-link profiles but does not yet have an automated procedure to attach the links end-to-end into a chain segment. Each link is composed of two rollers, which I'll call the first and second. The origin of a link's local x-y coordinate frame is centered on the first roller and its x-axis points toward the second roller. The origin of the next link's reference frame (at the center of its first roller) will be attached to the center of the current link's second roller. The current version of the \drawlink macro takes in the absolute x and y coordinates of the link's first roller (origin of its reference frame) as the first two arguments and the absolute rotation of this frame as the third argument. With these three quantities, a link can be drawn in any position and orientation relative to the global frame.

To simplify assembling a chain segment from links, I want to modify the \drawlink macro to take in the absolute position of the previous link's second roller and its absolute angle, take in the angle of the current link relative to the previous link, and return the absolute position of the current link's second roller and its absolute angle.

Thus I am looking for a procedure to return the absolute position of the current link's second roller and its absolute angle from the \drawlink macro and pass these quantities back into the next call to the \drawlink macro, along with the offset angle. Then the \drawchain macro will have only to provide relative angles between links and the \drawlink macro will handle the rest.

%&latex
\documentclass{article}
\usepackage{tikz}
\usepackage{pgfmath}
\usetikzlibrary{calc}
\usetikzlibrary{math}
\usepackage{xstring}

\def\drawlink(#1,#2,#3,#4){%
%#1 = x-position of first roller center.
%#2 = y-position of first roller center.
%#3 = link rotation angle.
%#4 = link gray saturation: 0 to 100
\begin{scope}
\tikzmath{%Some math to define and position the link components
    \lp = 0.5in;
    \re = 0.3299*\lp;
    \rc = 0.4284*\lp;
    \Th = 41.25;
    \Thc = 90 - \Th;
    \Tp = \Thc + #3;
    \Tn = \Thc - #3;
    \xs = #1 + cos(\Tp)*\re;
    \ys = #2 + sin(\Tp)*\re;
}
\fill[gray!#4] (\xs,\ys)
arc(\Tp:360-\Tn):\re)
arc(180-\Tn:\Tp:\rc) 
arc(-180+\Tp:180-\Tn:\re)
arc(-\Tn:-180+\Tp:\rc)
-- cycle;
\end{scope}
}

\begin{document}

\begin{tikzpicture}[x=1mm, y=1mm, scale=0.25]
\pgfdeclarelayer{top}
\pgfsetlayers{main,top}

\pgfonlayer{main}
\foreach \i in {1,...,5}{
\drawlink(\i in,0,0,50)
}
\endpgfonlayer

\pgfonlayer{top}
\foreach \i in {0.5,1.5,...,5.5}{
\drawlink(\i in,0,0,100)
}
\endpgfonlayer

\end{tikzpicture}
\end{document}

enter image description here

1

1 Answer 1

8

I would not do it like this. Rather, I'd use a pic which I put multiple times along a path using decorations. Note that I did not tune your code, rather focus on the methods "putting sth in a pic" and "repeating it in a decoration". However, I really like your clever idea of using layers here!

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{calc}
\usetikzlibrary{math}
\usetikzlibrary{decorations.markings}
\tikzset{pics/.cd,
link/.style={code={\begin{scope}
\tikzmath{%Some math to define and position the link components
    \lp = 0.5in;
    \re = 0.3299*\lp;
    \rc = 0.4284*\lp;
    \Th = 41.25;
    \Thc = 90 - \Th;
    \Tp = \Thc;
    \Tn = \Thc;
}
\fill[gray!#1] (0,0)%(\xs,\ys)
arc(\Tp:360-\Tn):\re)
arc(180-\Tn:\Tp:\rc) 
arc(-180+\Tp:180-\Tn:\re)
arc(-\Tn:-180+\Tp:\rc)
-- cycle;
\end{scope}
}}}
\begin{document}

\begin{tikzpicture}
\pgfdeclarelayer{top}
\pgfsetlayers{main,top}
\pgfonlayer{main}
\path[decorate,decoration={markings,
mark=between positions 0 and 1 step 2.9in
      with {\pic[scale=0.1]{link=30};}
}] (0,0) -- (9in,0);
\endpgfonlayer

\pgfonlayer{top}
\path[decorate,decoration={markings,
mark=between positions 0 and 1 step 2.9in
      with {\pic[scale=0.1]{link=80};}
}] (1.45in,0) -- (9in,0);
\endpgfonlayer

\end{tikzpicture}
\end{document}

enter image description here

ADDENDUM: I was just curious if it is possible to make TikZ draw a chain along a path. And I think that the answer is yes. The following post is not at all polished. In particular, I replaced your nice link by a simpler, less fancy version. The only reason is that this thing is centered around (0,0). If you come up with a shape which has a somewhat more intuitive parametrization, this can be used as well. I also did not yet pay too much attention to the requirement that the path closes. I copied this example to have a path that is somewhat reminiscent of a bicycle chain, yet the details of the path are not crucial. The only thing which might be important is that this routine will find points on the curve which are separated by a given distance (the dimension of the link minus twice the radius of its circle). This is done by computing appropriate intersections (and selecting "cleverly" the relevant one).

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{calc}
\usetikzlibrary{decorations.markings,intersections}
\tikzset{pics/.cd,
mylink/.style={code={
\fill[gray!#1] (-0.6,0.6) to[out=-50,in=-110,looseness=0.7] (0.6,0.6)
 arc(140:-140:0.9)
 -- (0.6,-0.6) to[out=110,in=50,looseness=0.7] (-0.6,-0.6) arc(-40:-320:0.9);
}}}
\begin{document}

\begin{tikzpicture}
\pgfdeclarelayer{top}
\pgfsetlayers{main,top}
% tangents are from https://tex.stackexchange.com/a/7209/121799
\pgfmathsetmacro{\rone}{10}
\pgfmathsetmacro{\rtwo}{8}
\pgfmathsetmacro{\mid}{\rone/(\rone + \rtwo)}
\pgfmathsetmacro{\out}{\rone/(\rone - \rtwo)}
\node[circle,minimum size=2 * \rone cm,inner sep=0pt] (c1) at (21.1,0) {};
\node[circle,minimum size=2 * \rtwo cm,inner sep=0pt] (c2) at (0,0) {};
\path (c1.center) -- node[coordinate,pos=\mid] (mid) {} (c2.center);
\path (c1.center) -- node[coordinate,pos=\out] (out) {}  (c2.center);
% \draw[red] (tangent cs:node=c2,point={(out)}) -- (tangent cs:node=c1,point={(out)});
% \draw[red] (tangent cs:node=c2,point={(out)},solution=2) -- (tangent cs:node=c1,point={(out)},solution=2);
% end https://tex.stackexchange.com/a/7209/121799

\path[name path=chain,postaction={decorate,decoration={markings,
mark=at position 0 with {\xdef\clen{\pgfdecoratedpathlength}}}},
draw=blue,thick] let 
\p1=($(tangent cs:node=c2,point={(out)})-(c2.center)$),
\n1={atan2(\y1,\x1)},
\p2=($(tangent cs:node=c1,point={(out)})-(c1.center)$),
\n2={atan2(\y2,\x2)},
\p3=($(tangent cs:node=c1,point={(out)},solution=2)-(c1.center)$),
\n3={atan2(\y3,\x3)},
\p4=($(tangent cs:node=c2,point={(out)},solution=2)-(c2.center)$),
\n4={atan2(\y4,\x4)} in
(tangent cs:node=c2,point={(out)}) -- 
(tangent cs:node=c1,point={(out)}) arc(\n2:\n3:\rone)
-- (tangent cs:node=c2,point={(out)},solution=2) arc(\n4:\n1+360:\rtwo);
\coordinate (p0pt) at (tangent cs:node=c2,point={(out)});
\path[name path=c0] (p0pt) circle (2.9in);
\fill[name intersections={of=chain and c0,total=\t}] foreach \i in {1,...,\t}
{(intersection-\i) circle(3pt) node[below] {\i}};
\path (intersection-1) coordinate (p1pt);
\pgfmathsetmacro{\Nmax}{int(\clen/(2.6cm))}
\typeout{\Nmax}
\foreach \X [evaluate=\X as \Y using {int(\X-1)},evaluate=\X as \Z using {int(\X+1)}] in {1,...,\Nmax} % \Nmax
{
\path[name path=c\X] (p\X pt) circle (2.6cm);
\fill[name intersections={of=chain and c\X,total=\t}] foreach \i in {1,...,\t}
{let
\p1=($(intersection-\i)-(p\Y pt)$),\n1={ifthenelse(int(veclen(\x1,\y1)/1pt)<10,int(-1),int(\Z/1pt))} 
in %\pgfextra{\typeout{\X:\i:\n1}} 
(intersection-\i) coordinate (p\n1) };
\ifodd\X
\pgfonlayer{main}
\path let \p1=($(p\X pt)-(p\Z pt)$),\n1={atan2(\y1,\x1)}
in ($(p\X pt)!0.5!(p\Z pt)$) pic[rotate=\n1]{mylink=30};
\endpgfonlayer
\else
\pgfonlayer{top}
\path let \p1=($(p\X pt)-(p\Z pt)$),\n1={atan2(\y1,\x1)}
in ($(p\X pt)!0.5!(p\Z pt)$) pic[rotate=\n1]{mylink=80};
\endpgfonlayer
\fi
}
\pgfonlayer{top}
\path let \p1=($(p\Nmax pt)-(p1pt)$),\n1={atan2(\y1,\x1)}
in ($(p\Nmax pt)!0.5!(p1pt)$) pic[rotate=\n1]{mylink=80};
\endpgfonlayer
\end{tikzpicture}
\end{document}

enter image description here

And one can animate this as well:

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{calc}
\usetikzlibrary{decorations.markings,intersections}
\tikzset{pics/.cd,
mylink/.style={code={
\fill[gray!#1] (-0.6,0.6) to[out=-50,in=-110,looseness=0.7] (0.6,0.6)
 arc(140:-140:0.9)
 -- (0.6,-0.6) to[out=110,in=50,looseness=0.7] (-0.6,-0.6) arc(-40:-320:0.9);
}}}
\begin{document}

\foreach \nn in {0,...,9}
{\begin{tikzpicture}
\pgfdeclarelayer{top}
\pgfsetlayers{main,top}
% tangents are from https://tex.stackexchange.com/a/7209/121799
\pgfmathsetmacro{\rone}{10}
\pgfmathsetmacro{\rtwo}{8}
\pgfmathsetmacro{\mid}{\rone/(\rone + \rtwo)}
\pgfmathsetmacro{\out}{\rone/(\rone - \rtwo)}
\node[circle,minimum size=2 * \rone cm,inner sep=0pt] (c1) at (21.1,0) {};
\node[circle,minimum size=2 * \rtwo cm,inner sep=0pt] (c2) at (0,0) {};
\path (c1.center) -- node[coordinate,pos=\mid] (mid) {} (c2.center);
\path (c1.center) -- node[coordinate,pos=\out] (out) {}  (c2.center);
% \draw[red] (tangent cs:node=c2,point={(out)}) -- (tangent cs:node=c1,point={(out)});
% \draw[red] (tangent cs:node=c2,point={(out)},solution=2) -- (tangent cs:node=c1,point={(out)},solution=2);
% end https://tex.stackexchange.com/a/7209/121799

\path[name path=chain,postaction={decorate,decoration={markings,
mark=at position 0 with {\xdef\clen{\pgfdecoratedpathlength}
\pgfmathsetmacro{\Nmax}{int(\clen/(2.6cm))}
\xdef\Nmax{\Nmax}},
mark=at position {2*\nn/(10*(\Nmax))} with {\coordinate (start) at (0,0);}
}},
draw=blue,thick] let 
\p1=($(tangent cs:node=c2,point={(out)})-(c2.center)$),
\n1={atan2(\y1,\x1)},
\p2=($(tangent cs:node=c1,point={(out)})-(c1.center)$),
\n2={atan2(\y2,\x2)},
\p3=($(tangent cs:node=c1,point={(out)},solution=2)-(c1.center)$),
\n3={atan2(\y3,\x3)},
\p4=($(tangent cs:node=c2,point={(out)},solution=2)-(c2.center)$),
\n4={atan2(\y4,\x4)} in
(tangent cs:node=c2,point={(out)}) -- 
(tangent cs:node=c1,point={(out)}) arc(\n2:\n3:\rone)
-- (tangent cs:node=c2,point={(out)},solution=2) arc(\n4:\n1+360:\rtwo);
\coordinate (p0pt) at (start) ;% (tangent cs:node=c2,point={(out)});
\path[name path=c0] (p0pt) circle (2.9in);
\fill[name intersections={of=chain and c0,total=\t}] foreach \i in {1,...,\t}
{(intersection-\i) circle(3pt) node[below] {\i}};
\path (intersection-1) coordinate (p1pt);

\typeout{\nn:\Nmax}
\foreach \X [evaluate=\X as \Y using {int(\X-1)},evaluate=\X as \Z using {int(\X+1)}] in {1,...,\Nmax} % \Nmax
{
\path[name path=c\X] (p\X pt) circle (2.6cm);
\fill[name intersections={of=chain and c\X,total=\t}] foreach \i in {1,...,\t}
{let
\p1=($(intersection-\i)-(p\Y pt)$),\n1={ifthenelse(int(veclen(\x1,\y1)/1pt)<10,int(-1),int(\Z/1pt))} 
in %\pgfextra{\typeout{\X:\i:\n1}} 
(intersection-\i) coordinate (p\n1) };
\ifodd\X
\pgfonlayer{main}
\path let \p1=($(p\X pt)-(p\Z pt)$),\n1={atan2(\y1,\x1)}
in ($(p\X pt)!0.5!(p\Z pt)$) pic[rotate=\n1]{mylink=30};
\endpgfonlayer
\else
\pgfonlayer{top}
\path let \p1=($(p\X pt)-(p\Z pt)$),\n1={atan2(\y1,\x1)}
in ($(p\X pt)!0.5!(p\Z pt)$) pic[rotate=\n1]{mylink=80};
\endpgfonlayer
\fi
}
\pgfonlayer{top}
\path let \p1=($(p\Nmax pt)-(p1pt)$),\n1={atan2(\y1,\x1)}
in ($(p\Nmax pt)!0.5!(p1pt)$) pic[rotate=\n1]{mylink=80};
\endpgfonlayer
\end{tikzpicture}}
\end{document}

enter image description here

9
  • 1
    +1 for using pics. Given the mention of angles in the question I think that the point is that the OP wants to draw these links around curves such as, for example, those in tex.stackexchange.com/questions/457784/…. (There seems to be a theme to the OP's posts, and I don't just mean that marmot is the first to answer them:)
    – user30471
    Nov 2, 2018 at 22:28
  • 1
    @Andrew Yes, drawing such a chain along a curve will be slightly more tricky and require something beyond decorations, namely intersections... This can all be done, but I do not see the need to use tikzmath, nor self-written parsers. Of course, I'd love to be proven wrong and learn something new...
    – user121799
    Nov 2, 2018 at 22:56
  • @marmot The chain-wrap C-program with lots of comments is more than 1000 lines, because rotating sprockets and chain segments to align link bushings with tooth pockets requires a lot of geometry and trig operations. And this is just to get initial conditions for a full-fledged dynamic bicycle model. Nov 2, 2018 at 23:45
  • 1
    @RogerWehage Well, I might be too optimistic, but I guess one can make it much shorter.
    – user121799
    Nov 2, 2018 at 23:49
  • @marmot I think decorations can have a significant role in a chain-wrap algorithm. When joining two sprockets with a chain segment, there are two potential seated link-roller anchor points at each end, yielding four combinations of one anchor point from each end. Decorations would quickly allow setting up 4 trial paths and eliminating 3 of the 4. Speed is not an issue; the C-based chain-wrap algorithm takes microseconds. TikZ in LaTeX is only for documentation and will be slow. Nov 3, 2018 at 1:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .