# TikZ: use-provide UML-style

I would like to draw some components diagrams with use and provide ports with TikZ. But the way I want them to look is quite different from what tikz-uml since my needs are differents. I do not focus on describing a real application, but more on the structure between components.

There are mainly two cases: those that I know how verbose I want the code to be, and those I'm not sure about the way to express them.

Here are the simple cases.

• When the components are close enough the U and the circle should touch.

And the code should be as simple as this.

\begin{tikzpicture}
\node[component] (A) {A};
\node[component,right of=A] (B) {B};
\draw[useprovide] (A) -- (B);
\end{tikzpicture}

• But when the components are too far away, the port stay within 1cm of the border of the component and the ports are connected with a line.

And the code should remain exactly the same.

• Also, two components may be connected by more than one pair of ports and they should be placed nicely.

And the code should remain as simple as possible. For instance:

\begin{tikzpicture}
\node[component] (A) {A};
\node[component,above of=A] (B) {B};
\draw[useprovide] (A) -- (B);
\draw[useprovide] (A) -- (B);
\end{tikzpicture}


Optionally it would be nice if it would allow me to put some text on the lines and around the use and provide ports.

And here are the cases I also need to draw but I don't know how I want to write them.

• One "use" port for several "provide" ports.

Notice the small separation between the lines on the "use" side while they still touch the half circle.

• One "provide" port for multiple "use" ports.

Again, there is a small gap between the lines.

• And last but not least, when the components are far away in a complex setting, I may want to decide of the path of the line connecting them, but let the code put the use and provide shapes at the right place.

