%% liftarm.sty
%% Copyright 2022 Matthias Floré
%
% This work may be distributed and/or modified under the
% conditions of the LaTeX Project Public License, either version 1.3
% of this license or (at your option) any later version.
% The latest version of this license is in
%   http://www.latex-project.org/lppl.txt
% and version 1.3 or later is part of all distributions of LaTeX
% version 2005/12/01 or later.
%
% This work has the LPPL maintenance status `maintained'.
% 
% The Current Maintainer of this work is Matthias Floré.
%
% This work consists of the files liftarm.pdf, liftarm.sty,
% liftarm.tex and README.md.
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{liftarm}[2022/04/07 v2.0 Draw liftarms with TikZ]
\RequirePackage{etoolbox}
\RequirePackage[dvipsnames]{xcolor}
\RequirePackage{tikz}
\usetikzlibrary{calc}
\newif\ifliftarm@animate
\newif\ifliftarm@brick
\newif\ifliftarm@connectreverse
\newif\ifliftarm@contour
\pgfkeys{
/liftarm/.is family,
/liftarm/.unknown/.code={\edef\liftarm@colorkey{\pgfkeyscurrentname}},
/liftarm,
axle holes/.store in=\liftarm@axleholes,
brick/.is if=liftarm@brick,
color/.store in=\liftarm@colorkey,
color 0/.initial=Gray,
color 1/.initial=darkgray,
color 2/.initial=Yellow,
color 3/.initial=Orange,
color 4/.initial=Red,
color 5/.initial=Green,
color 6/.initial=Blue,
color 7/.initial=Brown,
color modulo/.initial=8,
connect/.store in=\liftarm@connect,
connect coordinate/.store in=\liftarm@connectcoordinate,
connect reverse/.is if=liftarm@connectreverse,
contour/.is if=liftarm@contour,
coordinate/.store in=\liftarm@coordinate,
globalize/.code n args={2}{\xdef#1{#2}},
hole radius/.initial=0.3,
liftarm 1/.style={liftarm 1 options/.style={#1}},
liftarm 1 options/.style={},
liftarm 2/.style={liftarm 2 options/.style={#1}},
liftarm 2 options/.style={},
liftarm thickness/.initial=0.92,
mark color/.initial=Black,
mark holes/.store in=\liftarm@markholes,
origin/.initial=0,
scalefactor/.initial=0.5,
screw color/.initial=Black,
screw holes/.store in=\liftarm@screwholes,
screw holes angle/.initial=45,
trace/.store in=\liftarm@trace,
}
\pgfmathsetmacro{\liftarm@axleholeangle}{14}%2*\liftarm@axleholeradius*sin(\liftarm@axleholeangle)=1.78/8
\pgfmathsetmacro{\liftarm@screwholestartangle}{10}
\newcommand{\liftarm}[4][]{
\pgfmathsetmacro{\liftarm@length}{#3}
\ifdim \liftarm@length pt<0 pt
\PackageWarning{liftarm}{The length (\liftarm@length) of the liftarm is smaller than 0.}
\else
\pgfmathsetmacro{\liftarm@ang}{#4}
\begin{scope}[shift={(#2)},rotate=\liftarm@ang]
\pgfkeys{/liftarm,#1}
\pgfmathsetmacro{\liftarm@origin}{\pgfkeysvalueof{/liftarm/origin}}
\begin{scope}[shift={(-\liftarm@origin,0)}]
\pgfmathsetmacro{\liftarm@halfthickness}{0.5*\pgfkeysvalueof{/liftarm/scalefactor}*\pgfkeysvalueof{/liftarm/liftarm thickness}}
\pgfmathsetmacro{\liftarm@holeradius}{\pgfkeysvalueof{/liftarm/scalefactor}*\pgfkeysvalueof{/liftarm/hole radius}}
\pgfmathsetmacro{\liftarm@halfstudwidth}{\pgfkeysvalueof{/liftarm/scalefactor}*0.3}
\pgfmathsetmacro{\liftarm@studheight}{\pgfkeysvalueof{/liftarm/scalefactor}*0.2}
\pgfmathsetmacro{\liftarm@halfplateheight}{\pgfkeysvalueof{/liftarm/scalefactor}*0.2}
\pgfmathsetmacro{\liftarm@halfplatewidth}{\pgfkeysvalueof{/liftarm/scalefactor}*0.5}
\pgfmathsetmacro{\liftarm@axleholeradius}{\liftarm@halfthickness}
\pgfmathsetmacro{\liftarm@screwholeradius}{0.8*\liftarm@holeradius}
\colorlet{liftarm@markcolor}{\pgfkeysvalueof{/liftarm/mark color}}
\colorlet{liftarm@screwcolor}{\pgfkeysvalueof{/liftarm/screw color}}
\pgfmathsetmacro{\liftarm@screwholesangle}{\pgfkeysvalueof{/liftarm/screw holes angle}}
\ifcsname liftarm@colorkey\endcsname
\colorlet{liftarm@color}{\liftarm@colorkey}
\else
\pgfmathsetmacro{\liftarm@colornumber}{int(mod(\liftarm@length,\pgfkeysvalueof{/liftarm/color modulo}))}
\colorlet{liftarm@color}{\pgfkeysvalueof{/liftarm/color \liftarm@colornumber}}
\fi
\ifliftarm@brick
\def\liftarm@shape{(-1,{-\liftarm@halfplatewidth-\liftarm@halfplateheight})--(-1,\liftarm@halfplatewidth)
\foreach\liftarm@n in {-1,...,\liftarm@length}{
--({\liftarm@n+0.5-\liftarm@halfstudwidth},\liftarm@halfplatewidth)--({\liftarm@n+0.5-\liftarm@halfstudwidth},{\liftarm@halfplatewidth+\liftarm@studheight})--({\liftarm@n+0.5+\liftarm@halfstudwidth},{\liftarm@halfplatewidth+\liftarm@studheight})--({\liftarm@n+0.5+\liftarm@halfstudwidth},\liftarm@halfplatewidth)--({\liftarm@n+1},\liftarm@halfplatewidth)
}
--({\liftarm@length+1},{-\liftarm@halfplatewidth-\liftarm@halfplateheight})--cycle}
\else
\def\liftarm@shape{(0,\liftarm@halfthickness) arc (90:270:\liftarm@halfthickness)--(\liftarm@length,-\liftarm@halfthickness) arc (-90:90:\liftarm@halfthickness)--cycle}
\fi
\fill[liftarm@color,even odd rule] \liftarm@shape
\foreach\liftarm@n in {0,...,\liftarm@length}{
(\liftarm@n,0) circle[radius=\liftarm@holeradius]
};
\ifliftarm@contour
%\ifliftarm@brick
%\else
\draw[liftarm@color!75!Black,ultra thick] \liftarm@shape;
%\fi
\fi
\ifcsname liftarm@axleholes\endcsname
\foreach\liftarm@n in \liftarm@axleholes{
\pgfmathsetmacro{\liftarm@axlehole}{\liftarm@n}
\ifdim \liftarm@axlehole pt<0 pt
\else
\ifdim \liftarm@axlehole pt>\liftarm@length pt
\else
\foreach\liftarm@angle in {0,90,180,270}{
\begin{scope}[shift={(\liftarm@axlehole,0)},rotate=\liftarm@angle]
\fill[liftarm@color] (\liftarm@axleholeangle:\liftarm@axleholeradius) arc (\liftarm@axleholeangle:{90-\liftarm@axleholeangle}:\liftarm@axleholeradius)--({\liftarm@axleholeradius*sin(\liftarm@axleholeangle)},{\liftarm@axleholeradius*sin(\liftarm@axleholeangle)})--cycle;
\end{scope}
}
\fi
\fi
}
\fi
\ifcsname liftarm@markholes\endcsname
\foreach\liftarm@n in \liftarm@markholes{
\pgfmathsetmacro{\liftarm@markhole}{\liftarm@n}
\ifdim \liftarm@markhole pt<0 pt
\else
\ifdim \liftarm@markhole pt>\liftarm@length pt
\else
\fill[liftarm@markcolor] (\liftarm@markhole,0) circle[radius=\liftarm@holeradius];
\fi
\fi
}
\fi
\ifcsname liftarm@screwholes\endcsname
\foreach\liftarm@n in \liftarm@screwholes{
\pgfmathsetmacro{\liftarm@screwhole}{\liftarm@n}
\ifdim \liftarm@screwhole pt<0 pt
\else
\ifdim \liftarm@screwhole pt>\liftarm@length pt
\else
\begin{scope}[shift={(\liftarm@screwhole,0)},rotate=\liftarm@screwholesangle]
\foreach\liftarm@n in {-1,1}{
\fill[liftarm@screwcolor] ({\liftarm@screwholeradius*cos(\liftarm@screwholestartangle)},{\liftarm@n*\liftarm@screwholeradius*sin(\liftarm@screwholestartangle)}) arc ({\liftarm@n*\liftarm@screwholestartangle}:{\liftarm@n*(180-\liftarm@screwholestartangle)}:\liftarm@screwholeradius);
}
\end{scope}
\fi
\fi
}
\fi
\ifcsname liftarm@coordinate\endcsname
\foreach\liftarm@n/\liftarm@name in \liftarm@coordinate{
\pgfmathsetmacro{\liftarm@value}{\liftarm@n}
\coordinate (\liftarm@name) at (\liftarm@value,0);
}
\fi
\ifcsname liftarm@trace\endcsname
\ifliftarm@animate
\foreach\liftarm@n/\liftarm@numberofframes/\liftarm@tracefigure in \liftarm@trace{
\pgfmathsetmacro{\liftarm@value}{\liftarm@n}
\gappto\liftarm@animateframestrace{\newframe\begin{scope}}
\addtocounter{liftarm@animateframenumber}{1}
\xappto\liftarm@animateframestrace{[shift={(#2)},rotate=\liftarm@ang]}
\gappto\liftarm@animateframestrace{\begin{scope}}
\xappto\liftarm@animateframestrace{[shift={(\liftarm@value-\liftarm@origin,0)}]}
\ifdefempty{\liftarm@tracefigure}{
\gappto\liftarm@animateframestrace{\fill[Black] (0,0) circle}
\xappto\liftarm@animateframestrace{[radius=0.66*\liftarm@holeradius];}
}
{
\xappto\liftarm@animateframestrace{\expandonce\liftarm@tracefigure}
}
\gappto\liftarm@animateframestrace{\end{scope}\end{scope}}
\ifdefempty{\liftarm@numberofframes}{
\csxappto{liftarm@animatetimeline0}{\theliftarm@animateframenumber x0,}
}
{
\csxappto{liftarm@animatetimeline\theliftarm@animatestepnumber}{\theliftarm@animateframenumber x\liftarm@numberofframes,}
}
}
\fi
\fi
\end{scope}
\end{scope}
\fi
}
\newcommand{\liftarmconnect}[5][]{
\begin{scope}
\pgfkeys{/liftarm,#1}
\coordinate (liftarm@A) at (#2);
\coordinate (liftarm@B) at (#4);
\begin{scope}
\pgfkeys{/liftarm,liftarm 1 options}
\ifcsname liftarm@connect\endcsname
\pgfmathsetmacro{\liftarm@connectlengthAtemp}{\liftarm@connect-\pgfkeysvalueof{/liftarm/origin}}
\else
\pgfmathsetmacro{\liftarm@connectlengthAtemp}{#3-\pgfkeysvalueof{/liftarm/origin}}
\fi
\xdef\liftarm@connectlengthA{\liftarm@connectlengthAtemp}
\ifdim \liftarm@connectlengthA pt=0 pt
\PackageWarning{liftarm}{The length of the first liftarm is 0.}
\fi
\end{scope}
\begin{scope}
\pgfkeys{/liftarm,liftarm 2 options}
\ifcsname liftarm@connect\endcsname
\pgfmathsetmacro{\liftarm@connectlengthBtemp}{\liftarm@connect-\pgfkeysvalueof{/liftarm/origin}}
\else
\pgfmathsetmacro{\liftarm@connectlengthBtemp}{#5-\pgfkeysvalueof{/liftarm/origin}}
\fi
\xdef\liftarm@connectlengthB{\liftarm@connectlengthBtemp}
\ifdim \liftarm@connectlengthB pt=0 pt
\PackageWarning{liftarm}{The length of the second liftarm is 0.}
\fi
\end{scope}
\path
let
\p1=(liftarm@A),
\p2=(liftarm@B),
\n1={\x1/\pgf@xx},
\n2={\y1/\pgf@yy},
\n3={\x2/\pgf@xx},
\n4={\y2/\pgf@yy}
in
[
/liftarm/globalize={\liftarm@connectxalet}{\n3},
/liftarm/globalize={\liftarm@connectyalet}{\n4},
/liftarm/globalize={\liftarm@connectxblet}{\n1},
/liftarm/globalize={\liftarm@connectyblet}{\n2}
]
;
\pgfmathsetmacro{\liftarm@connectxa}{\liftarm@connectxalet}
\pgfmathsetmacro{\liftarm@connectya}{\liftarm@connectyalet}
\pgfmathsetmacro{\liftarm@connectxb}{\liftarm@connectxblet}
\pgfmathsetmacro{\liftarm@connectyb}{\liftarm@connectyblet}
\pgfmathsetmacro{\liftarm@absdiff}{abs(\liftarm@connectxa-\liftarm@connectxb)}
\ifdim \liftarm@absdiff pt<0.001 pt
\pgfmathsetmacro{\liftarm@connectanglegamma}{90}
\ifdim \liftarm@connectya pt<\liftarm@connectyb pt
\pgfmathsetmacro{\liftarm@connectangleshift}{0}
\else
\pgfmathsetmacro{\liftarm@connectangleshift}{180}
\fi
\else
\pgfmathsetmacro{\liftarm@connectanglegamma}{atan((\liftarm@connectyb-\liftarm@connectya)/(\liftarm@connectxb-\liftarm@connectxa))}
\ifdim \liftarm@connectxa pt<\liftarm@connectxb pt
\pgfmathsetmacro{\liftarm@connectangleshift}{0}
\else
\pgfmathsetmacro{\liftarm@connectangleshift}{180}
\fi
\fi
\pgfmathsetmacro{\liftarm@connectlength}{sqrt((\liftarm@connectyb-\liftarm@connectya)^2+(\liftarm@connectxb-\liftarm@connectxa)^2)}
\ifdim \liftarm@connectlength pt=0 pt
\PackageWarning{liftarm}{The length between the origins of the liftarms is 0.}
\fi
\pgfmathsetmacro{\liftarm@connectabsconnectlengthAminusconnectlengthB}{abs(\liftarm@connectlengthA-\liftarm@connectlengthB)}
\ifdim \liftarm@connectabsconnectlengthAminusconnectlengthB pt>\liftarm@connectlength pt
\PackageWarning{liftarm}{The liftarms can not be connected.}
\fi
\pgfmathsetmacro{\liftarm@connectconnectlengthAplusconnectlengthB}{\liftarm@connectlengthA+\liftarm@connectlengthB}
\ifdim \liftarm@connectconnectlengthAplusconnectlengthB pt<\liftarm@connectlength pt
\PackageWarning{liftarm}{The liftarms can not be connected.}
\fi
\pgfmathsetmacro{\liftarm@connectanglealpha}{acos(((\liftarm@connectlengthB)^2+(\liftarm@connectlength)^2-(\liftarm@connectlengthA)^2)/(2*(\liftarm@connectlength)*(\liftarm@connectlengthB)))}
\pgfmathsetmacro{\liftarm@connectanglebeta}{acos(((\liftarm@connectlength)^2+(\liftarm@connectlengthA)^2-(\liftarm@connectlengthB)^2)/(2*\liftarm@connectlength*(\liftarm@connectlengthA)))}
\pgfmathsetmacro{\liftarm@connectangledelta}{\liftarm@connectangleshift+180+\liftarm@connectanglegamma-\liftarm@connectanglebeta}
\def\liftarm@liftarmA{\liftarm[liftarm 1 options]{#2}{#3}{\liftarm@connectangledelta}}
\def\liftarm@liftarmB{\liftarm[liftarm 2 options]{#4}{#5}{\liftarm@connectangleshift+\liftarm@connectanglegamma+\liftarm@connectanglealpha}}
\ifliftarm@connectreverse
\liftarm@liftarmB
\liftarm@liftarmA
\else
\liftarm@liftarmA
\liftarm@liftarmB
\fi
\ifcsname liftarm@connectcoordinate\endcsname
\coordinate (\liftarm@connectcoordinate) at ($(#2)+(\liftarm@connectangledelta:\liftarm@connectlengthA)$);
\fi
\end{scope}
}
\newcommand{\liftarm@construction}[2][]{\begin{tikzpicture}[#1]
\liftarm@constructfigure
#2
\end{tikzpicture}}
\newcommand{\liftarmconstruct}[3][]{\item #2\nopagebreak

\gappto\liftarm@constructfigure{#3}
\expandafter\liftarm@construction\expandafter[\liftarm@constructoptions]{#1}
}
\newenvironment{liftarmconstruction}[1][]{\def\liftarm@constructoptions{#1}\def\liftarm@constructfigure{}\begin{enumerate}}{\end{enumerate}}
\newcounter{liftarm@animatenumberofsteps}
\newcounter{liftarm@animateframenumber}
\newcounter{liftarm@animatestepnumber}
\newwrite\liftarm@animatewritetimeline
\newcounter{liftarm@animatenumberofanimation}
\newcommand{\liftarmanimate}[4][]{%
\liftarm@animatetrue%
\addtocounter{liftarm@animatenumberofanimation}{1}%
\setcounter{liftarm@animatenumberofsteps}{-1}%
\gdef\liftarm@animateframes{}%
\gdef\liftarm@animateframestrace{}%
\setcounter{liftarm@animatestepnumber}{-1}%
\foreach\liftarm@n in {#3}{%
\addtocounter{liftarm@animatenumberofsteps}{1}%
\gappto\liftarm@animateframes{\newframe\addtocounter{liftarm@animatestepnumber}{1}#4}%
\xappto\liftarm@animateframes{{\liftarm@n}}%
}%
\patchcmd{\liftarm@animateframes}{\newframe}{}{}{}%
\csgdef{liftarm@animatetimeline0}{c,}%
\foreach\liftarm@n in {1,...,\theliftarm@animatenumberofsteps}{%
\csgdef{liftarm@animatetimeline\liftarm@n}{}%
}%
\setcounter{liftarm@animateframenumber}{\theliftarm@animatenumberofsteps}%
\IfFileExists{\jobname\theliftarm@animatenumberofanimation.tln}{}{%
\immediate\openout\liftarm@animatewritetimeline=\jobname\theliftarm@animatenumberofanimation.tln%
\immediate\write\liftarm@animatewritetimeline{::c,0}%
\immediate\closeout\liftarm@animatewritetimeline%
}%
\begin{animateinline}[#1,timeline=\jobname\theliftarm@animatenumberofanimation.tln]{#2}%
\liftarm@animateframes%
\liftarm@animateframestrace%
\end{animateinline}%
\immediate\openout\liftarm@animatewritetimeline=\jobname\theliftarm@animatenumberofanimation.tln%
\foreach\liftarm@n in {0,...,\theliftarm@animatenumberofsteps}{%
\immediate\write\liftarm@animatewritetimeline{::\csname liftarm@animatetimeline\liftarm@n\endcsname\liftarm@n}%
}%
\immediate\closeout\liftarm@animatewritetimeline%
\liftarm@animatefalse%
}
\endinput