39

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

2 Answers 2

29

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

2
  • 7
    Really nice answer! Commented Oct 30, 2012 at 2:43
  • 2
    Beautiful answer, that makes it quite evident that defining shapes should not be the end-user's work :-) This very similar question was solved using pic, an alternative that is worth mentioning since it behaves quite like a shape, but does not require going down to the pgf level.
    – Michaël
    Commented Dec 25, 2017 at 17:47
22

The following code implements a macro which draws your camera symbol at a given position and angle. An example of use is included.

\documentclass{article}
\usepackage{tikz}
\begin{document}
\def\camera#1#2{
\begin{scope}[shift={#1}, rotate=#2]
    \draw [fill=black](0,0) -- (2,2.5) -- (-2,2.5) -- cycle;
    \draw [fill=white,ultra thick](0,0) circle (1);
\end{scope}
}

\begin{tikzpicture}
\camera{(0,0)}{0}
\camera{(3,3)}{30}
\camera{(2.8,-1)}{-100}
\end{tikzpicture}
\end{document}

Result

1
  • 2
    I already did this in my own code and it has some major downsides. If you have to draw lines or arrows between the shapes, you have to calculate the coordinates yourself, instead of being able to just point to an anchor of a shape. That's why I prefer defining actual shapes for bigger drawings by now. For a small one this is a fast solution, but for bigger ones the overhead pays out.
    – Dave
    Commented Dec 12, 2016 at 14:04

You must log in to answer this question.

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