tikz.dev / TikZpingus Manual

The TikZpingus package

Internals

C.11 Anchors and Placement

C.11.1 Important Commands

\pingu@bbox@left

Together with \pingu@bbox@right, \pingu@bbox@bottom, \pingu@bbox@top, \pingu@bbox@midx, and \pingu@bbox@midy this dimension spans the silhouette of the body, the box every anchor refers to.

\pingu@bbox@right

See \pingu@bbox@left.

\pingu@bbox@bottom

See \pingu@bbox@left.

\pingu@bbox@top

See \pingu@bbox@left.

\pingu@bbox@midx

See \pingu@bbox@left.

\pingu@bbox@midy

See \pingu@bbox@left.

\pingu@bbox@update

Recalculates the six dimensions of the silhouette. Only /pingu/height affects them.

\pingu@anchor@add{⟨name⟩}{⟨x⟩}{⟨y⟩}

Registers a new named anchor for /pingu/anchor.

\pingu@place@anchors

Creates the outer coordinates (center, north, …, and anchor) of the current penguin.

C.11.2 The Code

The silhouette and all named anchors:

\newdimen\pingu@shoulder@x
\newdimen\pingu@bbox@left \newdimen\pingu@bbox@right
\newdimen\pingu@bbox@bottom \newdimen\pingu@bbox@top
\newdimen\pingu@bbox@midx \newdimen\pingu@bbox@midy
% only the height is configurable, so re-assigning is cheap
\def\pingu@bbox@update{%
\pingu@bbox@left\z@
\pingu@bbox@right2\pingu@w@half
\pingu@bbox@bottom-\dimexpr\pingu@side@h@half+\pingu@foot@outer@h\relax
\pingu@bbox@top\dimexpr\pingu@outer@off+\pingu@head@h@half\relax
\pingu@bbox@midx\pingu@w@half
\pingu@bbox@midy\dimexpr(\pingu@bbox@top+\pingu@bbox@bottom)/2\relax
\pingu@shoulder@x\dimexpr2\pingu@w@half-.10674pt\relax
}

% An anchor is a point of the penguin that is moved onto the origin.
\let\@pingu@anchors@\@empty
% #1 name | #2 x | #3 y
\def\pingu@anchor@add#1#2#3{%
\pingu@csappto{@pingu@anchors@}{"#1"}%
\expandafter\def\csname @pingu@anchor@#1@\endcsname{{#2}{#3}}%
}
\pingu@anchor@add{origin} {\pingu@bbox@left} {0pt}
\pingu@anchor@add{center} {\pingu@bbox@midx} {\pingu@bbox@midy}
\pingu@anchor@add{north} {\pingu@bbox@midx} {\pingu@bbox@top}
\pingu@anchor@add{south} {\pingu@bbox@midx} {\pingu@bbox@bottom}
\pingu@anchor@add{east} {\pingu@bbox@right}{\pingu@bbox@midy}
\pingu@anchor@add{west} {\pingu@bbox@left} {\pingu@bbox@midy}
\pingu@anchor@add{north east} {\pingu@bbox@right}{\pingu@bbox@top}
\pingu@anchor@add{north west} {\pingu@bbox@left} {\pingu@bbox@top}
\pingu@anchor@add{south east} {\pingu@bbox@right}{\pingu@bbox@bottom}
\pingu@anchor@add{south west} {\pingu@bbox@left} {\pingu@bbox@bottom}
\pingu@anchor@add{head} {\pingu@bbox@midx} {\pingu@outer@off}
\pingu@anchor@add{head top} {\pingu@bbox@midx} {\pingu@bbox@top}
\pingu@anchor@add{bill} {\pingu@bbox@midx} {-.05mm}
\pingu@anchor@add{belly} {\pingu@bbox@midx} {-.68*\pingu@side@h@half}
\pingu@anchor@add{feet} {\pingu@bbox@midx} {\pingu@bbox@bottom}
\pingu@anchor@add{left shoulder} {\pingu@shoulder@x} {\pingu@outer@off}
\pingu@anchor@add{right shoulder}{\pingu@bbox@left} {\pingu@outer@off}

