%<–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––> % % 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 by Jakob Voss %<–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––> \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{tkz-orm}[2010/01/20 v0.1 Object-Role Modeling] %<–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––> \RequirePackage{tikz}[2009/09/04] %%%% Required pgf/TikZ libraries %\usetikzlibrary{calc} \usepgflibrary{shapes.multipart} \usetikzlibrary{shadows} \usetikzlibrary{positioning} \usetikzlibrary{calc} \makeatletter %%%% Options %\DeclareOption*{% % \PackageWarning{tkz-orm}{Unknown option ‘\CurrentOption’}% %} \def\orm@error#1{\relax}% TODO %\def\orm@error#1{\PackageWarning{tkz-orm}{#1}}% %%%% Stuff missing in TikZ (should become part if TikZ) \pgfarrowsdeclare{none}{none}{ \arrowsize=0pt \pgfarrowsleftextend{0} \pgfarrowsrightextend{0} }{ \arrowsize=0pt \pgfarrowsleftextend{0} \pgfarrowsrightextend{0} } \tikzstyle{noarrows}=[arrows=none-none] %%%% ORM color constants \definecolor{constraintcolor}{rgb}{1,0,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 like textsuperscript \DeclareRobustCommand*\textsubscript[1]{% \@textsubscript{\selectfont#1}} \newcommand{\@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{\textsubscript{\orm@font@scriptsize #1}} \def\ormind#1{\textsuperscript{\orm@font@bold@scriptsize #1}} \def\ormbraces#1{{\orm@font\textbraceleft#1\textbraceright}} \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 \tikzstyle{every orm line}= [solid,draw,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{every orm}= [every orm line,fill=white,orm] \tikzstyle{zoomed}= [line width=\orm@linewidth*1.5] \tikzstyle{every object type}= [] \tikzstyle{every entity}= [] \tikzstyle{entity}= [every orm, shape=rectangle,rounded corners, align=center,minimum size=6mm, every object type,every entity] \tikzstyle{every value}= [] \tikzstyle{value}= [every orm, shape=rectangle,rounded corners,densely dashed, align=center,minimum size=6mm, every object type,every value] \tikzstyle{relationship}= [every orm line,every relationship] \tikzstyle{relation}= [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]} %%%% 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] {#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 \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 \pgfusepathqfillstroke } % enables the 'mandatory' arrow tip %\tikzstyle{mandatory}= [relationship,cdot-] %\tikzstyle{required}= [relationship,cdot-] %\tikzstyle{optional}= [relationship] %\tikzstyle{required by}= [relationship,-cdot] %\tikzstyle{both required}= [relationship,cdot-cdot] %\tikzstyle{both mandatory}= [relationship,cdot-cdot] % TODO: save the current 'to path' parameters and restore it or use execute \tikzstyle{mandatory}= [relationship, to path={-- (\tikztotarget) {[to path={-- (\tikztotarget) \tikztonodes}] \tikztonodes} node[cdot,pos=0]{}} ] \tikzstyle{required by}= [relationship, to path={-- (\tikztotarget) {[to path={-- (\tikztotarget) \tikztonodes}] \tikztonodes} node[cdot,pos=1]{}} ] \tikzstyle{both mandatory}= [relationship, to path={-- (\tikztotarget) {[to path={-- (\tikztotarget) \tikztonodes}] \tikztonodes} node[cdot,pos=0]{} node[cdot,pos=1]{}} ] \tikzstyle{required}= [mandatory] \tikzstyle{both required}= [both mandatory] \tikzstyle{optional}= [relationship] \tikzstyle{constraint dot} = [fill=constraintcolor,minimum size=1.5mm,draw=none,shape=circle,inner sep=0] \tikzstyle{cdot}=[constraint dot] %%%% Constraint symbols \tikzstyle{every constraint}= [draw=constraintcolor] \tikzstyle{every constraint line}= [line width=\orm@linewidth,draw=constraintcolor,solid] \tikzstyle{every constraint circle}=[] \tikzstyle{constraint circle}= [every orm,every constraint, 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}= [line width=\orm@linewidth,draw=constraintcolor,dotted] \tikzstyle{limits to}= [line width=\orm@linewidth,draw=constraintcolor,dashed, color=constraintcolor,-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{implied}= [fill=gray!30,thin] \tikzstyle{implied model}= [every orm/.append style={fill=gray!30}, every orm line/.append style={thin}] %%%% 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 type,every entity] \tikzset{constraint/text/.style={color=constraintcolor,font=\orm@font,align=center}} \tikzset{constraint/.default=text} % TODO: color=constraintcolor \constraintdeclare{custom}{} % empty constraint circle % inclusive-or (disjunctive mandatory role) constraint \constraintdeclareasnode{mandatory}{ \fill[every constraint,fill=constraintcolor] (0,0) circle (1mm); } \constraintdeclarealias{required}{mandatory} \constraintdeclarealias{total}{mandatory} \constraintdeclarealias{or}{mandatory} % exclusive constraint ('x') \constraintdeclare{exclusive}{ ($(\tikzlastnode.center) +(45:2mm)$) edge[every constraint line] +(225:4mm) ($(\tikzlastnode.center) +(-45:2mm)$) edge[every constraint line] +(-225:4mm) } \constraintdeclarealias{x}{exclusive} % exclusive-or constraint ('xor' or 'partition') \constraintdeclareasnode{xor}{ \fill[every constraint,fill=constraintcolor] (0,0) circle (1mm); \draw[every constraint line] (45:2mm) -- (225:2mm) (-45:2mm) -- (-225:2mm); } \constraintdeclarealias{partition}{xor} % external uniqueness constraint \constraintdeclare{unique}{ (\tikzlastnode.east) edge [every constraint line] (\tikzlastnode.west) } \constraintdeclare{preferred unique}{ ($(\tikzlastnode.west) +(0,-.4mm)$) edge[every constraint line] +(4mm,0mm) ($(\tikzlastnode.west) +(0, .4mm)$) edge[every constraint line] +(4mm,0mm) } \constraintdeclare{equal}{ ($(\tikzlastnode.center) +(-.8mm,-.4mm)$) edge[every constraint line] +(1.6mm,0mm) ($(\tikzlastnode.center) +(-.8mm, .4mm)$) edge[every constraint line] +(1.6mm,0mm) } \constraintdeclareasnode{subset}{ \draw [every constraint line,rounded corners=0] (.8mm,0mm) -- (-.2mm, 0mm) arc (270:90:0.5mm) -- (.8mm,.9mm) (.8mm,-.9mm) -- (-.8mm,-.9mm); } \constraintdeclareasnode{supset}{ \draw [every constraint line,rounded corners=0] (-.8mm,0mm) -- (.2mm, 0mm) arc (-90:90:0.5mm) -- (-.8mm,.9mm) (.8mm,-.9mm) -- (-.8mm,-.9mm); } % Collection constraints (no standard ORM2) \constraintdeclareasnode{ordered}{ \draw [every constraint line,rounded corners=0] (1.6mm,.8mm) -- (0,0) -- (1.6mm,-.8mm); } \constraintdeclarealias{sorted}{ordered} \constraintdeclareasnode{counted}{ \draw [every constraint line] (-.9mm,-.4mm) -- (+.9mm,-.4mm) (-.9mm,+.4mm) -- (+.9mm,+.4mm) (-.4mm,-.9mm) -- (-.4mm,+.9mm) (+.4mm,-.9mm) -- (+.4mm,+.9mm); } \constraintdeclarealias{in bag}{counted} \constraintdeclarealias{in multiset}{counted} %%%% Ring constraints % TODO: join \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); } \constraintdeclareasnode{purely reflexive}{ \path [ormdot] (180:2mm) circle; \path [ormdot] ( 0:2mm) circle; \draw[every constraint line] (-.8mm, .4mm) -- (.8mm, .4mm) (-.8mm,-.4mm) -- (.8mm,-.4mm); } \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[use as bounding box] (270:2.75mm) -- (90:2.75mm); \path [ormdot] (180:2mm) circle; \path [ormdot] ( 0:2mm) circle; \path [every constraint line,fill=white,radius=.7mm] (0:2mm) circle; \draw [every constraint line] (270:1mm) 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. \tikzset{objectification/.style={ entity,fill=none, append after command={ \bgroup% [current point is local=true] node[orm,above=0 of \tikzlastnode] {``#1''} \egroup% } }} %%%% Subtypes \tikzstyle{subtype}= [line width=\orm@linewidth*1.5, % TODO: how to get current line width? >=orm arrow,<-,solid,draw] \tikzstyle{suptype}= [subtype,->] %%%% 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 \tikzset{ unique/.code={\orm@parse@unique#1:\pgf@nil}, unique/.default=1, } \tikzset{ skip unique/.code={\orm@parse@unique@skipped#1:\pgf@nil}, skip unique/.default=1, } % parses b:l and b \def\orm@parse@unique#1:#2\pgf@nil{% \def\orm@temp{#2}% \ifx\orm@temp\pgfutil@empty% \orm@@parse@unique#1:0_1:\pgf@nil% \else% \orm@@parse@unique#1:0_#2\pgf@nil% \fi% } \def\orm@parse@unique@skipped#1:#2\pgf@nil{% \def\orm@temp{#2}% \ifx\orm@temp\pgfutil@empty% \orm@@parse@unique#1:1_1:\pgf@nil% \else% \orm@@parse@unique#1:1_#2\pgf@nil% \fi% } % parses n-m:l and n:l \def\orm@@parse@unique#1:#2_#3:\pgf@nil{% \pgfutil@in@{-}{#1}% \ifpgfutil@in@% \orm@parse@unique@@bar#1:#3_#2\pgf@nil% \else% \orm@parse@unique@@bar#1-#1:#3_#2\pgf@nil% \fi% } % Appends a uniqueness bar at the current predicate node. % % #1 - from role (1,2,..) % #2 - to role (1,2,..) % #3 - level (above: 1,2,.., below: -1,-2,..) % #4 - 1 if skipped, 0 otherwise \def\orm@parse@unique@@bar#1-#2:#3_#4\pgf@nil{% \def\orm@tempA{#1}% \ifx\orm@tempA\pgfutil@empty% \relax% \orm@parse@unique@@bar#2-#2:-1_#4\pgf@nil \else% \tikzset{append after command={% \bgroup% [current point is local=true] \pgfextra{% \ifnum#4>0% \let\orm@unique@bar@style=\orm@unique@bar@skipped% \fi% } let \n1 = {\orm@rolebar@xshifta{#1}}, \n2 = {\orm@rolebar@xshiftb{#2}}, \n3 = {\orm@rolebar@yshift{#3}}, \p1 = ($(\tikzlastnode.west)!-\n3!-90:(\tikzlastnode.east)$), \p2 = ($(\tikzlastnode.east)!-\n3!90:(\tikzlastnode.west)$) in ($(\p1)!\n1!(\p2)$) edge [\orm@unique@bar@style] ($(\p1)!\n2!(\p2)$) \egroup% }}% \fi% } \makeatother %<–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––> \endinput