0

If possible, I want to draw following figure, where two nodes side-by-side inside a cloud:

enter image description here

  • In order to achieve this should I use fit for the both inner-nodes? I was not able to fit two-muliple nodes into \AsymCloud.
  • Should I first draw cloud and than align nodes' positions inside the cloud? I was able to manage this by try-and-error:
\documentclass[journal,onecolumn]{IEEEtran}
\usepackage{tikz}
\usetikzlibrary{fit,matrix,positioning,shadows}
\usetikzlibrary{shapes.geometric,fit}
\usetikzlibrary{shapes.misc}
\usetikzlibrary{arrows,shapes,shapes.geometric,shapes.multipart,trees}

\tikzset{fileshape/.style 2 args={chamfered rectangle, draw, ultra thick, chamfered rectangle corners=north east, minimum height=12mm, minimum width=10mm, #1, label={[text=#1]270:{\sffamily #2}} }}

\newcommand{\AsymCloud}[3]{ \begin{scope}[shift={#1},scale=#3,thick,draw=cyan] \draw (-1.6,-0.7) .. controls (-2.3,-1.1) and (-2.7,0.3) .. (-1.7,0.3)coordinate(asy1) .. controls (-1.6,0.7) and (-1.2,0.9) .. (-0.8,0.7) .. controls (-0.5,1.5) and (0.6,1.3) .. (0.7,0.5) .. controls (1.5,0.4) and (1.2,-1) .. (0.4,-0.6)coordinate(asy2) .. controls (0.2,-1) and (-0.2,-1) .. (-0.5,-0.7) .. controls (-0.9,-1) and (-1.3,-1) .. cycle; \path (asy1) -- (asy2) node[pos=0.5] {#2}; \end{scope} }

\usepackage{hyperref} \begin{document} \begin{figure}[!htp] \centering \begin{tikzpicture}[% >=latex,shorten >=2pt,shorten <=2pt,shape aspect=1, arrow/.style={<->, >=stealth, semithick}, block/.style={}, every node/.style = {font=\scriptsize} ] \AsymCloud{(3.3, 0.55)}{}{0.75} \node[cylinder,shape border rotate=90,draw,fill=white,inner xsep=3pt,align=center,xshift=35mm,yshift=0.5cm] (A) {}; \node[cylinder,shape border rotate=90,draw,fill=green,inner xsep=3pt,align=center,left of=A] (A') {};

    %% Here I was not able to fit the node into \AsymCloud
    \node[cylinder,shape border rotate=90,draw,fill=white,shape aspect=0.2,font={\baselineskip=9pt},
    inner xsep=3pt,align=center,xshift=3mm,yshift=-2mm] (D') {};
    \node[draw,cloud,inner sep=-1.2pt,draw=cyan,thick,fit=(D')] (D) {};
\end{tikzpicture}

\end{figure}

\end{document}

Output fit and positioning approach:

enter image description here

alper
  • 1,389

2 Answers2

2

This fits a saved tikzpicture of the cloud to a local bounding box. The problem is, you need to define inner and outer rectangles for the cloud. The inner rectangle holds the contents while the outer rectangle is used for the bounding box.

I wound up making the outer rectangle 2.2 times the size of the inner rectangle with the same center. Then all I have to do is increase the size by 2.2.

\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\usetikzlibrary{shapes.misc}
\usetikzlibrary{arrows,shapes,shapes.geometric,shapes.multipart}

\newsavebox{\cloud} \savebox\cloud{\begin{tikzpicture} \begin{scope}[thick,draw=cyan] \draw (-1.6,-0.7) .. controls (-2.3,-1.1) and (-2.7,0.3) .. (-1.7,0.3) coordinate(asy1) .. controls (-1.6,0.7) and (-1.2,0.9) .. (-0.8,0.7) .. controls (-0.5,1.5) and (0.6,1.3) .. (0.7,0.5) .. controls (1.5,0.4) and (1.2,-1) .. (0.4,-0.6) coordinate(asy2) .. controls (0.2,-1) and (-0.2,-1) .. (-0.5,-0.7) .. controls (-0.9,-1) and (-1.3,-1) .. cycle; \path (asy1) -- (asy2); \end{scope} \pgfresetboundingbox %\draw (-1.4,-0.5) rectangle (0.6,0.6);% inner rectangle \coordinate (center) at ($(-1.4,-0.5)!0.5!(0.6,0.6)$); \path ($(center)!2.2!(-1.4,-0.5)$) ($(center)!2.2!(0.6,0.6)$);% outer rectangle %\draw[red] (current bounding box.south west) rectangle (current bounding box.north east); \end{tikzpicture}}

\newcommand{\ACwidth}{}% reserve global names \newcommand{\ACheight}{}

\newcommand{\AsymCloud}[1]{% #1 = target bounding box \begin{pgfinterruptboundingbox} \pgfpointdiff{\pgfpointanchor{#1}{south west}}% {\pgfpointanchor{#1}{north east}}% \pgfgetlastxy{\ACwidth}{\ACheight}% \global\let\ACwidth=\ACwidth \global\let\ACheight=\ACheight \end{pgfinterruptboundingbox} \node[inner sep=0pt] at ({#1}) {\setlength{\dimen0}{\ACwidth}% \setlength{\dimen1}{\ACheight}% \resizebox{2.2\dimen0}{2.2\dimen1}{\usebox\cloud}}; }

\begin{document} \begin{tikzpicture}[% >=latex,shorten >=2pt,shorten <=2pt,shape aspect=1, arrow/.style={<->, >=stealth, semithick}, block/.style={}, every node/.style = {font=\scriptsize} ] \begin{scope}[local bounding box=target] \node[cylinder,shape border rotate=90,draw,fill=white,inner xsep=3pt] at (3.5,0.5) (A) {}; \node[cylinder,shape border rotate=90,draw,fill=green,inner xsep=3pt,left of=A] (A') {}; \end{scope} \AsymCloud{target}

    \begin{scope}[local bounding box=D2]
      \node[cylinder,shape border rotate=90,draw,fill=white,shape aspect=0.2] at(0.4, -0.2) (D) {};
    \end{scope}% note: D is not a rectangle
    %\draw[red] (D2.south west) rectangle (D2.north east);
    \AsymCloud{D2}
\end{tikzpicture}

\end{document}

demo

John Kormylo
  • 79,712
  • 3
  • 50
  • 120
2

You can use the fit library to fit a rectangle around your cylinders and use that as reference to draw the cloud. (Meaning, we measure how much space they need and use that space as a reference for drawing the cloud.)

I've adjusted the path of the cloud to be 0.3 shifted to the right so that the content seems more centered in it.

I'm assuming the clouds are always oriented the same and you will never need to rotate them, otherwise the logic is a bit more complicated. I'm using the calc library for now but this could be written very easily more efficient with basic PGF macros or actually turned into a shape if necessary. (Though, not with the same functionalities as other shapes, that would need much more work.)

This doesn't truly makes sure that the nodes are actually inside the cloud but the path of the cloud is just right that it will be.

Code

\documentclass[tikz]{standalone}
\usetikzlibrary{calc, fit, positioning, shapes.geometric, shapes.symbols}
\tikzset{
  AsymFit/.style={
    reset cm, % just to be safe
    insert path={
      node[overlay, shape=rectangle, inner sep=+0pt,
        outer sep=+0pt, path only, fit={#1}](@){}
      [shift=(@)] let \p X=(@.east), \p Y=(@.north), \n@={max(\x X, \y Y)} in},
    x={(right:\n@)}, y={(up:\n@)}, AsymCloud},
  AsymCloud/.style={insert path={
    (-1.3,-0.7) .. controls (-2.0,-1.1) and (-2.4, 0.3) .. (-1.3, 0.3)
                .. controls (-1.3, 0.7) and (-0.9, 0.9) .. (-0.5, 0.7)
                .. controls (-0.2, 1.5) and ( 0.9, 1.3) .. ( 1.0, 0.5)
                .. controls ( 1.8, 0.4) and ( 1.5,-1  ) .. ( 0.8,-0.6)
                .. controls ( 0.5,-1  ) and ( 0.1,-1  ) .. (-0.2,-0.7)
                .. controls (-0.6,-1  ) and (-1.0,-1  ) .. cycle}}}
\begin{document}
\begin{tikzpicture}[
  Cylinder/.style={shape=cylinder, shape border rotate=90, draw, inner xsep=+3pt}]
\node[Cylinder]            (A)  {};
\node[Cylinder, left=of A] (A') {};

\draw[cyan, thick, AsymFit=(A)(A')]; \end{tikzpicture} \end{document}

Output

enter image description here

Qrrbrbirlbel
  • 119,821
  • 2
    Load the bbox library and use bezier bounding box on the cloud path to get a close bounding box around the cloud. (Though we could hard-code this in if you need this often.) – Qrrbrbirlbel Aug 12 '23 at 21:12
  • Is it possible to reduce size of the \AsymCloud? originally I was scaling it using ex: \AsymCloud{(3.3, 0.55)}{}{0.75} – alper Aug 14 '23 at 16:26
  • 1
    @alper Sure. Just adjust the value \n@, say \n@={0.75*max(\x X, \y Y)}, though the cloud might clip the fitted area now. You might have to adjust the cloud drawing accordingly. – Qrrbrbirlbel Aug 14 '23 at 17:07