12

I want to create a database shape in tikz that looks something like this:

enter image description here

I need the two arcs in the middle, so I cannot directly use cylinder, as suggested in this question.

Ideally, I would like to start from the cylinder shape and just add a few arcs, but I cannot figure out how to do this? Is the code of the cylinder shape available somewhere? Or is there a better way to achieve this?

I have looked at this question that asks how to create a shape for a camera, but it is quite far from what I want.

1
  • The cylinder shape is defined in this file starting from line 4021.
    – Max
    Jul 24, 2018 at 10:04

2 Answers 2

14

I don't think you need to define a new shape for this, unless you also want to use the border anchor. If you do, please let me know.

For simple usage you could use the following:

\documentclass[tikz,margin=2mm]{standalone}

\makeatletter
\tikzset{
    database/.style={
        path picture={
            \draw (0, 1.5*\database@segmentheight) circle [x radius=\database@radius,y radius=\database@aspectratio*\database@radius];
            \draw (-\database@radius, 0.5*\database@segmentheight) arc [start angle=180,end angle=360,x radius=\database@radius, y radius=\database@aspectratio*\database@radius];
            \draw (-\database@radius,-0.5*\database@segmentheight) arc [start angle=180,end angle=360,x radius=\database@radius, y radius=\database@aspectratio*\database@radius];
            \draw (-\database@radius,1.5*\database@segmentheight) -- ++(0,-3*\database@segmentheight) arc [start angle=180,end angle=360,x radius=\database@radius, y radius=\database@aspectratio*\database@radius] -- ++(0,3*\database@segmentheight);
        },
        minimum width=2*\database@radius + \pgflinewidth,
        minimum height=3*\database@segmentheight + 2*\database@aspectratio*\database@radius + \pgflinewidth,
    },
    database segment height/.store in=\database@segmentheight,
    database radius/.store in=\database@radius,
    database aspect ratio/.store in=\database@aspectratio,
    database segment height=0.1cm,
    database radius=0.25cm,
    database aspect ratio=0.35,
}
\makeatother

\begin{document}
    \begin{tikzpicture}[line width=1pt]
        \node[database,label=below:some label] {};
        \node[database,label=below:some label,database radius=1cm,database segment height=0.5cm] at (3,0) {};
    \end{tikzpicture}
\end{document}

The \makeatletter and \makeatother are necessary to use macros with @ in them. It is not really needed here, but I prefer to use it to keep macros readable and to prevent you from overwriting them somewhere without using the pgf keys.

This examples results in:

enter image description here

Edit

As requested in the comments, with the following code you can set a style for all three segments separately. You can use database top segment={<style>}, database middle segment={<style>}, and database bottom segment={<style>} for this.

\documentclass[tikz,margin=2mm]{standalone}

