%<–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––>
%
% 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/ge/.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:$\ge$}
}}
% 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%
\
[email protected]\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
\
[email protected]\pgfutil@tempdima%
\pgfutil@tempdimb=\radius% y
\
[email protected]\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 \
[email protected]\pgf@xa% xa = 0.5 r
\pgf@ya=\radius \
[email protected]\pgf@ya% ya = 0.4 r
\
[email protected]\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 \
[email protected]\pgf@xa% xa = 0.5 r
\pgf@ya=\radius \
[email protected]\pgf@ya% ya = 0.4 r
\
[email protected]\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 \
[email protected]\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 \
[email protected]\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 \
[email protected]\pgf@xa%
\pgf@ya=\radius \
[email protected]\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 \
[email protected]\pgf@xa%
\pgf@ya=\radius \
[email protected]\pgf@ya%
\pgf@xc=\radius \
[email protected]\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 \
[email protected]\pgf@xa%
\pgf@xb=\pgflinewidth%
\pgf@yb=\radius \
[email protected]\pgf@yb%
\pgf@ya=\radius \
[email protected]\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 \
[email protected]\pgf@xa%
\pgf@xb=\pgflinewidth%
\pgf@yb=\radius \
[email protected]\pgf@yb%
\pgf@ya=\radius \
[email protected]\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 \
[email protected]\pgf@xa%
\pgf@xb=\radius \
[email protected]\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 '<which>:<level>' and '<which>'
\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:<level> and n:<level>
\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 '<which>:<level>' and '<which>'
\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:<level> and n:<level>
\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