%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Start of pgf-umlsd.sty % % Some macros for UML Sequence Diagrams. % Home page of project: http://pgf-umlsd.googlecode.com/ % Author: Xu Yuan , Southeast University, China % Contributor: Nobel Huang , Southeast University, China % % History: % v0.5 2009/09/30 Fix Issue 2 reported by vlado.handziski % - Nested callself is supported % - Rename sdloop and sdframe to sdblock % v0.4 2008/12/08 Fix Issue 1 reported by MathStuf: % Nested sdloop environment hides outer loop % v0.3 2008/11/10 in Berlin, fix for the PGF cvs version: % - the list items in \foreach are not evaluated by default now, % the `evaluate' opinion should be used % v0.2 2008/03/20 create project at http://pgf-umlsd.googlecode.com/ % - use `shadows' library % Thanks for Dr. Ludger Humbert's feedback! % - reduce the parameter numbers, the user can write the content % of instance (such as no colon) % - the user can redefine the `inststyle' % - new option: switch underlining of the instance text % - new option: switch rounded corners % v0.1 2008/01/25 first release at http://www.fauskes.net/pgftikzexamples/ % \NeedsTeXFormat{LaTeX2e}[1999/12/01] \ProvidesPackage{pgf-umlsd}[2008/03/20 v0.2 Some LaTeX macros for UML Sequence Diagrams.] % Options % ? the instance name under line ? \newif\ifpgfumlsdunderline\pgfumlsdunderlinetrue \DeclareOption{underline}{\pgfumlsdunderlinetrue} \DeclareOption{underline=true}{\pgfumlsdunderlinetrue} \DeclareOption{underline=false}{\pgfumlsdunderlinefalse} % ? the instance box with rounded corners ? \newif\ifpgfumlsdroundedcorners\pgfumlsdroundedcornersfalse \DeclareOption{roundedcorners}{\pgfumlsdroundedcornerstrue} \DeclareOption{roundedcorners=true}{\pgfumlsdroundedcornerstrue} \DeclareOption{roundedcorners=false}{\pgfumlsdroundedcornersfalse} \ProcessOptions % declare layers \pgfdeclarelayer{background} \pgfdeclarelayer{threadground} \pgfsetlayers{background,threadground,main} % new counters \newcounter{preinst} \newcounter{instnum} \newcounter{threadnum} \newcounter{seqlevel} % level \newcounter{callevel} \newcounter{callselflevel} \newcounter{blocklevel} % new an instance % Example: % \newinst[edge distance]{var}{name:class} \newcommand{\newinst}[3][0.2]{ \stepcounter{instnum} \path (inst\thepreinst.east)+(#1,0) node[inststyle] (inst\theinstnum) {\ifpgfumlsdunderline \underline{#3} \else #3 \fi}; \path (inst\theinstnum)+(0,-0.5*\unitfactor) node (#2) {}; \tikzstyle{instcolor#2}=[] \stepcounter{preinst} } % new an instance thread % Example: % \newinst[color]{var}{name}{class} \newcommand{\newthread}[3][gray!30]{ \newinst{#2}{#3} \stepcounter{threadnum} \node[below of=inst\theinstnum,node distance=0.8cm] (thread\thethreadnum) {}; \tikzstyle{threadcolor\thethreadnum}=[fill=#1] \tikzstyle{instcolor#2}=[fill=#1] } % draw running (thick) line, should not call directly \newcommand*{\drawthread}[2]{ \begin{pgfonlayer}{threadground} \draw[threadstyle] (#1.west) -- (#1.east) -- (#2.east) -- (#2.west) -- cycle; \end{pgfonlayer} } % a function call % Example: % \begin{call}[height]{caller}{function}{callee}{return} % \end{call} \newenvironment{call}[5][1]{ \stepcounter{seqlevel} \stepcounter{callevel} % push \path (#2)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (cf\thecallevel) {} (#4.\threadbias)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (ct\thecallevel) {}; \draw[->,>=triangle 60] ({cf\thecallevel}) -- (ct\thecallevel) node[midway, above] {#3}; \def\l\thecallevel{#1} \def\f\thecallevel{#2} \def\t\thecallevel{#4} \def\returnvalue{#5} \tikzstyle{threadstyle}+=[instcolor#2] } { \addtocounter{seqlevel}{\l\thecallevel} \path (\f\thecallevel)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (rf\thecallevel) {} (\t\thecallevel.\threadbias)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (rt\thecallevel) {}; \draw[dashed,->,>=angle 60] ({rt\thecallevel}) -- (rf\thecallevel) node[midway, above]{\returnvalue}; \drawthread{ct\thecallevel}{rt\thecallevel} \addtocounter{callevel}{-1} % pop } % a function do not need call others % Example: % \begin{callself}[height]{caller}{function}{return} % \end{callself} \newenvironment{callself}[4][1]{ \stepcounter{seqlevel} \stepcounter{callevel} % push \stepcounter{callselflevel} \path (#2)+(\thecallselflevel*0.1-0.1,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (sc\thecallevel) {} ({sc\thecallevel}.east)+(0,-0.33*\unitfactor) node (scb\thecallevel) {}; \draw[->,>=triangle 60] ({sc\thecallevel}.east) -- ++(0.8,0) node[near start, above right] {#3} -- ++(0,-0.33*\unitfactor) -- (scb\thecallevel); \def\l\thecallevel{#1} \def\f\thecallevel{#2} \def\returnvalue{#4} \tikzstyle{threadstyle}+=[instcolor#2] }{ \addtocounter{seqlevel}{\l\thecallevel} \path (\f\thecallevel)+(\thecallselflevel*0.1-0.1,-\theseqlevel*\unitfactor-0.33*\unitfactor) node (sct\thecallevel) {}; \draw[dashed,->,>=angle 60] ({sct\thecallevel}.east) node (sce\thecallevel) {} -- ++(0.8,0) -- node[midway, right]{\returnvalue} ++(0,-0.33*\unitfactor) -- ++(-0.8,0); \drawthread{scb\thecallevel}{sce\thecallevel} \addtocounter{callevel}{-1} % pop \addtocounter{callselflevel}{-1} } % message between threads % Example: % \mess{sender}{message content}{receiver} \newcommand{\mess}[3]{ \stepcounter{seqlevel} \path (#1)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (messbeg) {} (#3)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (messend) {}; \draw[->,>=angle 60] (messbeg) -- (messend) node[midway, above] {#2}; } \newenvironment{messcall}[4][1]{ \stepcounter{seqlevel} \stepcounter{callevel} % push \path (#2)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (cf\thecallevel) {} (#4.\threadbias)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (ct\thecallevel) {}; \draw[->,>=angle 60] ({cf\thecallevel}) -- (ct\thecallevel) node[midway, above] {#3}; \def\l\thecallevel{#1} \def\f\thecallevel{#2} \def\t\thecallevel{#4} \tikzstyle{threadstyle}+=[instcolor#2] } { \addtocounter{seqlevel}{\l\thecallevel} \path (\f\thecallevel)+(0,-\theseqlevel*\unitfactor-0.7*\unitfactor) node (rf\thecallevel) {} (\t\thecallevel.\threadbias)+(0,-\theseqlevel*\unitfactor-0.3*\unitfactor) node (rt\thecallevel) {}; \drawthread{ct\thecallevel}{rt\thecallevel} \addtocounter{callevel}{-1} % pop } % In the situation of multi-threads, some objects are called at the % same time. Currently, we have to adjust the bias of thread line % manually. Possible parameters are: center, west, east \newcommand{\setthreadbias}[1]{\global\def\threadbias{#1}} % In the situation of multi-threads, some events happen at the same % time. Currently, we have to adjust the level(time) of events % manually. This function makes the call earlier. \newcommand{\prelevel}{\addtocounter{seqlevel}{-1}} % a block box with caption % \begin{sdblock}[caption background color]{caption}{comments} % \end{sdblock} \newenvironment{sdblock}[3][white]{ \stepcounter{seqlevel} \stepcounter{blocklevel} % push \coordinate (blockbeg\theblocklevel) at (0,-\theseqlevel*\unitfactor-\unitfactor); \stepcounter{seqlevel} \def\blockcolor\theblocklevel{#1} \def\blockname\theblocklevel{#2} \def\blockcomm\theblocklevel{#3} \begin{pgfinterruptboundingbox} }{ \coordinate (blockend) at (0,-\theseqlevel*\unitfactor-2*\unitfactor); \path (current bounding box.east)+(0.2,0) node (boxeast) {} (current bounding box.west |- {blockbeg\theblocklevel}) + (-0.2,0) node (nw) {}; \path (boxeast |- blockend) node (se) {}; \draw (nw) rectangle (se); % title \node[blockstyle] (blocktitle) at (nw) {\blockname\theblocklevel}; \path (blocktitle.south east) + (0,0.2) node (set) {} (blocktitle.south east) + (-0.2,0) node (seb) {} (blocktitle.north east) + (0.2,0) node (comm) {}; \draw[fill=\blockcolor\theblocklevel] (blocktitle.north west) -- (blocktitle.north east) -- (set.center) -- (seb.center) -- (blocktitle.south west) -- cycle; \node[blockstyle] (blocktitle) at (nw) {\blockname\theblocklevel}; \node[blockcommentstyle] (blockcomment) at (comm) {\blockcomm\theblocklevel}; \end{pgfinterruptboundingbox} % add two points to keep the bounding box \node[dot] (fnw) at (nw) {}; \node[dot] (fse) at (se) {}; \addtocounter{blocklevel}{-1} % pop \stepcounter{seqlevel} } % the environment of sequence diagram \newenvironment{sequencediagram}{ \begin{tikzpicture} \setlength{\unitlength}{1cm} \tikzstyle{sequence}=[coordinate] \tikzstyle{inststyle}=[rectangle, draw, anchor=west, minimum height=0.8cm, minimum width=1.6cm, fill=white, drop shadow={opacity=1,fill=black}] \ifpgfumlsdroundedcorners \tikzstyle{inststyle}+=[rounded corners=3mm] \fi \tikzstyle{blockstyle}=[anchor=north west] \tikzstyle{blockcommentstyle}=[anchor=north west, font=\small] \tikzstyle{dot}=[inner sep=0pt,fill=black,circle,minimum size=0.2pt] \global\def\unitfactor{0.6} \global\def\threadbias{center} % reset counters \setcounter{preinst}{0} \setcounter{instnum}{0} \setcounter{threadnum}{0} \setcounter{seqlevel}{0} \setcounter{callevel}{0} \setcounter{callselflevel}{0} \setcounter{blocklevel}{0} % origin \node[coordinate] (inst0) {}; } { \begin{pgfonlayer}{background} \foreach \t [evaluate=\t] in {1,...,\theinstnum}{ \draw[dotted] (inst\t) -- ++(0,-\theseqlevel*\unitfactor-2.2*\unitfactor); } \foreach \t [evaluate=\t] in {1,...,\thethreadnum}{ \path (thread\t)+(0,-\theseqlevel*\unitfactor-0.1*\unitfactor) node (threadend) {}; \tikzstyle{threadstyle}+=[threadcolor\t] \drawthread{thread\t}{threadend} } \end{pgfonlayer} \end{tikzpicture}} %%% End of pgf-umlsd.sty %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%