Declaring shapes is one of the most useful features of the tikz/pgf
library. It can be extended by endless choice and really is a good idea.
The problem with the shapes is the difficulties one has to go through to get it to work.
This is due to the low-level coding that is necessary and essentially a good bookkeeping
of variables is the problem. If one is not used to low-level variable reassignment this
can become a cumbersome and even a task which cannot be completed without coding more than
the basic layer offers.
This is also the problem of your code.
Commenting your code
\pgfdeclareshape{carr}{%
\inheritsavedanchors[from=circle]
\inheritanchor[from=circle]{center}
\inheritanchor[from=circle]{north}
\inheritanchor[from=circle]{south}
\inheritanchor[from=circle]{east}
\inheritanchor[from=circle]{west}
All this is fine, just keep it, you may realise that your east, west, etc. is not
correctly placed. Thus you should add your own.
\backgroundpath{%
% center
\centerpoint
\pgf@xa=\pgf@x
\pgf@ya=\pgf@y
This is also good, you acquire the center coordinates. However, the center coordinates
only have a value if there is text in the node. They are defined as:
\pgf@x=.5\wd\pgfnodeparttextbox
\pgf@y=.5\ht\pgfnodeparttextbox
\advance\pgf@y by-.5\dp\pgfnodeparttextbox
Thus if no text is present they are both 0pt
. This tells you why it didn't show if no
node text is supplied.
% west triangle corner
\radius
\pgf@yb=-5\pgf@y
\centerpoint
\advance\pgf@yb by\pgf@y
\pgf@xb=-2\pgf@x
Your problem here is that \radius
is actually a dimension. You do not save this value in
any way. You need to use \radius
as a regular dimension:
\pgf@x=\radius
This means that what ever you are saving to \pgf@yb
you have no idea of what is (or
actually it is \pgf@y
from \centerpoint
, but that is another matter).
% east triangle corner
\radius
\pgf@yc=-5\pgf@y
\centerpoint
\advance\pgf@yc by\pgf@y
\pgf@xc=4\pgf@x
same problem here. This will not work.
% draw triangle..
\pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}%
\pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@yb}}%
\pgfpathlineto{\pgfpoint{\pgf@xc}{\pgf@yc}}%
\pgfpathclose
This is fine
% central circle (from pgflibraryshapes.geometric.code.tex)..
\pgf@process{\radius}
Here is another mistake. Let's go through what \pgf@process
does.
Lets consider the following function:
\def\pgfcalc{\pgf@xb=2pt\pgf@x=\pgf@xb}
\pgfcalc
will actually define both \pgf@xb
and \pgf@x
. In many cases you are only
interested in the final result, which is \pgf@x
and \pgf@y
. In such case one will
invoke \pgfcalc
by \pgf@process
.
\pgf@xb=1pt
\pgfcalc
% Here \pgf@xb and \pgf@x are set by \pgfcalc
% So \pgf@xb and \pgf@x are both 2pt
\pgf@xb=1pt
\pgf@process{\pgfcalc}
% Here ONLY \pgf@x is set. \pgf@xb is still 1pt
This means that you can only call \pgf@process
on a macro and not a dimension (as \radius
is). The code you refer to have \radius
defined as an anchor
which is a macro. You could have done it on \centerpoint
if \centerpoint
were to change any other than \pgf@x
and \pgf@y
.
For further information on the purposes see the manual.
Lets move on...
\pgfutil@tempdima=\pgf@x%
\pgfutil@tempdimb=\pgf@y%
\pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}%
\pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}%
\advance\pgfutil@tempdima by-\pgf@xb%
\advance\pgfutil@tempdimb by-\pgf@yb%
\pgfpathellipse{\centerpoint}{\pgfqpoint{\pgfutil@tempdimb}{0pt}}{\pgfqpoint{0pt}{\pgfutil@tempdimb}}%
% and lower circles
\pgf@xa=1pt % the radius:should be a parameter
\pgfutil@tempdima=\pgf@xb \advance\pgfutil@tempdima by\pgf@xa
\pgfutil@tempdimb=\pgf@yb \advance\pgfutil@tempdimb by-\pgf@xa
\pgfpathcircle{\pgfpoint{\pgfutil@tempdima}{\pgfutil@tempdimb}}{\pgf@xa}
\pgfutil@tempdima=\pgf@xb \advance\pgfutil@tempdima by-\pgf@xa
\pgfutil@tempdimb=\pgf@yb \advance\pgfutil@tempdimb by-\pgf@xa
\pgfpathcircle{\pgfpoint{\pgfutil@tempdima}{\pgfutil@tempdimb}}{\pgf@xa}
}}
This final part is actually ok, however, it is cumbersome to read and one can easily make
mistakes. I would partition it a bit more.
A fast solution
This solution has been made so that you can work more with it.
There are many options that you need to fine-tune and post-process.
It is up to you to complete with the top circle etc.
\documentclass{article}
\usepackage{tikz}
\makeatletter
\pgfdeclareshape{carr}{%
\inheritsavedanchors[from=circle]
\inheritanchor[from=circle]{center}
\inheritanchor[from=circle]{north}
\inheritanchor[from=circle]{south}
\inheritanchor[from=circle]{east}
\inheritanchor[from=circle]{west}
\backgroundpath{%
% Save radius to x
\pgf@x=\radius
% Radius is also containing the "minimum width" and "minimum height"
% This ensures that even with no text the shape will be drawn.
% Unless of course that min are set to 0pt
% So no need to check for that
% Save radius
\pgfutil@tempdima=\pgf@x%
% west triangle corner "b"
\pgf@xb=-3\pgf@x%
\pgf@yb=-4\pgf@x%
% east triangle corner "c"
\pgf@xc= 3\pgf@x%
\pgf@yc=-4\pgf@x%
% If text is present shift shape to center
% You need to shift more, but to get the idea
\centerpoint
\advance\pgf@xb by\pgf@x
\advance\pgf@yb by\pgf@y
\advance\pgf@xc by\pgf@x
\advance\pgf@yc by\pgf@y
% Save centerpoint in "a" (top triangle point)
\pgf@xa=\pgf@x
\pgf@ya=\pgf@y
% Below are good for debugging purposes.
%\message{^^JTop : \the\pgf@xa,\the\pgf@ya}
%\message{^^JWest: \the\pgf@xb,\the\pgf@yb}
%\message{^^JEast: \the\pgf@xc,\the\pgf@yc}
%\message{^^JCent: \the\pgf@x,\the\pgf@y}
% draw triangle..
\pgfpathmoveto{\pgfpoint{\pgf@xa}{\pgf@ya}}%
\pgfpathlineto{\pgfpoint{\pgf@xb}{\pgf@yb}}%
\pgfpathlineto{\pgfpoint{\pgf@xc}{\pgf@yc}}%
\pgfpathclose
% The radius of the small circles
% Read in from option TODO
\pgfutil@tempdimb=3pt
% Move top triangle to head circle
\advance\pgf@ya by.25\pgfutil@tempdimb
% Move west triangle corner to west circle center
\advance\pgf@xb by 1.5\pgfutil@tempdima
\advance\pgf@yb by -\pgfutil@tempdimb
% For handling line thickness if you wish "edge touch" and not "overlap"
%\advance\pgf@yb by -.5\pgflinewidth
% Move east triangle corner to east circle center
\advance\pgf@xc by-1.5\pgfutil@tempdima
\advance\pgf@yc by -\pgfutil@tempdimb
% For handling line thickness if you wish "edge touch" and not "overlap"
%\advance\pgf@yc by -.5\pgflinewidth
% This saves underlying "stuff" when you have the explicit `\pgfqpoint` and is thus a little faster
\edef\pgf@marshal{%
\noexpand\pgfpathcircle{%
\noexpand\pgfqpoint{\the\pgf@xa}{\the\pgf@ya}}
{\the\pgfutil@tempdimb}%
\noexpand\pgfpathcircle{%
\noexpand\pgfqpoint{\the\pgf@xb}{\the\pgf@yb}}
{\the\pgfutil@tempdimb}%
\noexpand\pgfpathcircle{%
\noexpand\pgfqpoint{\the\pgf@xc}{\the\pgf@yc}}
{\the\pgfutil@tempdimb}%
}\pgf@marshal
}}
\makeatother
\begin{document}
\begin{tikzpicture}
\node[shape=carr,draw] at (3,0) {a};
\node[shape=carr,draw] at (5,0) {}; % missing triangle (not anymore)
% Your \draw example will never work! shapes are nodes, you need a node to assign the shape!
\end{tikzpicture}
\end{document}
Here is the end result:
Good luck battling those shapes! :)
OK, there is a lot going on here, but hopefully the comments go someway to making it clear how I've achieved this. I use a lot of nested style
definitions, which possibly may make things tricky to follow but hopefully the style
names give some indication of what they do. I also use a lot of nested \foreach
loops for no other reason than I found it easier than using a matrix.
Some apologies in advance for mangling the German words in the style
definitions.
\documentclass[border=5pt]{standalone}
\usepackage{tikz}
% pifont for tick and cross.
\usepackage{pifont}
% Only need the matrix library for delimiters.
\usetikzlibrary{matrix,fit}
\usetikzlibrary{shapes.geometric}
% Define some colors.
\colorlet{Klasse A}{red}
\colorlet{Klasse B}{blue}
\colorlet{probe color}{Klasse A}
\colorlet{correct color}{Klasse A}
\colorlet{incorrect color}{Klasse A}
\colorlet{testproben}{gray!20}
\def\vmark{}
\tikzset{
tight fit/.style={inner sep=0pt, outer sep=0pt},
probe color/.code={\colorlet{probe color}{#1}},
correct color/.code={\colorlet{correct color}{#1}},
incorrect color/.code={\colorlet{incorrect color}{#1}},
probe/.style args={#1-#2}{%
outer sep=0pt,
shape=rectangle,
probe #1-#2/.try,
execute at begin node={%
% Hide the testproben spike in a style rather than
% clutter up the main code.
\begin{tikzpicture}[x=2.75pt,y=1.5pt, scale=0.625]
\path [draw=probe color] plot [smooth] coordinates {(0,0)
(1,2) (2,10) (3,1) (4,3) (5,1) (6,4) (7,0)};
\end{tikzpicture}}
},
vorhesage/.style args={#1-#2}{
shape=circle,
draw=correct color,
text=white,
font=\bf\small,
vorhesage #1-#2/.try,
incorrect vorhesage #1-#2/.try,
minimum size=0.625cm
},
surrogat/.style={
shape=regular polygon,
regular polygon sides=3,
minimum height=1cm,
draw
}
}
\tikzset{
% The styles applied to specified testproben and (correct) vorhesage
testproben/.style args={#1-#2}{
probe #1-#2/.style={
fill=testproben
},
vorhesage #1-#2/.style={
fill=correct color,
execute at begin node=\def\vmark{\ding{51}}% A tick
}
},
% The styles applied to specified testproben and (incorrect) vorhesage
testproben */.style args={#1-#2}{
probe #1-#2/.style={
fill=testproben
},
incorrect vorhesage #1-#2/.style={
fill=incorrect color,
draw=correct color,
very thick,
execute at begin node=\def\vmark{\ding{55}}% A cross
}
},
Daten A/.style={
A/.try,
probe color=Klasse A,
},
Daten B/.style={
B/.try,
probe color=Klasse B,
shift={(5,0)}
},
Vorhesage A/.style={
A/.try,
correct color=Klasse A,
incorrect color=Klasse B,
},
Vorhesage B/.style={
B/.try,
correct color=Klasse B,
incorrect color=Klasse A,
shift={(5,0)}
},
iteration 1/.style={
% Define the testproben (and vorhesagen) for iteration 1
A/.style={
testproben={1-4}, % testproben row 1, column 4
testproben={2-3}, testproben={2-5},
testproben *={3-1}, testproben={3-2},
},
B/.style={
testproben={1-2}, testproben={1-3},
testproben *={2-1},
testproben={3-4}
},
},
iteration 2/.style={
A/.style={
testproben={1-1}, testproben={1-4},testproben={1-5},
testproben={2-2},
testproben={3-5}
},
B/.style={
testproben={1-2}, testproben={2-1},
testproben={3-1}, testproben={3-3}
}
},
iteration 3/.style={
A/.style={
testproben={1-5}, testproben={1-4},
testproben={2-2}, testproben={2-2},
testproben *={3-1}
},
B/.style={
testproben={1-2},
testproben={2-1},
testproben={3-3}, testproben={3-4}
},
}
}
\begin{document}
\begin{tikzpicture}[x=0.75cm, y=0.75cm, >=stealth]
\foreach \itr in {1,2,3}{
% Install the A and B styles for this iteration.
% The A and B styles define the testproben for Klasse A
% and Kasse B.
\tikzset{iteration \itr/.try}
\foreach \g in {1,2,3}{
\ifcase\g
\or
% Draw the probes
\foreach \K/\I/\J in {A/3/5, B/3/4}{%
% Install the Daten \K style.
% For Daten Klasse A this sets the probe color to blue
% For Daten Klasse B this sets the probe color to red
% and shifts everything along.
% In both cases the relevant style (A or B) is `executed'
% defining which probes are testproben.
\tikzset{Daten \K/.try}
\foreach \i in {1, ..., \I}{%
\foreach \j in {1, ..., \J}{%
\node (probe \itr-\K-\i-\j) at (\j, -\itr*3.5-\i)
[probe=\i-\j] {};
}
}
% Define a node that fits around all the nodes for this
% particular Daten Klasse.
\node [tight fit,fit=(probe \itr-\K-1-1)
(probe \itr-\K-\I-\J)]
(iteration \itr\space daten \K){};
}
\or
% Now draw the Surrogats (Surrogaten?)
\foreach \I in {1,2,3}{
\tikzset{shift=(probe \itr-B-1-4.east)}
\node (surrogat \itr-\I) at (1.125,1-\I) [surrogat] {};
\draw [ultra thick, ->]
(surrogat \itr-\I.west) ++(-0.625,0) -- ++(0.5,0);
\draw [ultra thick, ->]
(surrogat \itr-\I.east) ++(0.125,0) -- ++(0.5,0);
}
\or
% Finally the Forcasts
%
% Shift things along a but from the surrogats.
\tikzset{shift={(surrogat \itr-1)}, shift=(0:0.5)}
\foreach \K/\I/\J in {A/3/5, B/3/4}{
% This is the same as for the Daten Klasse,
% execept tis time the vorhesage proben are drawn.
\tikzset{Vorhesage \K/.try}
\foreach \i in {1, ..., \I}{
\foreach \j in {1, ..., \J}{
\node (vorhesage \itr-\K-\i-\j) at (\j, -\i+1)
[vorhesage=\i-\j] {\vmark};
}
},
% Draw a node around each set of vorhesage nodes.
\node [tight fit,
fit=(vorhesage \itr-\K-1-1) (vorhesage \itr-\K-\I-\J)]
(iteration \itr\space vorhesage \K){};
}
\fi%
}
}
% Now add the labels and delimiters.
\foreach \itr in {1,2,3}{
\node [tight fit, fit={(iteration \itr\space daten A)}, left
delimiter=\{, label={[xshift=-0.5cm]left:\itr.
Iteration}] {};
}
\foreach \K in {A, B}{
\node [tight fit, fit={(iteration 1 daten \K)}, above
delimiter=\{,label={[yshift=0.5cm]90:Daten Klassen \K}] {};
\node [tight fit, fit={(iteration 1 vorhesage \K)}, above
delimiter=\{,label={[yshift=0.5cm, align=center]90:{Vorhesage
\\Proben \K}}] {};
}
\node [fill=testproben, rounded corners=1ex, below=0.25cm, anchor=north west]
(testprobe)
at (probe 3-A-3-1.south east){Testprobe};
\draw [very thick, ->, rounded corners=1ex]
(testprobe.west) -| (probe 3-A-3-1.south);
\node [below=0.25cm, anchor=north west]
(trainingsprobe)
at (probe 3-A-3-4.south east){Trainingsprobe};
\draw [very thick, ->, rounded corners=1ex]
(trainingsprobe.west) -| (probe 3-A-3-4.south);
\node [below=1cm, anchor=north east]
(surrogatsmodelle)
at (surrogat 3-3.south west){$i=3\times k=3$ Surrogatsmodelle};
\draw [very thick, ->, rounded corners=1ex]
(surrogatsmodelle.east) -| (surrogat 3-3.south);
\node [below=0.5cm, align=left, anchor=north west] (eingeordnet)
at (vorhesage 3-A-3-1.south east){Testprobe aus Klasse \textcolor{Klasse
A}{A} wird \\als Klasse \textcolor{Klasse B}{B} eingeordnet};
\draw [very thick, ->, rounded corners=1ex]
(eingeordnet.west) -| (vorhesage 3-A-3-1.south);
\end{tikzpicture}
\end{document}
Best Answer
For commutative diagrams I suggest using the
tikz-cd
package; it usesTikZ
to facilitate the drawing of commutative diagrams (it has its own arrows library designed for diagrams and the arrow you are looking for is already buil-in); a little example with the requested inclusion (and some others just for illustration):You can use the
arrows
library fromTikZ
, so for example, to use the-latex
style fromTikZ
, you can sayThe above code changes the tip arrow style for all the diagrams, but you can select the -latex arrow tip just for some arrows (perhaps not really desirable to have two different arrow tips on the same diagram):