%<–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––> % % tkz-orm.sty - Object-Role Modeling Diagrams in LaTeX % %<–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––> % This is file may be be distributed and/or modified % % 1. under the LaTeX Project Public License and/or % 2. under the GNU Public License. % % Copyright 2009-2016 by Jakob Voss, Camil Staps %<–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––> \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{tkz-orm}[2016/01/15 v0.1.4 Object-Role Modeling] %<–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––> \RequirePackage{tikz}[2009/09/04] %%%% Required pgf/TikZ libraries %\usetikzlibrary{calc} \usepgflibrary{shapes.multipart} \usetikzlibrary{shadows} \usetikzlibrary{positioning} \usetikzlibrary{calc} \usetikzlibrary{fit} \makeatletter %%%% Options %\DeclareOption*{% % \PackageWarning{tkz-orm}{Unknown option ‘\CurrentOption’}% %} \def\orm@error#1{\relax}% TODO %\def\orm@error#1{\PackageWarning{tkz-orm}{#1}}% %\pgfkeysdefargs{/tikz/ormcolorlet}{#1}{\colorlet{#2}{#1}} % \pgfkeys{/orm/.is family} % TODO \def\tkzorm{\textsf{tkz-orm}} %%%% ORM color constants \colorlet{constraintcolor}{magenta!100} \colorlet{ormconstraintcolor}{magenta!100} \colorlet{ormdeonticcolor}{blue!100} \colorlet{ormimpliedcolor}{gray!100} \def\orm@constraint@stroke{magenta} \def\orm@constraint@fill{magenta} \pgfkeysdefargs{/tikz/constraintcolor}{#1}{\colorlet{constraintcolor}{#1}} %%%% ORM width and length \newlength{\orm@linewidth} \setlength{\orm@linewidth}{0.25mm} %%%% ORM fonts \newcommand{\orm@font}{\sffamily\small} \newcommand{\orm@font@bold}{\bfseries\sffamily\small} \newcommand{\orm@font@tiny}{\sffamily\tiny} \newcommand{\orm@font@scriptsize}{\sffamily\scriptsize} \newcommand{\orm@font@bold@scriptsize}{\bfseries\sffamily\scriptsize} %%%% Text markup % textsubscript may not be defined like textsuperscript \newcommand{\orm@textsubscript}[1]{% {\m@th\ensuremath{_{\mbox{\fontsize\sf@size\z@#1}}}}} % similar to \blacktriangle (could better be implemented directly in pgf) \def\orm@blacktriangle#1{ \tikz \node[draw=none,inner sep=.2ex] { \tikz [#1]\path[fill=black] (0,0) -- (.6ex,1.3ex) -- (1.2ex,0) -- cycle;}; } \def\ormarrowup{\orm@blacktriangle{rotate=0}} \def\ormarrowdown{\orm@blacktriangle{rotate=180}} \def\ormarrowleft{\orm@blacktriangle{rotate=90}} \def\ormarrowright{\orm@blacktriangle{rotate=-90}} \def\ormtext{\orm@font} \def\ormbf{\orm@font@bold} \def\ormc{\orm@font\color{constraintcolor}} \def\ormsup#1{\textsuperscript{\orm@font@scriptsize #1}} \def\ormsub#1{\orm@textsubscript{\orm@font@scriptsize #1}} \def\ormind#1{\textsuperscript{\orm@font@bold@scriptsize\color{constraintcolor} #1}} \def\ormbraces#1{{\orm@font\textbraceleft#1\textbraceright}} \def\ormvalues#1{\ormc{\ormbraces{#1}}} \def\ormleft#1{{\ormarrowleft\ormtext #1}} \def\ormup#1{{\ormtext #1\ormarrowup}} % TODO: add '*' variant in constraint color for each command %%% Sizes \def\orm@role@linewidth{\orm@linewidth} % 0.25mm \def\orm@role@width{4mm} \def\orm@role@height{2.5mm} %%%% ORM styles \pgfkeys{/tikz/constraint stroke/.store in=\orm@constraint@stroke} \pgfkeys{/tikz/constraint fill/.store in=\orm@constraint@fill} \pgfkeys{/tikz/constraint color/.style={constraint stroke=#1,constraint fill=#1}} \tikzstyle{every orm line}= [solid,draw=black,line width=\orm@linewidth,-] \tikzstyle{orm}= [font=\orm@font,node distance=4mm, %label distance=1.5mm, %line width=\orm@linewidth, label distance=1.25mm ] \tikzstyle{orm-spacious}= [orm, every object/.style={shape=circle} ] \tikzstyle{every orm}= [every orm line,fill=white,orm] \tikzstyle{zoomed}= [line width=\orm@linewidth*1.5] \tikzstyle{every object}= [] \tikzstyle{every entity}= [] \tikzstyle{entity}= [every orm, shape=rectangle,rounded corners, align=center,minimum size=6mm, every object,every entity] \tikzstyle{every value}= [] \tikzstyle{value}= [every orm, shape=rectangle,rounded corners,densely dashed, align=center,minimum size=6mm, every object,every value] \tikzstyle{relationship}= [every orm line,every relationship] \tikzstyle{relation}= [relationship] \tikzstyle{plays}= [relationship] \tikzstyle{every relationship}= [] \tikzstyle{role index}= [font=\orm@font@tiny,inner sep=0,minimum width=\orm@role@width] \tikzstyle{every predicate}= [] %%% Shortcuts in tikzpicture \tikzaddtikzonlycommandshortcutdef{\entity}{\node[entity]} \tikzaddtikzonlycommandshortcutdef{\value}{\node[value]} \tikzaddtikzonlycommandshortcutdef{\unary}{\node[role]} \tikzaddtikzonlycommandshortcutdef{\role}{\node[role]} \tikzaddtikzonlycommandshortcutdef{\binary}{\node[roles]} \tikzaddtikzonlycommandshortcutdef{\roles}{\node[roles]} \tikzaddtikzonlycommandshortcutdef{\ternary}{\node[roles=3]} \tikzaddtikzonlycommandshortcutdef{\vunary}{\node[vrole]} \tikzaddtikzonlycommandshortcutdef{\vrole}{\node[vrole]} \tikzaddtikzonlycommandshortcutdef{\vbinary}{\node[vroles]} \tikzaddtikzonlycommandshortcutdef{\vroles}{\node[vroles]} \tikzaddtikzonlycommandshortcutdef{\vternary}{\node[vroles=3]} \tikzaddtikzonlycommandshortcutdef{\plays}{\draw[relationship]} % TODO: add error message if matrix package not loaded ? \tikzaddtikzonlycommandshortcutdef{\rules}{\matrix[row sep=0mm,nodes={right}]} \tikzaddtikzonlycommandshortcutdef{\limits}{\draw[limits]} \tikzaddtikzonlycommandshortcutdef{\limited}{\draw[limits]} \tikzaddtikzonlycommandshortcutdef{\limitsto}{\draw[limits to]} \tikzaddtikzonlycommandshortcutdef{\constraint}{\node[text=constraintcolor]} %%%% Textual rules \tikzset{rule/.style args={#1}{% append after command={% \bgroup% [current point is local=true] (\tikzlastnode.north west) node[font=\orm@font@bold@scriptsize, anchor=north east,inner sep=0,text=constraintcolor] {#1} \egroup% }, orm,inner sep=0.5mm, }} \tikzset{rule/.default={}} %%%% Predicates and roles \tikzstyle{all predicates}=[ every orm line, fill=white, rectangle split, inner sep=0 ] \tikzset{roles/.style args={#1}{ all predicates,rectangle split parts=#1, rectangle split allocate boxes=#1, rectangle split horizontal=true, rectangle split empty part height=2.5mm,rectangle split empty part width=2.25mm, % 4-1.5 = 2.5 % every label/.style={label distance=1.5mm} %label distance=1.5mm every predicate }} \tikzset{roles/.default={2}} \tikzset{vroles/.add style={}{roles=#1,rotate=90}} \tikzset{vroles/.default={2}} \tikzstyle{role}=[roles=1] \tikzstyle{vrole}=[vroles=1] \tikzstyle{role name}=[font=\orm@font,color=blue,inner sep=0.5mm] %% end of roles % TODO: read % http://www.latex-project.org/guides/clsguide.pdf \pgfkeyssetvalue{/tkzorm/constraintstroke}{constraintcolor} \pgfkeyssetvalue{/tkzorm/constraintfill}{constraintcolor} %%% % arrow heads % dot as arrow head (no color) \pgfarrowsdeclare{dot}{dot}{ \pgfutil@tempdima=0.2pt% \advance\pgfutil@tempdima by.2\pgflinewidth% \pgfutil@tempdimb=5.5\pgfutil@tempdima\advance\pgfutil@tempdimb by\pgflinewidth \pgfarrowsleftextend{+-\pgfutil@tempdimb} \pgfutil@tempdimb=1.5\pgfutil@tempdima\advance\pgfutil@tempdimb by.5\pgflinewidth \pgfarrowsrightextend{+\pgfutil@tempdimb} }{ \pgfutil@tempdima=0.2pt% \advance\pgfutil@tempdima by.2\pgflinewidth% \pgfsetdash{}{+0pt} \pgfpathcircle{\pgfqpoint{+3\pgfutil@tempdima}{0pt}}{+4.5\pgfutil@tempdima} \pgfsetstrokecolor{\orm@constraint@stroke} \pgfsetfillcolor{\orm@constraint@fill} %\pgfsetfillcolor{\pgfkeysvalueof{/tkzorm/constraintfill}} \pgfusepathqfillstroke } % mandatory constraint dot arrow head \pgfarrowsdeclare{mdot}{mdot}{ \pgfarrowsrightextend{0pt}% put arrow head over the tip \pgfutil@tempdima=0.25mm\advance\pgfutil@tempdima by 0.5\pgflinewidth% lw \pgfarrowsleftextend{+-\pgfutil@tempdima}% for arrow composing }{ \pgfutil@tempdima=0.5mm\advance\pgfutil@tempdima by \pgflinewidth% radius \pgfutil@tempdimb=0.25mm\advance\pgfutil@tempdimb by 0.5\pgflinewidth% lw \pgfpathcircle{\pgfqpoint{0.5\pgflinewidth}{0pt}}{\pgfutil@tempdima} \pgfsetfillcolor{ormconstraintcolor} \pgfusepathqfill } % implied mandatory constraint dot arrow head \pgfarrowsdeclare{idot}{idot}{ \pgfarrowsrightextend{0pt}% put arrow head over the tip \pgfutil@tempdima=0.25mm\advance\pgfutil@tempdima by 0.5\pgflinewidth% lw \pgfarrowsleftextend{+-\pgfutil@tempdima}% for arrow composing }{ \pgfutil@tempdima=0.5mm\advance\pgfutil@tempdima by \pgflinewidth% radius \pgfpathcircle{\pgfqpoint{0.5\pgflinewidth}{0pt}}{\pgfutil@tempdima} \pgfsetfillcolor{ormimpliedcolor} \pgfusepathqfill } % deontic mandatory constraint dot arrow head \pgfarrowsdeclare{odot}{odot}{ \pgfutil@tempdima=0.5mm\advance\pgfutil@tempdima by 1.0\pgflinewidth% radius \pgfarrowsrightextend{0.5\pgfutil@tempdima}% put arrow head over the tip \pgfarrowsleftextend{0pt}% for arrow composing }{ %\pgfutil@tempdima=0.5mm\advance\pgfutil@tempdima by 1.0\pgflinewidth% radius %\pgfutil@tempdimb=0.25mm\advance\pgfutil@tempdimb by 0.5\pgflinewidth% lw %\advance\pgfutil@tempdima by -0.5\pgfutil@tempdimb% -linewidth \pgfutil@tempdima=0.475mm\advance\pgfutil@tempdima by 0.75\pgflinewidth% r-lw \pgfutil@tempdimb=0.25mm\advance\pgfutil@tempdimb by 1\pgflinewidth% lw \pgfpathcircle{\pgfqpoint{\pgfutil@tempdimb}{0pt}}{\pgfutil@tempdima} \pgfutil@tempdima=0.25mm\advance\pgfutil@tempdima by 0.5\pgflinewidth% lw \pgfsetdash{}{+0pt} \pgfsetlinewidth{\pgfutil@tempdima} \pgfsetstrokecolor{ormdeonticcolor} \pgfsetfillcolor{white} \pgfusepathqfillstroke } % implied deontic mandatory constraint dot arrow head \pgfarrowsdeclare{iodot}{iodot}{ \pgfutil@tempdima=0.5mm\advance\pgfutil@tempdima by 1.0\pgflinewidth% radius \pgfarrowsrightextend{0.5\pgfutil@tempdima}% put arrow head over the tip \pgfarrowsleftextend{0pt}% for arrow composing }{ \pgfutil@tempdima=0.475mm\advance\pgfutil@tempdima by 0.75\pgflinewidth% r-lw \pgfutil@tempdimb=0.25mm\advance\pgfutil@tempdimb by 1\pgflinewidth% lw \pgfpathcircle{\pgfqpoint{\pgfutil@tempdimb}{0pt}}{\pgfutil@tempdima} \pgfutil@tempdima=0.25mm\advance\pgfutil@tempdima by 0.5\pgflinewidth% lw \pgfsetdash{}{+0pt} \pgfsetlinewidth{\pgfutil@tempdima} \pgfsetstrokecolor{ormimpliedcolor} \pgfsetfillcolor{white} \pgfusepathqfillstroke } % filled arrow for subtyping, subset constraints and value-comparision constraints \pgfarrowsdeclare{orm arrow}{orm arrow} { \pgfutil@tempdima=0.5pt% \advance\pgfutil@tempdima by.1\pgflinewidth% \pgfutil@tempdimb=8.705\pgfutil@tempdima\advance\pgfutil@tempdimb by.5\pgflinewidth% \pgfarrowsleftextend{+-\pgfutil@tempdimb} \pgfutil@tempdimb=.5\pgfutil@tempdima\advance\pgfutil@tempdimb by1.28\pgflinewidth% \pgfarrowsrightextend{+\pgfutil@tempdimb} }{ \pgfutil@tempdima=0.5pt% \advance\pgfutil@tempdima by.1\pgflinewidth% \pgfsetdash{}{+0pt} \pgfsetmiterjoin \pgfpathmoveto{\pgfpointadd{\pgfqpoint{0.5\pgfutil@tempdima}{0pt}}{\pgfqpointpolar{157}{10\pgfutil@tempdima}}} \pgfpathlineto{\pgfqpoint{0.5\pgfutil@tempdima}{0\pgfutil@tempdima}} \pgfpathlineto{\pgfpointadd{\pgfqpoint{0.5\pgfutil@tempdima}{0pt}}{\pgfqpointpolar{-157}{10\pgfutil@tempdima}}} \pgfpathclose \pgfsetfillcolor{ormconstraintcolor} % TODO: change if implied, deontic etc. %\pgfsetstrokefillcolor{ormconstraintcolor} % TODO: change if implied, deontic etc. \pgfusepathqfillstroke } % line styles that enable the mandatory constraint arrow tip \tikzstyle{required} =[relationship,mdot-] \tikzstyle{required by} =[relationship,-mdot] \tikzstyle{both required} =[relationship,mdot-mdot] % aliases \tikzstyle{mandatory} =[required] \tikzstyle{both mandatory}=[both required] \tikzstyle{optional} =[relationship] % draw a mandatory constraint arrow tip over a node \tikzset{constraint dot/.style={append after command={ (\tikzlastnode.center) edge[required] (\tikzlastnode.center) }}} \tikzstyle{cdot} =[constraint dot] %%%% Constraint symbols \tikzstyle{every constraint}= [] % TODO: every uniqueness bar \tikzstyle{every constraint line}= [every orm line,draw=constraintcolor,every constraint] %\tikzstyle{every constraint line}= [line width=\orm@linewidth,draw=constraintcolor,solid] \tikzstyle{every constraint circle}=[] \tikzstyle{constraint circle}= [every orm,every constraint line, shape=circle, font=\orm@font@scriptsize, minimum size=4mm,,inner sep=0pt, every constraint circle] \tikzstyle{constraint no circle}= [constraint circle,draw=none,fill=none] \tikzstyle{limits}= [every constraint line,dotted] \tikzstyle{limits to}= [every constraint line,dashed,-orm arrow] % internal uniqueness constraint % Implied and duplicated model parts: \tikzstyle{duplicated}= [drop shadow] \tikzstyle{duplicated model}= [every orm/.append style={drop shadow}, every predicate/.append style={drop shadow}] \tikzstyle{every implied line}= [constraintcolor=gray!80,thin] \tikzstyle{every implied}= [fill=gray!30,every implied line] \tikzstyle{implied}=[ every orm/.append style={every implied}, every orm line/.append style={every implied line},, constraint color=gray!100,thin, every relationship/.style={fill=gray!30}, required/.append style={idot-}, required by/.append style={-idot}, both required/.append style={idot-idot} ] \tikzstyle{implied model}=[implied] \tikzstyle{deontic}= [ every orm line/.append style={constraintcolor=blue!100}, constraintcolor=blue!100, constraint color=blue!100, required/.append style={odot-}, required by/.append style={-odot}, both required/.append style={odot-odot} ] %%%% Constraints % Define a constraints type % % #1 = name of the constraint % #2 = path code to be drawn above the constraint circle with |append after command| % \newcommand{\constraintdeclare}[2]{% \tikzset{constraint/#1/.style={constraint circle,append after command={#2}}}% } % Define a constraint alias % % #1 = alias name % #2 = name of the constraint % \newcommand{\constraintdeclarealias}[2]{% \tikzset{constraint/#1/.style={constraint=#2}}% } % Define a constraint by drawing another node at the some position as the current node \newcommand{\constraintdeclareasnode}[2]{ \constraintdeclare{#1}{ (\tikzlastnode.center) node[anchor=center] {\tikz{#2}} }} % style /tikz/constraint and its keys \tikzset{constraint/.is choice} %\tikzset{constraint/misc/.style={constraint circle,text=constraintcolor}}% %\constraintdeclarealias{le}{symmetric} %\tikzset{constraint/le/.append style={label={[text=constraintcolor,font=\orm@font@scriptsize]center:$<$}}} % TODO: clean up code \tikzset{constraint//.style={ constraint circle, append after command={ \bgroup% [current point is local=true] (\tikzlastnode) +(180:2mm) node[cdot]{} +(0:2mm) node[cdot]{} \egroup% }, label={[text=constraintcolor,font=\orm@font@scriptsize]center:$>$} }} % TODO: clean up code \tikzset{constraint/le/.style={ constraint circle, append after command={ \bgroup% [current point is local=true] (\tikzlastnode) +(180:2mm) node[cdot]{} +(0:2mm) node[cdot]{} \egroup% }, label={[text=constraintcolor,font=\orm@font@scriptsize]center:$\le$} }} %\tikzstyle{entity}= [every orm, % shape=rectangle,rounded corners, % align=center,minimum size=6mm, % every object,every entity] \tikzset{constraint/text/.style={color=constraintcolor,font=\orm@font,align=center}} \tikzset{constraint/.default=text} % TODO: color=constraintcolor %%%% Ring constraints % TODO: possibly join with cdot \tikzstyle{ormdot}= [fill=constraintcolor,radius=.6mm,draw=none] \constraintdeclareasnode{acyclic}{ \path [ormdot] (90:2mm) circle; \path [ormdot] (210:2mm) circle; \path [ormdot] (330:2mm) circle; \draw [every constraint line] (270:1mm) edge (270:3mm); } %\constraintdeclarealias{DAG}{acyclic} % directed-acyclic-graph (DAG) \constraintdeclareasnode{intransitive}{ \fill[every constraint line,fill=white,draw=white] circle (2.2mm); \path [ormdot] (90:2mm) circle; \path [ormdot] (210:2mm) circle; \path [ormdot] (330:2mm) circle; \path[every constraint line,rounded corners=0] (90:2mm) -- (210:2mm) -- (330:2mm) -- (90:2mm); \draw[every constraint line] (270:0mm) to (270:1.75mm); } % TODO: document \constraintdeclareasnode{strongly intransitive}{ \fill[every constraint line,fill=white,draw=white] circle (2.2mm); \path [ormdot] (90:2mm) circle; \path [ormdot] (30:1mm) circle; \path [ormdot] (210:2mm) circle; \path [ormdot] (330:2mm) circle; \path[every constraint line,rounded corners=0] (90:2mm) -- (210:2mm) -- (330:2mm) -- (90:2mm); \draw[every constraint line] (270:0mm) to (270:1.75mm); } % TODO: document tree and otree \constraintdeclareasnode{tree}{ \fill[every constraint line,fill=white,draw=white] circle (2.2mm); \path [ormdot] (90:2mm) circle; \path [ormdot] (210:2mm) circle; \path [ormdot] (330:2mm) circle; \path[every constraint line,rounded corners=0] (339:2mm) -- (90:2mm) -- (210:2mm); } \constraintdeclareasnode{otree}{ \fill[every constraint line,fill=white,draw=white] circle (2.2mm); \path [ormdot] (90:2mm) circle; \path [ormdot] (210:2mm) circle; \path [ormdot] (330:2mm) circle; \path[every constraint line,rounded corners=0] (339:2mm) -- (90:2mm) -- (210:2mm); \path[every constraint line,rounded corners=0] (.8mm,-0mm) -- (-.6mm,-1mm) -- (.8mm,-2mm); } \constraintdeclareasnode{purely reflexive}{ \path[use as bounding box] (270:2.75mm) -- (90:2.75mm); \path [ormdot] (180:2mm) circle; \draw[every constraint line] (90:1.25mm) to (270:1.25mm); % (-.8mm, .4mm) -- (.8mm, .4mm) % (-.8mm,-.4mm) -- (.8mm,-.4mm); } \constraintdeclareasnode{reflexive}{ \path[use as bounding box] (270:2.75mm) -- (90:2.75mm); \path [ormdot] (180:2mm) circle; } \constraintdeclareasnode{irreflexive}{ \path[use as bounding box] (270:2.75mm) -- (90:2.75mm); \path [ormdot] (180:2mm) circle; % \draw [every constraint line] (90:1.25mm) -- (270:1.25mm); \draw [every constraint line] (270:1mm) to (270:2.75mm); } \constraintdeclareasnode{symmetric irreflexive}{ \path [ormdot] (180:2mm) circle; \path [ormdot] ( 0:2mm) circle; \draw [every constraint line] (-.8mm, .4mm) -- (.8mm, .4mm) (-.8mm,-.4mm) -- (.8mm,-.4mm) (90:1.25mm) to (270:1.25mm); } \constraintdeclareasnode{transitive}{ \draw[every constraint line,fill=white,draw=white] circle (2.2mm); \path [ormdot] (90:2mm) circle; \path [ormdot] (210:2mm) circle; \path [ormdot] (330:2mm) circle; \draw[every constraint line] (90:2mm) -- (210:2mm) -- (330:2mm) -- (90:2mm); } \constraintdeclareasnode{acyclic intransitive}{ \path [ormdot] (90:2mm) circle; \path [ormdot] (210:2mm) circle; \path [ormdot] (330:2mm) circle; \path[every constraint line] (90:2mm) -- (210:2mm) -- (330:2mm) -- (90:2mm); \draw[every constraint line] (270:0mm) to (270:2.75mm); } \constraintdeclareasnode{symmetric}{ \path [ormdot] (180:2mm) circle; \path [ormdot] ( 0:2mm) circle; } \constraintdeclareasnode{asymmetric}{ \path[use as bounding box] (270:2.75mm) -- (90:2.75mm); \path [ormdot] (+2mm,0mm) circle; \path [ormdot] (-2mm,0mm) circle; \draw [every constraint line] (270:1mm) to (270:2.75mm); } \constraintdeclareasnode{antisymmetric}{ \path [ormdot] (180:2mm) circle; \path [ormdot] ( 0:2mm) circle; \draw[every constraint line] (90:2.75mm) to (270:2.75mm); } \constraintdeclareasnode{tree}{ \draw [every constraint line,fill=white,draw=white] circle (2.2mm); \path [ormdot] (90:2mm) circle; \path [ormdot] (210:2mm) circle; \path [ormdot] (330:2mm) circle; \draw [every constraint line] (210:2mm) -- (90:2mm) -- (330:2mm); } % TODO: strictly order, total, connected, etc. % TODO: test and document this \tikzset{objectification/.style={ entity,fill=none, append after command={ \bgroup% [current point is local=true] node[orm,above=0 of \tikzlastnode] {``#1''} \egroup% } }} %%%% Subtypes % TODO: how to get current line width? \tikzstyle{inheritance}=[line width=\orm@linewidth*1.5,>=orm arrow, draw,color=constraintcolor] \tikzstyle{subtype}=[inheritance,<-,solid] \tikzstyle{suptype}=[inheritance,->,solid] \tikzstyle{subinterface}=[subtype,dashed] \tikzstyle{supinterface}=[suptype,dashed] %%%% Role indices \tikzset{index/.code={\orm@parse@index#1:\pgf@nil}} \tikzset{index/.default=1} \def\orm@parse@index#1:#2\pgf@nil{% \def\orm@temp{#2}% \ifx\orm@temp\pgfutil@empty% \orm@@parse@index1:#1:\pgf@nil% \else% \orm@@parse@index#1:#2\pgf@nil% \fi% } % \orm@verticalfalse % \orm@verticaltrue % \tikz@do@alignfalse \def\orm@@parse@index#1:#2:\pgf@nil{% \tikzset{append after command={% \bgroup% [current point is local=true] let \n1 = {#1*\orm@role@width} in (\tikzlastnode.west) to[draw=none] node[role index,pos=1,sloped,anchor=east] {#2} ($(\tikzlastnode.west)!\n1!(\tikzlastnode.east)$) \egroup% } }} \tikzset{index/.default=1} %%%% Uniqueness constraint bars \def\orm@rolebar@length#1{#1*\orm@role@width-2*\orm@role@linewidth} \def\orm@rolebar@shift{0.75mm} % = 3*0.25mm \def\orm@rolebar@xshifta#1{\orm@role@linewidth+#1*\orm@role@width-\orm@role@width} \def\orm@rolebar@xshiftb#1{#1*\orm@role@width-\orm@role@linewidth} \def\orm@rolebar@yshift#1{% \ifnum#1<0-\fi0.5*\orm@role@height+#1*\orm@rolebar@shift% } \tikzstyle{uniqueness bar}=[every constraint line,-] \tikzstyle{skipped uniqueness bar}=[ every constraint line,-,dotted,preaction={every constraint line,-,white} ] \def\orm@unique@bar@normal{uniqueness bar} \def\orm@unique@bar@skipped{skipped uniqueness bar} \global\let\orm@unique@bar@style=\orm@unique@bar@normal %% % Styles to draw external constraints % \pgfkeys{ /orm/external constraint/.style={ every constraint line, fill=white,text=constraintcolor,align=center, font=\orm@font@scriptsize,minimum size=4mm,inner sep=0pt } } % Defines an external constraints as node shape, based on the circle shape % The symbol is *always* drawn but circle and fill can be modified. % You can still modify the layout via color, line width, stroke opacity. % The shapes do not respect outer sep (outer xsep/outer ysep) yet (TODO). \newcommand{\constraintdeclareshape}[2]{% \expandafter\pgfdeclareshape{#1}{ \inheritsavedanchors[from=circle] \inheritanchor[from=circle]{north} \inheritanchor[from=circle]{north west} \inheritanchor[from=circle]{north east} \inheritanchor[from=circle]{center} \inheritanchor[from=circle]{west} \inheritanchor[from=circle]{east} \inheritanchor[from=circle]{mid} \inheritanchor[from=circle]{mid west} \inheritanchor[from=circle]{mid east} \inheritanchor[from=circle]{base} \inheritanchor[from=circle]{base west} \inheritanchor[from=circle]{base east} \inheritanchor[from=circle]{south} \inheritanchor[from=circle]{south west} \inheritanchor[from=circle]{south east} \inheritanchorborder[from={circle}] \backgroundpath{% \behindbackgroundpath and \beforebackgroundpath don't do. \pgfsetdash{}{0pt}% solid circle \pgfutil@tempdima=\radius% \pgfmathsetlength{\pgf@xb}{\pgfkeysvalueof{/pgf/outer xsep}}% \pgfmathsetlength{\pgf@yb}{\pgfkeysvalueof{/pgf/outer ysep}}% \ifdim\pgf@xb<\pgf@yb% \advance\pgfutil@tempdima by-\pgf@yb% \else% \advance\pgfutil@tempdima by-\pgf@xb% \fi% \pgfpathcircle{\centerpoint}{\pgfutil@tempdima}% } \foregroundpath{% \pgfsetdash{}{0pt}% \pgfsetbuttcap% \pgfsetmiterjoin% #2 \pgfusepathqstroke% } } \tikzset{constraint/#1/.style={/orm/external constraint,shape=#1}} \tikzset{#1 constraint/.style={/orm/external constraint,shape=#1}} } \constraintdeclareshape{unique}{ \pgfpathmoveto{\centerpoint\advance\pgf@x by-\radius}% \pgfpathlineto{\centerpoint\advance\pgf@x by\radius}% } \constraintdeclareshape{preferred unique}{ \centerpoint \pgf@xc=\pgf@x \pgf@yc=\pgf@y% \pgfutil@tempdima=\pgflinewidth% \pgfutil@tempdima1.5\pgfutil@tempdima% \pgfpathmoveto{\centerpoint\advance\pgf@x by-\radius\advance\pgf@y by-\pgfutil@tempdima}% \pgfpathlineto{\centerpoint\advance\pgf@x by\radius\advance\pgf@y by-\pgfutil@tempdima}% \pgfpathmoveto{\centerpoint\advance\pgf@x by-\radius\advance\pgf@y by\pgfutil@tempdima}% \pgfpathlineto{\centerpoint\advance\pgf@x by\radius\advance\pgf@y by\pgfutil@tempdima}% } \constraintdeclareshape{equal}{ \pgfutil@tempdima=\radius% x \pgfutil@tempdima0.4\pgfutil@tempdima% \pgfutil@tempdimb=\radius% y \pgfutil@tempdimb0.2\pgfutil@tempdimb% \pgfpathmoveto{\centerpoint\advance\pgf@x by-\pgfutil@tempdima\advance\pgf@y by\pgfutil@tempdimb}% \pgfpathlineto{\centerpoint\advance\pgf@x by\pgfutil@tempdima\advance\pgf@y by\pgfutil@tempdimb}% \pgfpathmoveto{\centerpoint\advance\pgf@x by-\pgfutil@tempdima\advance\pgf@y by-\pgfutil@tempdimb}% \pgfpathlineto{\centerpoint\advance\pgf@x by\pgfutil@tempdima\advance\pgf@y by-\pgfutil@tempdimb}% } \constraintdeclareshape{subset}{ \pgf@xa=\radius \pgf@xa0.4\pgf@xa% xa = 0.5 r \pgf@ya=\radius \pgf@ya0.4\pgf@ya% ya = 0.4 r \pgf@yb0.5\pgf@ya% yb = 0.5 ya = 0.2 r \pgf@xb=\pgf@xa \advance\pgf@xb by -\pgf@yb% \pgfpathmoveto{\centerpoint\advance\pgf@x by-\pgf@xa\advance\pgf@y by-\pgf@ya}% \pgfpathlineto{\centerpoint\advance\pgf@x by \pgf@xa\advance\pgf@y by-\pgf@ya}% \pgfpathmoveto{\centerpoint\advance\pgf@x by \pgf@xa}% \pgfpathlineto{\centerpoint\advance\pgf@x by-\pgf@xb}% \pgfpathmoveto{\centerpoint\advance\pgf@x by \pgf@xa\advance\pgf@y by\pgf@ya}% \pgfpathlineto{\centerpoint\advance\pgf@x by-\pgf@xb\advance\pgf@y by\pgf@ya}% \pgfpatharc{90}{270}{\pgf@yb} } \constraintdeclareshape{supset}{ \pgf@xa=\radius \pgf@xa0.4\pgf@xa% xa = 0.5 r \pgf@ya=\radius \pgf@ya0.4\pgf@ya% ya = 0.4 r \pgf@yb0.5\pgf@ya% yb = 0.5 ya = 0.2 r \pgf@xb=\pgf@xa \advance\pgf@xb by -\pgf@yb% \pgfpathmoveto{\centerpoint\advance\pgf@x by-\pgf@xa\advance\pgf@y by-\pgf@ya}% \pgfpathlineto{\centerpoint\advance\pgf@x by \pgf@xa\advance\pgf@y by-\pgf@ya}% \pgfpathmoveto{\centerpoint\advance\pgf@x by-\pgf@xa}% \pgfpathlineto{\centerpoint\advance\pgf@x by \pgf@xb}% \pgfpathmoveto{\centerpoint\advance\pgf@x by-\pgf@xa\advance\pgf@y by\pgf@ya}% \pgfpathlineto{\centerpoint\advance\pgf@x by \pgf@xb\advance\pgf@y by\pgf@ya}% \pgfpatharc{90}{-90}{\pgf@yb} } \constraintdeclareshape{exclusive}{% exclusive constraint ('x') \pgfpathmoveto{\pgfpointadd{\centerpoint}{\pgfqpointpolar{45}{\radius}}}% \pgfpathlineto{\pgfpointadd{\centerpoint}{\pgfqpointpolar{225}{\radius}}}% \pgfpathmoveto{\pgfpointadd{\centerpoint}{\pgfqpointpolar{135}{\radius}}}% \pgfpathlineto{\pgfpointadd{\centerpoint}{\pgfqpointpolar{-45}{\radius}}}% } % inclusive-or (disjunctive mandatory role) constraint \constraintdeclareshape{mandatory}{ \pgf@xa=\radius \pgf@xa0.4\pgf@xa% \pgfpathcircle{\centerpoint}{\pgf@xa} \pgfsetfillcolor{constraintcolor} % TODO: constraitfillcolor / not for deontic % use deontic dot instead? \pgfusepathqfillstroke } % exclusive-or constraint ('xor' or 'partition') \constraintdeclareshape{partition}{ \pgf@xa=\radius \pgf@xa0.4\pgf@xa% \pgfpathcircle{\centerpoint}{\pgf@xa} \pgfsetfillcolor{constraintcolor} % TODO: constraitfillcolor / not for deontic \pgfusepathqfillstroke \pgfpathmoveto{\pgfpointadd{\centerpoint}{\pgfqpointpolar{45}{\radius}}}% \pgfpathlineto{\pgfpointadd{\centerpoint}{\pgfqpointpolar{225}{\radius}}}% \pgfpathmoveto{\pgfpointadd{\centerpoint}{\pgfqpointpolar{135}{\radius}}}% \pgfpathlineto{\pgfpointadd{\centerpoint}{\pgfqpointpolar{-45}{\radius}}}% } % Collection constraints (not standard ORM2) \constraintdeclareshape{rank}{% aka ordered set \pgf@xa=\radius \pgf@xa0.3\pgf@xa% \pgf@ya=\radius \pgf@ya0.4\pgf@ya% \pgfpathmoveto{\centerpoint\advance\pgf@x by\pgf@xa\advance\pgf@y by\pgf@ya}% \pgfpathlineto{\centerpoint\advance\pgf@x by-\pgf@ya}% \pgfpathlineto{\centerpoint\advance\pgf@x by\pgf@xa\advance\pgf@y by-\pgf@ya}% } \constraintdeclareshape{sequence}{% \pgf@xa=\radius \pgf@xa0.3\pgf@xa% \pgf@ya=\radius \pgf@ya0.4\pgf@ya% \pgf@xc=\radius \pgf@xc0.2\pgf@xc% \pgfpathmoveto{\centerpoint\advance\pgf@x by\pgf@xa\advance\pgf@y by\pgf@ya}% \pgfpathlineto{\centerpoint\advance\pgf@x by-\pgf@ya}% \pgfpathlineto{\centerpoint\advance\pgf@x by\pgf@xa\advance\pgf@y by-\pgf@ya}% \pgfusepathqstroke \pgfsetdash{{\pgflinewidth}{1pt}}{0pt}% dotted \pgfpathmoveto{\centerpoint\advance\pgf@x by-\radius}% \pgfpathlineto{\centerpoint\advance\pgf@x by\radius}% } \constraintdeclareshape{weak sequence}{% \pgf@xa=\radius \pgf@xa0.3\pgf@xa% \pgf@xb=\pgflinewidth% \pgf@yb=\radius \pgf@yb0.15\pgf@yb% \pgf@ya=\radius \pgf@ya0.4\pgf@ya% \pgfpathmoveto{\centerpoint\advance\pgf@x by\pgf@xa\advance\pgf@y by\pgf@ya\advance\pgf@y by\pgf@yb}% \pgfpathlineto{\centerpoint\advance\pgf@x by-\pgf@xa\advance\pgf@y by\pgf@yb}% \pgfpathlineto{\centerpoint\advance\pgf@x by\pgf@xa\advance\pgf@y by-\pgf@ya\advance\pgf@y by\pgf@yb}% \pgfpathmoveto{\centerpoint\advance\pgf@x by-\pgf@xa\advance\pgf@x by-\pgf@xb\advance\pgf@y by-\pgf@yb}% \pgfpathlineto{\centerpoint\advance\pgf@x by\pgf@xa\advance\pgf@x by-\pgf@xb\advance\pgf@y by-\pgf@ya\advance\pgf@y by-\pgf@yb}% \pgfusepathqstroke \pgfsetdash{{\pgflinewidth}{1pt}}{0pt}% dotted \pgfpathmoveto{\centerpoint\advance\pgf@x by-\radius\advance\pgf@y by\pgf@yb}% \pgfpathlineto{\centerpoint\advance\pgf@x by\radius\advance\pgf@y by\pgf@yb}% } \constraintdeclareshape{weak rank}{% aka poset \pgf@xa=\radius \pgf@xa0.3\pgf@xa% \pgf@xb=\pgflinewidth% \pgf@yb=\radius \pgf@yb0.15\pgf@yb% \pgf@ya=\radius \pgf@ya0.4\pgf@ya% \pgfpathmoveto{\centerpoint\advance\pgf@x by\pgf@xa\advance\pgf@y by\pgf@ya\advance\pgf@y by\pgf@yb}% \pgfpathlineto{\centerpoint\advance\pgf@x by-\pgf@xa\advance\pgf@y by\pgf@yb}% \pgfpathlineto{\centerpoint\advance\pgf@x by\pgf@xa\advance\pgf@y by-\pgf@ya\advance\pgf@y by\pgf@yb}% \pgfpathmoveto{\centerpoint\advance\pgf@x by-\pgf@xa\advance\pgf@x by-\pgf@xb\advance\pgf@y by-\pgf@yb}% \pgfpathlineto{\centerpoint\advance\pgf@x by\pgf@xa\advance\pgf@x by-\pgf@xb\advance\pgf@y by-\pgf@ya\advance\pgf@y by-\pgf@yb}% } \constraintdeclareshape{counted}{ \pgf@xa=\radius \pgf@xa0.5\pgf@xa% \pgf@xb=\radius \pgf@xb0.25\pgf@xb% \pgfpathmoveto{\centerpoint\advance\pgf@x by-\pgf@xa\advance\pgf@y by\pgf@xb} \pgfpathlineto{\centerpoint\advance\pgf@x by\pgf@xa\advance\pgf@y by\pgf@xb} \pgfpathmoveto{\centerpoint\advance\pgf@x by-\pgf@xa\advance\pgf@y by-\pgf@xb} \pgfpathlineto{\centerpoint\advance\pgf@x by\pgf@xa\advance\pgf@y by-\pgf@xb} \pgfpathmoveto{\centerpoint\advance\pgf@x by\pgf@xb\advance\pgf@y by\pgf@xa} \pgfpathlineto{\centerpoint\advance\pgf@x by\pgf@xb\advance\pgf@y by-\pgf@xa} \pgfpathmoveto{\centerpoint\advance\pgf@x by-\pgf@xb\advance\pgf@y by\pgf@xa} \pgfpathlineto{\centerpoint\advance\pgf@x by-\pgf@xb\advance\pgf@y by-\pgf@xa} } %\constraintdeclarealias{in bag}{counted} %\constraintdeclarealias{in multiset}{counted} \constraintdeclareshape{external}{} % needed because shapes are in /orm/... \constraintdeclareshape{misc}{} % alias for 'external' %\constraintdeclare{custom}{} % empty constraint circle \constraintdeclarealias{required}{mandatory} \constraintdeclarealias{total}{mandatory} \constraintdeclarealias{or}{mandatory} \constraintdeclarealias{x}{exclusive} \constraintdeclarealias{xor}{partition} %% % Styles to draw uniqueness bars % \pgfkeys{/orm/.cd, uniqueness bar from/.initial=1,% uniqueness bar to/.initial=1,% uniqueness bar level/.initial=1,% uniqueness bar roles/.initial=1,% uniqueness bar set roles/.style={% /orm/uniqueness bar roles=#1, /pgf/minimum width=#1*4mm }, uniqueness bar lower/.initial=0,% uniqueness bar from/.value required,% uniqueness bar to/.value required,% uniqueness bar level/.value required,% uniqueness bar roles/.value required,% uniqueness bar set roles/.value required,% uniqueness bar lower/.value required,% uniqueness bar style/.style={} } % % This node shape draws a uniqueness bar above or below a predicate node. % You should use is to shape an invisible node of same size and position % as the predicate to constraint. The bar is affected by several styles % named '/orm/uniqueness bar ...' each. % \pgfdeclareshape{uniqueness bar}{ \inheritsavedanchors[from=rectangle] \inheritanchorborder[from=rectangle] \inheritanchor[from=rectangle]{north} \inheritanchor[from=rectangle]{west} \inheritanchor[from=rectangle]{east} \inheritanchor[from=rectangle]{south} \inheritanchor[from=rectangle]{center} \savedmacro\barlevel{% \edef\barlevel{\pgfkeysvalueof{/orm/uniqueness bar level}}% \edef\barfrom{\pgfkeysvalueof{/orm/uniqueness bar from}}% \edef\barto{\pgfkeysvalueof{/orm/uniqueness bar to}}% \edef\barroles{\pgfkeysvalueof{/orm/uniqueness bar roles}}% \edef\barlower{\pgfkeysvalueof{/orm/uniqueness bar lower}}% } \foregroundpath{ \southwest \pgf@xa=\pgf@x \pgf@ya=\pgf@y \northeast \pgf@xb=\pgf@x \pgf@yb=\pgf@y \pgfmathsetlength\pgfutil@tempdima{ (\pgf@xb-\pgf@xa-\pgflinewidth) / (\barroles>0 ? \barroles : 1) }% role width \pgf@xb=\pgf@xa \advance\pgf@xa by 1.5\pgflinewidth% \advance\pgf@xa by -1\pgfutil@tempdima% \advance\pgf@xa by \barfrom\pgfutil@tempdima% \advance\pgf@xb by-0.5\pgflinewidth% \advance\pgf@xb by \barto\pgfutil@tempdima% \pgfmathsetlength\pgfutil@tempdima{\barlevel}% \pgfutil@tempdimb=3\pgflinewidth% level distance \ifdim\pgfutil@tempdima<0pt% \advance\pgf@ya by +0.5\pgflinewidth% \advance\pgf@ya by \barlower\pgfutil@tempdimb% \advance\pgf@ya by \barlevel\pgfutil@tempdimb% \pgf@yb=\pgf@ya% \else% \advance\pgf@yb by-0.5\pgflinewidth% \advance\pgf@yb by -\barlower\pgfutil@tempdimb% \advance\pgf@yb by \barlevel\pgfutil@tempdimb% \fi% \pgfpathmoveto{\pgfqpoint{\pgf@xa}{\pgf@yb}}% \pgfpathlineto{\pgfqpoint{\pgf@xb}{\pgf@yb}}% } } % Power types \tikzset{power/.style args={#1}{ append after command={ \pgfextra{ \node[entity,shape=rectangle,rounded corners,fit={(\tikzlastnode)},label={[font=\orm@font]#1}] at (\tikzlastnode) (\tikzlastnode-power) {}; } } } } % Sequence types \tikzset{sequence/.style args={#1}{ append after command={ \pgfextra{ \node[entity,shape=rectangle,rounded corners=0pt,fit={(\tikzlastnode)},label={[font=\orm@font]#1}] at (\tikzlastnode) (\tikzlastnode-sequence) {}; } } } } \tikzset{ unique/.code={\orm@parse@xunique#1:\pgf@nil}, unique/.default=1, } % parses ':' and '' \def\orm@parse@xunique#1:#2\pgf@nil{% \def\orm@temp{#2}% \ifx\orm@temp\pgfutil@empty% \orm@@parse@xunique#1:1:\pgf@nil% \else% \orm@@parse@xunique#1:#2\pgf@nil% \fi% } % parses n-m: and n: \def\orm@@parse@xunique#1:#2:\pgf@nil{% \pgfutil@in@{-}{#1}% \ifpgfutil@in@% \orm@parse@xunique@@bar#1:#2\pgf@nil% \else% \orm@parse@xunique@@bar#1-#1:#2\pgf@nil% \fi% } \def\orm@parse@xunique@@bar#1-#2:#3\pgf@nil{% \def\orm@tempA{#1}% \ifx\orm@tempA\pgfutil@empty% \relax% \orm@parse@xunique@@bar#2-#2:-1\pgf@nil \else% \tikzset{append after command={ \bgroup% [current point is local=true] let \p1 = (\tikzlastnode.west), \p2 = (\tikzlastnode.east) in \pgfextra{\pgfmathparse{(\x2-\x1)/4mm}} (\tikzlastnode) node[ /orm/uniqueness bar from/.expand once=#1, /orm/uniqueness bar to/.expand once=#2, /orm/uniqueness bar set roles/.expand once=\pgfmathresult, /orm/uniqueness bar level=#3, shape=uniqueness bar, \orm@unique@bar@style, minimum height=2.5mm, /orm/uniqueness bar style ]{} \egroup }}% \fi% } %% Skipped uniqueness bar % TODO: remove this duplicated code. \tikzset{ skip unique/.code={\orm@parse@sunique#1:\pgf@nil}, skip unique/.default=1, } % parses ':' and '' \def\orm@parse@sunique#1:#2\pgf@nil{% \def\orm@temp{#2}% \ifx\orm@temp\pgfutil@empty% \orm@@parse@sunique#1:1:\pgf@nil% \else% \orm@@parse@sunique#1:#2\pgf@nil% \fi% } % parses n-m: and n: \def\orm@@parse@sunique#1:#2:\pgf@nil{% \pgfutil@in@{-}{#1}% \ifpgfutil@in@% \orm@parse@sunique@@bar#1:#2\pgf@nil% \else% \orm@parse@sunique@@bar#1-#1:#2\pgf@nil% \fi% } \def\orm@parse@sunique@@bar#1-#2:#3\pgf@nil{% \def\orm@tempA{#1}% \ifx\orm@tempA\pgfutil@empty% \relax% \orm@parse@xunique@@bar#2-#2:-1\pgf@nil \else% \tikzset{append after command={ \bgroup% [current point is local=true] let \p1 = (\tikzlastnode.west), \p2 = (\tikzlastnode.east) in \pgfextra{\pgfmathparse{(\x2-\x1)/4mm}} \pgfextra{\let\orm@unique@bar@style=\orm@unique@bar@skipped}% (\tikzlastnode) node[ /orm/uniqueness bar from/.expand once=#1, /orm/uniqueness bar to/.expand once=#2, /orm/uniqueness bar set roles/.expand once=\pgfmathresult, /orm/uniqueness bar level=#3, shape=uniqueness bar, \orm@unique@bar@style, minimum height=2.5mm ]{} \egroup }}% \fi% } % TODO: add optional parameters and document this \tikzstyle{preferred unique}=[unique=1:1,unique=1:2] \makeatother %<–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––> \endinput