Notice How the long connection between A and D is made of 3 parts. I may also want the 90° turn on the side of D to happen before the use port. (But I'll probably try to avoid that.)

The question is: how can I do that easily? Should I use a decorated path? Arrow tips? Something completely different? For a given method, how do I handle those 6 cases?

The code of the examples above is ugly and full of little adjustments.

What I tried:

• I tried to define the U shape as a new node shape. I basically copied the semi circle predefined node shape code and modified it to put it in the right direction and offset it a bit so that the path it terminates just touch it. But the fact that it is part of the PGF layer make it hard to use. I especially don't understand how the transformations (translations and rotations) work.

• I tried with a to path operation but things get complicated as soon as I want to handle the second case as there is no facility like the state precondition of the path length as there is for the decorated path. The automatic corrdinate transformation of the decorated path (that makes the path drawing always straight on the right) is also absent from the to path operation and forces me to work in 2D instead of 1D. In addition to this, it draws my half circle backward sometimes for no apparent reason.

I am open to any other solution as long as it makes me write as little "noise" code as possible for each schema.

I can provide the code of my failed attempts if needed, as well as the code I used for the schema here.

• Sorry but I don't understand what's your question. Do you want somebody develops a package for such diagrams? Dec 4, 2014 at 10:41
• @Ignasi here it is, I clarified the question and the things I tried. Dec 4, 2014 at 12:14

Next code shows you a possible solution. Instead of new shapes or decorations I propose some .style which will add U or circles to regular nodes. I've preferred to use this solution instead of pics because It's not so easy to place last ones.

All component boxes are defined with

component/.style={draw, rounded corners, minimum width=3cm, minimum height=1.5cm, fill=green}


while u and o additions are based in two circle nodes

u/.style={draw,circle,minimum size=7mm,outer sep=0pt},
o/.style={draw,circle,minimum size=6mm,outer sep=0pt}


The o-arm is just a line with a circular node attached to it:

owest/.style 2 args={append after command={
\pgfextra
\draw ([yshift=#1]\tikzlastnode.west)--++(180:6.5mm)
node[o,anchor=east] (\tikzlastnode-#2) {};
\endpgfextra}},
owest/.default={0mm}{owest},


and the u-arm is also a line with a circular node, but clipped to its half:

ueast/.style 2 args={append after command={
\pgfextra
\begin{scope}
\clip ([yshift={#1+4mm}]\tikzlastnode.east) rectangle ++(1cm,-8mm);
\draw ([yshift=#1]\tikzlastnode.east)--++(0:6.5mm)
node[u,anchor=west] (\tikzlastnode-#2) {};
\end {scope}
\endpgfextra}},
ueast/.default={0mm}{ueast},


As you can see both styles have two arguments, the first one is a x/y-shift around the main anchor (north, west, south, east) and the second a name sufix. Both arguments will help to draw double connections between component. By default their values are 0mm shift and suffix {u|o}{north|east|south|west}.

Both u and o components are circular nodes instead of just circles because this way it's easier to draw paths to any of their positions like in your last three examples.

As you can see all dimensions are fixed, but you can change according your taste. With the code provided, u-arm is 1cm long, o-arm is 1.25cm long and distance between nodes should be 2cm to simulate attaching both. May be you'll have to take care with these values in case you change line width.

\node[component,ueast,onorth] (A) {A};
\node[component, right=2cm of A, owest,unorth] (B) {B};
\draw[|<->|,red] ([yshift=-3mm]A.north east)--++(0:2cm) node[above,midway]{\SI{2}{\centi\meter}};
\draw[|<->|,red] ([xshift=5mm]B.north)--++(90:{1cm}) node[right,midway]{\SI{1}{\centi\meter}};
\draw[|<->|,red] ([xshift=-5mm]A.north)--node[left]{\SI{1.25}{\centi\meter}} ++(90:{1.25cm});


When component nodes are separated, you can draw lines between any positions of their u or o nodes.

Be careful. You can use something like B-owest when referring to a o-arm node because B-owest really means "draw until B-owest.center and stop at its border", but it you use this expression with an u-arm, the same will happen. The blue line in next example shows it. Next code shows how to solve this problem using explicit border anchor points, like A-ueast.west or B-owest.130. The last one is an anchor at angle 130 over nodes border and the first is an alias for A-ueast.180.

\node[component,ueast] (A) {A};
\node[component, right=5cm of A, owest] (B) {B};
\draw (A-ueast.west)--(B-owest);
\draw[red] (A-ueast.170)--(B-owest.130);
\draw[red] (A-ueast.-120)--(B-owest.south);


With this syntax it's easy to get joints like in third example:

When you have to draw more than one connection in same component side you can use the corresponding style, but providing corresponding arguments. Something like:

\node[component,onorth={-5mm}{-5},onorth={5mm}{5}] (A) {A};
\node[component, above=2cm of A,usouth={-5mm}{-5},usouth={5mm}{5}] (B) {B};


or

\node[component,oeast={-4mm}{-4},ueast={4mm}{4}] (A) {A};
\node[component,right=4cm of A,uwest={-4mm}{-4},owest={4mm}{4}] (B) {B};
\draw (A--4)--(B--4.east);
\draw (A-4.west)--(B-4);


That's all. Now the complete code:

\documentclass[tikz,border=2mm]{standalone}
\usetikzlibrary{positioning}
\usepackage{siunitx}

\begin{document}
\tikzset{
component/.style={draw, rounded corners, minimum width=3cm, minimum height=1.5cm, fill=green},
u/.style={draw,circle,minimum size=7mm,outer sep=0pt},
o/.style={draw,circle,minimum size=6mm,outer sep=0pt},
ueast/.style 2 args={append after command={
\pgfextra
\begin{scope}
\clip ([yshift={#1+4mm}]\tikzlastnode.east) rectangle ++(1cm,-8mm);
\draw ([yshift=#1]\tikzlastnode.east)--++(0:6.5mm)
node[u,anchor=west] (\tikzlastnode-#2) {};
\end {scope}
\endpgfextra}},
ueast/.default={0mm}{ueast},
uwest/.style 2 args={append after command={
\pgfextra
\begin{scope}
\clip ([yshift={#1+4mm}]\tikzlastnode.west) rectangle ++(-1cm,-8mm);
\draw ([yshift=#1]\tikzlastnode.west)--++(180:6.5mm)
node[u,anchor=east] (\tikzlastnode-#2) {};
\end {scope}
\endpgfextra}},
uwest/.default={0mm}{uwest},
unorth/.style 2 args={append after command={
\pgfextra
\begin{scope}
\clip ([xshift={#1+4mm}]\tikzlastnode.north) rectangle ++(-8mm,1cm);
\draw ([xshift=#1]\tikzlastnode.north)--++(90:6.5mm)
node[u,anchor=south] (\tikzlastnode-#2) {};
\end {scope}
\endpgfextra}},
unorth/.default={0mm}{unorth},
usouth/.style 2 args={append after command={
\pgfextra
\begin{scope}
\clip ([xshift={#1+4mm}]\tikzlastnode.south) rectangle ++(-8mm,-1cm);
\draw ([xshift=#1]\tikzlastnode.south)--++(-90:6.5mm)
node[u,anchor=north] (\tikzlastnode-#2) {};
\end {scope}
\endpgfextra}},
usouth/.default={0mm}{usouth},
oeast/.style 2 args={append after command={
\pgfextra
\draw ([yshift=#1]\tikzlastnode.east)--++(0:6.5mm)
node[o,anchor=west] (\tikzlastnode-#2) {};
\endpgfextra}},
oeast/.default={0mm}{oeast},
owest/.style 2 args={append after command={
\pgfextra
\draw ([yshift=#1]\tikzlastnode.west)--++(180:6.5mm)
node[o,anchor=east] (\tikzlastnode-#2) {};
\endpgfextra}},
owest/.default={0mm}{owest},
onorth/.style 2 args={append after command={
\pgfextra
\draw ([xshift=#1]\tikzlastnode.north)--++(90:6.5mm)
node[o,anchor=south] (\tikzlastnode-#2) {};
\endpgfextra}},
onorth/.default={0mm}{onorth},
osouth/.style 2 args={append after command={
\pgfextra
\draw ([xshift=#1]\tikzlastnode.south)--++(-90:6.5mm)
node[o,anchor=north] (\tikzlastnode-#2) {};
\endpgfextra}},
osouth/.default={0mm}{osouth},
}

\begin{tikzpicture}
\node[component,ueast,onorth] (A) {A};
\node[component, right=2cm of A, owest,unorth] (B) {B};
\draw[|<->|,red] ([yshift=-3mm]A.north east)--++(0:2cm) node[above,midway]{\SI{2}{\centi\meter}};
\draw[|<->|,red] ([xshift=5mm]B.north)--++(90:{1cm}) node[right,midway]{\SI{1}{\centi\meter}};
\draw[|<->|,red] ([xshift=-5mm]A.north)--node[left]{\SI{1.25}{\centi\meter}} ++(90:{1.25cm});
\end{tikzpicture}

\begin{tikzpicture}
\node[component,ueast] (A) {A};
\node[component, right=5cm of A, owest] (B) {B};
\draw[line width=1mm, blue, opacity=.5] (A-ueast)--(B-owest);
\draw (A-ueast.west)--(B-owest);
\draw[red] (A-ueast.170)--(B-owest.130);
\draw[red] (A-ueast.-120)--(B-owest.south);
\end{tikzpicture}

\begin{tikzpicture}
\node[component,unorth] (A) {A};
\node[component, above right=3cm and -5mm of A, osouth] (C) {C};
\node[component, above left=3cm and -5mm of A, osouth] (B) {B};
\draw (A-unorth.-85)|-(C-osouth);
\draw (A-unorth.-95)|-(B-osouth);
\end{tikzpicture}

\begin{tikzpicture}
\node[component,onorth={-5mm}{-5},onorth={5mm}{5}] (A) {A};
\node[component, above=2cm of A,usouth={-5mm}{-5},usouth={5mm}{5}] (B) {B};
\end{tikzpicture}

\begin{tikzpicture}
\node[component,oeast={-4mm}{-4},ueast={4mm}{4}] (A) {A};
\node[component,right=4cm of A,uwest={-4mm}{-4},owest={4mm}{4}] (B) {B};
\draw (A--4)--(B--4.east);
\draw (A-4.west)--(B-4);
\end{tikzpicture}
\end{document}

• I like this solution. It is better than the arrow tips + decorated path I was trying to use. I didn't know that A.-85 notation. Is that an angle? So with this solution, if I want the U and circle to almost touch like in the first case, I have to separate them by exactly 2cm? Dec 11, 2014 at 1:07
• In order to draw several ports on the same side, I make a \path between the wanted corners and put empty nodes with style unorth that are positioned on the path with option /tikz/pos. Am I right? Dec 11, 2014 at 4:59
• Not a big deal, but it seems like the presence of a scope environment prevent the path the node belong to from being drawn. Maybe I missed something? Dec 11, 2014 at 5:03
• @Celelibi I completely rewrite the answer. I hope it solves your doubts now. If it's still not enough, please insist. Dec 11, 2014 at 10:59
• sorry I took so much time to reply. (I had plenty of stuff to do with my PhD manuscript.) Your answer is just awesome. If you had an idea to make it easy to have several port shapes (square + half square, triangle + half triangle), that would be perfect. Jan 26, 2015 at 22:02