[Tex/LaTex] Use a custom shape as a “building block”

tikz-pgf

I want to create a custom shape (not a big one) and use it in
custom places. The shape is a camera and is quite simple:

    \draw [fill=black](0,0) -- (2,2.5) -- (-2,2.5) -- cycle;
    \draw [fill=white,ultra thick](0,0) circle (1);

What I want is to be able to place my custom shape in arbitrary places and to be able to rotate it. The origin for placement and rotation should be the (0,0) point.

Is it there an easy way to do this or do I have to redraw it for rotation and positioning?
enter image description here

Best Answer

First things first: There's nothing wrong with JLDiaz answer.

But you wanted a shape.

The \pgfdeclareshape definition is copied from the forbidden sign (that is only a circle with a full-diameter slash) and adjusted to add the actual camera-thing. Both implemented cameras inherit the circle shape, so everything that applies to circle applies to fix- and rotcamera, too.

There is much room for improvement (don't use the camera shapes without a draw, for example), I actually don't prefer that the default direction the camera “looks” in is north as one would assume that the east direction is the standard one (i.e. angle = 0). This can easily fixed for rotcamera by replacing the line

\pgftransformrotate{\pgf@camera@rotate}%

with

\pgftransformrotate{\pgf@camera@rotate-90}%

It is also possible replacing the \pgfmathsetmacro line or by redefining the \pgfmath… lines.

For the fixcamera one must only add the line

\pgftransformrotate{-90}%

before the \pgfmath… part.

I prefer rotcamera with a default east direction (i.e. with -90 added).

The circle part is (much like the original circle shape) transparent, so the background is visible and is not over-drawn (use fill=white if you want this); this can be seen in the last example and picture.

Content

fixcamera shape

The shape fixcamera can only be rotated with the /tikz/rotate key (similar to JLDiaz' \camera). Note, that this also rotates the . reference (like .90 or .north).

rotcamera shape and /tikz/camera rotate

The shape rotcamera is nearly the same as fixcamera only that it rotates the camera-thing (not the circle itself) by the value of the /tikz/camera rotate key (that is initialized with 0).

camera and its optional parameter.

Additionally I introduced a camera key that takes one optional argument (the rotation).

The key also holds the keys draw, the setting minimum size = 2cm and ultra thick to mimic your original design. The parameter #1 will be given to camera rotate, it is set per default to 0 by

 camera/.default=0

after the definition of camera/.style itself.

Code

\documentclass[tikz,border=2pt]{standalone}

\makeatletter

% this declares a camera that rotates
\pgfdeclareshape{rotcamera}
{
  \inheritsavedanchors[from=circle] % this is nearly a circle
  \inheritanchorborder[from=circle]
  \inheritanchor[from=circle]{north}
  \inheritanchor[from=circle]{north west}
  \inheritanchor[from=circle]{north east}
  \inheritanchor[from=circle]{center}
  \inheritanchor[from=circle]{west}
  \inheritanchor[from=circle]{east}
  \inheritanchor[from=circle]{mid}
  \inheritanchor[from=circle]{mid west}
  \inheritanchor[from=circle]{mid east}
  \inheritanchor[from=circle]{base}
  \inheritanchor[from=circle]{base west}
  \inheritanchor[from=circle]{base east}
  \inheritanchor[from=circle]{south}
  \inheritanchor[from=circle]{south west}
  \inheritanchor[from=circle]{south east}
  \inheritbackgroundpath[from=circle]
  \foregroundpath{
    \centerpoint%
    \pgf@xc=\pgf@x%                                                                                                                   \pgf@xc = x value of centerpoint
    \pgf@yc=\pgf@y%                                                                                                                   \pgf@yc = y value of centerpoint
    \pgfutil@tempdima=\radius%                                                                                              \pgfutil@tempdima = radius
    \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}%                                                                     \pgf@xb = outer xsep
    \pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}%                                                                     \pgf@yb = outer ysep
    \pgfmathsetmacro{\pgf@camera@rotate}{\pgfkeysvalueof{/tikz/camera rotate}}%                                            \pgf@camera@rotate = camera rotate
    \ifdim\pgf@xb<\pgf@yb%
      \advance\pgfutil@tempdima by-\pgf@yb%
    \else%
      \advance\pgfutil@tempdima by-\pgf@xb%
    \fi%
    \pgftransformrotate{\pgf@camera@rotate}% add -90 for standard east direction
    \pgfpathmoveto{\pgfpointadd{\pgfqpoint{\pgf@xc}{\pgf@yc}}{\pgfqpoint{0.624695047\pgfutil@tempdima}{0.780868809\pgfutil@tempdima}}}
    \pgfpathlineto{\pgfpointadd{\pgfqpoint{\pgf@xc}{\pgf@yc}}{\pgfqpoint{2\pgfutil@tempdima}{2.5\pgfutil@tempdima}}}
    \pgfpathlineto{\pgfpointadd{\pgfqpoint{\pgf@xc}{\pgf@yc}}{\pgfqpoint{-2\pgfutil@tempdima}{2.5\pgfutil@tempdima}}}
    \pgfpathlineto{\pgfpointadd{\pgfqpoint{\pgf@xc}{\pgf@yc}}{\pgfqpoint{-0.624695047\pgfutil@tempdima}{0.780868809\pgfutil@tempdima}}}
    \pgfpatharc{128.6598083}{51.34019175}{\pgfutil@tempdima}
    \pgfpathclose
    \pgfsetfillcolor{black}
    \pgfusepath{fill}
  }
}