% The wings are the one part whose reach depends on the selected style, so every
% style registers where its ends sit relative to the shoulder it grows from.
% #1 style | #2/#3 left end | #4/#5 left tip | #6/#7 right end | #8/#9 right tip
\def\pingu@wingend@add#1#2#3#4#5#6#7#8#9{%
\@namedef{@pingu@wingend@#1@leftx}{#2}\@namedef{@pingu@wingend@#1@lefty}{#3}%
\@namedef{@pingu@wingend@#1@lefttipx}{#4}\@namedef{@pingu@wingend@#1@lefttipy}{#5}%
\@namedef{@pingu@wingend@#1@rightx}{#6}\@namedef{@pingu@wingend@#1@righty}{#7}%
\@namedef{@pingu@wingend@#1@righttipx}{#8}\@namedef{@pingu@wingend@#1@righttipy}{#9}%
}
% #1 left/right | #2 x, y, tipx, or tipy
\def\pingu@wingend#1#2{\csname @pingu@wingend@\csname @pingu@select@#1wing@\endcsname @#1#2\endcsname}
\pingu@anchor@add{left wing} {\pingu@shoulder@x+\pingu@wingend{left}{x}} {\pingu@outer@off+\pingu@wingend{left}{y}}
\pingu@anchor@add{right wing} {\pingu@bbox@left+\pingu@wingend{right}{x}} {\pingu@outer@off+\pingu@wingend{right}{y}}
\pingu@anchor@add{left wing tip} {\pingu@shoulder@x+\pingu@wingend{left}{tipx}}{\pingu@outer@off+\pingu@wingend{left}{tipy}}
\pingu@anchor@add{right wing tip} {\pingu@bbox@left+\pingu@wingend{right}{tipx}}{\pingu@outer@off+\pingu@wingend{right}{tipy}}

\newdimen\pingu@anchor@dx \newdimen\pingu@anchor@dy
% shift by the negated anchor; a value without a unit is given in |cm|
\def\pingu@anchor@shift#1#2{%
\pgfmathparse{-1*(#2)}%
\ifpgfmathunitsdeclared#1\pgfmathresult pt\relax\else#1\pgfmathresult cm\relax\fi
}
\def\pingu@anchor@set#1#2{\def\pingu@anchor@x{#1}\def\pingu@anchor@y{#2}}
\def\pingu@anchor@select#1{%
\ifcsname @pingu@anchor@#1@\endcsname
\expandafter\expandafter\expandafter\pingu@anchor@set\csname @pingu@anchor@#1@\endcsname
\else\pingu@anchor@free{#1}\fi
}
% any unknown name has to be a point |(<x>,<y>)|, parentheses optional
\def\pingu@anchor@free#1{%
\pgfutil@in@{,}{#1}%
\ifpgfutil@in@
\pgfutil@in@{(}{#1}%
\ifpgfutil@in@\let\pingu@anchor@next\pingu@anchor@free@p
\else\let\pingu@anchor@next\pingu@anchor@free@c\fi
\else\let\pingu@anchor@next\pingu@anchor@err\fi
\pingu@anchor@next#1\@nil
}
\def\pingu@anchor@free@p(#1,#2)#3\@nil{\pingu@anchor@set{#1}{#2}}
\def\pingu@anchor@free@c#1,#2\@nil{\pingu@anchor@set{#1}{#2}}
\def\pingu@anchor@err#1\@nil{%
\PackageError{\tikzpingus@filename}{There is no '#1' anchor. Choose one of '\@pingu@anchors@' or pass a point like '(1cm,2mm)'}{Or ask the package maintainer :D}%
}

Relative placement joins the two anchors facing each other:

% Relative placement joins the two anchors facing each other, e.g.\ |right of|
% our |south west| with their |south east|, keeping both on the same ground.
\def\pingu@relative#1#2{\def\pingu@rel@dir{#1}\pingu@relative@#2::\@nil}
\def\pingu@relative@#1:#2:#3\@nil{%
\def\pingu@rel@name{#1}\def\pingu@rel@dist{#2}%
\ifx\pingu@rel@dist\@empty\def\pingu@rel@dist{0pt}\fi
\csname pingu@relative@\pingu@rel@dir\endcsname
}
% #1 coordinate our own anchor is moved to
\def\pingu@relative@to#1{%
\edef\pingu@rel@tmp{\noexpand\pgfqkeys{/pingu}{@place={shift={(#1)}}}}\pingu@rel@tmp
}
\def\pingu@relative@right{\pingu@anchor@select{south west}%
\pingu@relative@to{[xshift=\pingu@rel@dist]\pingu@rel@name-south east}}
\def\pingu@relative@left{\pingu@anchor@select{south east}%
\pingu@relative@to{[xshift=-\pingu@rel@dist]\pingu@rel@name-south west}}
\def\pingu@relative@above{\pingu@anchor@select{south}%
\pingu@relative@to{[yshift=\pingu@rel@dist]\pingu@rel@name-north}}
\def\pingu@relative@below{\pingu@anchor@select{north}%
\pingu@relative@to{[yshift=-\pingu@rel@dist]\pingu@rel@name-south}}

Both are hooked into these keys:

anchor/.code = \pingu@anchor@select{#1},
@place/.code = {\pgfqkeys{/pingu}{@pingu@place/.style={#1}}},
right of/.code = \pingu@relative{right}{#1},
left of/.code = \pingu@relative{left}{#1},
above of/.code = \pingu@relative{above}{#1},
below of/.code = \pingu@relative{below}{#1},