\makeatletter
\tikzset{
    database top segment style/.style={draw},
    database middle segment style/.style={draw},
    database bottom segment style/.style={draw},
    database/.style={
        path picture={
            \path [database bottom segment style]
                (-\db@r,-0.5*\db@sh) 
                -- ++(0,-1*\db@sh) 
                arc [start angle=180, end angle=360,
                    x radius=\db@r, y radius=\db@ar*\db@r]
                -- ++(0,1*\db@sh)
                arc [start angle=360, end angle=180,
                    x radius=\db@r, y radius=\db@ar*\db@r];
            \path [database middle segment style]
                (-\db@r,0.5*\db@sh) 
                -- ++(0,-1*\db@sh) 
                arc [start angle=180, end angle=360,
                    x radius=\db@r, y radius=\db@ar*\db@r]
                -- ++(0,1*\db@sh)
                arc [start angle=360, end angle=180,
                    x radius=\db@r, y radius=\db@ar*\db@r];
            \path [database top segment style]
                (-\db@r,1.5*\db@sh) 
                -- ++(0,-1*\db@sh) 
                arc [start angle=180, end angle=360,
                    x radius=\db@r, y radius=\db@ar*\db@r]
                -- ++(0,1*\db@sh)
                arc [start angle=360, end angle=180,
                    x radius=\db@r, y radius=\db@ar*\db@r];
            \path [database top segment style]
                (0, 1.5*\db@sh) circle [x radius=\db@r, y radius=\db@ar*\db@r];
        },
        minimum width=2*\db@r + \pgflinewidth,
        minimum height=3*\db@sh + 2*\db@ar*\db@r + \pgflinewidth,
    },
    database segment height/.store in=\db@sh,
    database radius/.store in=\db@r,
    database aspect ratio/.store in=\db@ar,
    database segment height=0.1cm,
    database radius=0.25cm,
    database aspect ratio=0.35,
    database top segment/.style={
        database top segment style/.append style={#1}},
    database middle segment/.style={
        database middle segment style/.append style={#1}},
    database bottom segment/.style={
        database bottom segment style/.append style={#1}}
}
\makeatother

\begin{document}
    \begin{tikzpicture}[line width=1pt]
        \node[database,label=below:some label, database middle segment={fill=green}, database bottom segment={draw=none, fill=blue}] {};
        \node[database,label=below:some label,database radius=1cm,database segment height=0.5cm, database top segment={draw=blue,fill=red}] at (3,0) {};
    \end{tikzpicture}
\end{document}

The example above results in:

enter image description here

7
  • If I try to add a label \node[below of=database] {some label};, the position of the label looks incorrect. Is this because of the missing border anchor?
    – Peter
    Jul 24, 2018 at 12:39
  • 1
    @Peter when you use below of=database, you place the new node at a certain distance from the center of the referenced node. With the positioning library you can use below=of database, which differs slightly in notation but makes sure that the node distance is kept between the outer dimensions of the two nodes. To add a label, it is simpler to draw the database node as follows: \node[database,label=below:some label] {};. (Both of these things don't bother with the border anchor).
    – Max
    Jul 24, 2018 at 12:43
  • @Max How can we fill this database with color for each layer and keep the circles' borders to be black? Jan 12, 2020 at 17:36
  • 1
    @MoustafaMahmoud See my edit.
    – Max
    Jan 14, 2020 at 8:31
  • 1
    @ChristophThiede You can pass text width=<length> to the label options between square brackets as follows: \node[database,label={[text width=2cm]below:some long label that should be wrapped},database radius=1cm,database segment height=0.5cm, database top segment={draw=blue,fill=red}] at (6,0) {};. Optionally you could add align=center.
    – Max
    Jun 30, 2022 at 12:13
9

You are right, a cylinder exists already in shapes.geometric and is is very easy to use it to draw a database shape.

\documentclass[tikz,border=3.14mm]{standalone}
\usetikzlibrary{shapes.geometric}
\tikzset{database/.style={cylinder,aspect=0.5,draw,rotate=90,path picture={
\draw (path picture bounding box.160) to[out=180,in=180] (path picture bounding
box.20);
\draw (path picture bounding box.200) to[out=180,in=180] (path picture bounding
box.340);
}}}
\begin{document}
\begin{tikzpicture}
\node[database] {};
\end{tikzpicture}
\end{document}

enter image description here

5
  • This is a good solution, even keeping the border anchor intact, but it doesn't scale so nicely (+1 nonetheless for simplicity).
    – Max
    Jul 24, 2018 at 14:19
  • @MaxSnippe Thanks for the upvote (and of course I upvoted your answer long time ago...;-) I'm not sure, though, I understand your concerns about scaling. At least \node[database,scale=3] {}; works fine as far as I can see.
    – user121799
    Jul 24, 2018 at 16:27
  • I should have been more specific. I actually meant when setting a different minimum width, e.g. \node[database,minimum width=2cm]{};. Firstly, this overwrites the aspect key I believe, such that the to[out=180,in=180] have a different radius then the cylinder itself, and secondly it places the border anchors (.20,.160, etc.) at completely different places.
    – Max
    Jul 24, 2018 at 17:05
  • 1
    @MaxSnippe Yes, you're right, one should not stretch this ;-)
    – user121799
    Jul 24, 2018 at 17:07
  • This solution is also really nice, but I ended up accepting the other answer, because I went with that one.
    – Peter
    Jul 25, 2018 at 7:38

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