% this declares a fixed camera, it has to be rotated by the /tikz/rotate key
\pgfdeclareshape{fixcamera}
{
  \inheritsavedanchors[from=circle] % this is nearly a circle
  \inheritanchorborder[from=circle]
  \inheritanchor[from=circle]{north}
  \inheritanchor[from=circle]{north west}
  \inheritanchor[from=circle]{north east}
  \inheritanchor[from=circle]{center}
  \inheritanchor[from=circle]{west}
  \inheritanchor[from=circle]{east}
  \inheritanchor[from=circle]{mid}
  \inheritanchor[from=circle]{mid west}
  \inheritanchor[from=circle]{mid east}
  \inheritanchor[from=circle]{base}
  \inheritanchor[from=circle]{base west}
  \inheritanchor[from=circle]{base east}
  \inheritanchor[from=circle]{south}
  \inheritanchor[from=circle]{south west}
  \inheritanchor[from=circle]{south east}
  \inheritbackgroundpath[from=circle]
  \foregroundpath{
    \centerpoint%
    \pgf@xc=\pgf@x%                                                                                                                   \pgf@xc = x value of centerpoint
    \pgf@yc=\pgf@y%                                                                                                                   \pgf@yc = y value of centerpoint
    \pgfutil@tempdima=\radius%                                                                                              \pgfutil@tempdima = radius
    \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}%                                                                     \pgf@xb = outer xsep
    \pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}%                                                                     \pgf@yb = outer ysep
    \ifdim\pgf@xb<\pgf@yb%                                                                                                           correction of radius variable
      \advance\pgfutil@tempdima by-\pgf@yb%
    \else%
      \advance\pgfutil@tempdima by-\pgf@xb%
    \fi%
    %\pgftransformrotate{-90}%
    \pgfpathmoveto{\pgfpointadd{\pgfqpoint{\pgf@xc}{\pgf@yc}}{\pgfqpoint{0.624695047\pgfutil@tempdima}{0.780868809\pgfutil@tempdima}}}
    \pgfpathlineto{\pgfpointadd{\pgfqpoint{\pgf@xc}{\pgf@yc}}{\pgfqpoint{2\pgfutil@tempdima}{2.5\pgfutil@tempdima}}}
    \pgfpathlineto{\pgfpointadd{\pgfqpoint{\pgf@xc}{\pgf@yc}}{\pgfqpoint{-2\pgfutil@tempdima}{2.5\pgfutil@tempdima}}}
    \pgfpathlineto{\pgfpointadd{\pgfqpoint{\pgf@xc}{\pgf@yc}}{\pgfqpoint{-0.624695047\pgfutil@tempdima}{0.780868809\pgfutil@tempdima}}}
    \pgfpatharc{128.6598083}{51.34019175}{\pgfutil@tempdima}
    \pgfpathclose
    \pgfsetfillcolor{black}
    \pgfusepath{fill}
  }
}

\makeatother

% this sets the default value of /tikz/camera rotate to 0 (no rotation, camera is oriented north)
% if we don't set it, it's automatically zero
\pgfkeyssetvalue{/tikz/camera rotate}{0}

% this defines a /tikz/camera style that takes one optional argument,
%  i.e. the rotation of the camera (which, again, is zero, if the argument is omitted
\tikzset{
    camera/.style={
        draw,
        ultra thick,
        minimum size=2cm,
        rotcamera,
        camera rotate=#1
    },
    camera/.default=0
}

\begin{document}
\begin{tikzpicture}[every node/.style={draw, ultra thick, minimum size=2cm}]
\node[fixcamera, rotate =    0] (c1) at (0,0)    {};
\node[fixcamera, rotate =   30] (c2) at (3,3)    {};
\node[fixcamera, rotate = -100] (c3) at (2.8,-1) {};

\draw[ultra thick, blue] (c1.south) to[out=270, in=210] (c3.210)
                         (c3.80)    to[out=80,  in=320] (c2.320)
                         (c2.255)   to[out=255, in=15]  (c1.15);
\end{tikzpicture}

\begin{tikzpicture}[every node/.style={draw, ultra thick, minimum size=2cm}]
\node[rotcamera, camera rotate =    0] (c1) at (0,0)    {};
\node[rotcamera, camera rotate =   30] (c2) at (3,3)    {};
\node[rotcamera, camera rotate = -100] (c3) at (2.8,-1) {};

\draw[ultra thick, blue] (c1.south) to[out=270, in=210] (c3.210)
                         (c3.80)    to[out=80,  in=320] (c2.320)
                         (c2.255)   to[out=255, in=15]  (c1.15);
\end{tikzpicture}

\begin{tikzpicture}
\node[camera      ] (c1) at (0,0)    {};
\node[camera=   30] (c2) at (3,3)    {};
\node[camera= -100] (c3) at (2.8,-1) {};

\draw[ultra thick, blue] (c1.south) to[out=270, in=210] (c3.210)
                         (c3.80)    to[out=80,  in=320] (c2.320)
                         (c2.255)   to[out=255, in=15]  (c1.15);
\end{tikzpicture}

\begin{tikzpicture}[every node/.style={draw}]
\draw (0,0) -- node[midway,rotcamera]                   {} (3,0)
            -- node[pos=.7,rotcamera, camera rotate=90] {} (3,3) to[out=180,in=90] (0,0) -- cycle;
\end{tikzpicture}
\end{document}

Outputs

Output of rotcamera (and optional camera rotate) or camera

rotcamera

Output of fixcamera (and additional rotate)

fixcamera

Output of a draw path with cameras

cameras on path