Skip to content
Snippets Groups Projects
Commit 240c88c2 authored by Christoffer Holm's avatar Christoffer Holm
Browse files

Added a proof-of-concept implementation of \drawlist which draws linked lists

parent 6ff0e800
Branches
No related tags found
No related merge requests found
...@@ -828,3 +828,241 @@ ...@@ -828,3 +828,241 @@
\drawarray@NextArg \drawarray@NextArg
} }
\makeatother \makeatother
%%%% draw list %%%%
\makeatletter
%% TODO: Implement directions and overflow handling
\newcommand{\drawlist}[3][]{%
%% Draw an linked list inside a tikzpicture, highly customizable.
%%
%% Parameters:
%%
%% #1 (optional): Options
%% #2: List name
%% #3: Index 0 content
%% #N: index (N-3) content
%%
%% Options:
%%
%% x = <x position>:
%% sets the x position of the first element.
%%
%% y = <y position>:
%% sets the y position of the first element.
%%
%% position = <coordinate or position>: This option
%% overrides the x and y settings. Allows the user
%% to specify a specific position which could be a
%% named coordinate. For example:
%%
%% \drawlist[anchor=north, position=node.south]{my list}{0}{1}{2}
%%
%% will place an list directly underneath the named
%% position 'node'.
%%
%% anchor = <tikz anchor>:
%% sets which anchor should be used when placing
%% the first element node.
%%
%% element = {<node styling>}:
%% Passes the styling options to each element
%% node.
%%
%% element size = <size>:
%% Set the minimum size of each element.
%%
%% fill = <color>:
%% Set a fill color for *all* the elements.
%%
%% show index:
%% Option that inserts a label for each index in
%% the list as well, the position of the labels
%% are set to 'south' by default. This is disabled
%% by default.
%%
%% index start = <start index>:
%% Set what index the list should start at (0 by default).
%%
%% index position = <left|right|up|down|west|east|north|south>:
%% Specify which side of the nodes the index label
%% should be placed. By default set to 'south'
%%
%% index style = {<node styling>}:
%% Passes the styling options to each index node.
%%
%% Example:
%%
%% \begin{tikzpicture}
%% \drawlist[element size=1cm]{my list}{'a'}{'b'}{'c'}
%% \drawlist[x=4cm, y=0cm]{other list}{1}{2}{34}
%% \end{tikzpicture}
%%
\pgfkeys{/drawlist, default, #1}%
\edef\list@Name{#2}%
\edef\list@Nodes{}%
\pgfmathtruncatemacro{\list@Index}{\list@StartIndex}%
%% Fix the coordinate either based on position or specific x and y coords
\ifx\list@Position\undefined%
\coordinate (list origin) at ($(\list@X, \list@Y) + (\list@xshift, \list@yshift)$);%
\else%
\coordinate (list origin) at ($(\list@Position) + (\list@xshift, \list@yshift)$);%
\fi%
% Once the initial position is set, undef it so that it is
% not defined for the next \drawlist
\undef\list@Position
\drawlist@Element[list origin]{#3}%
\pgfmathtruncatemacro{\list@Index}{\list@Index + 1}%
%% Goto next argument
\drawlist@NextArg%
}
\newcommand{\drawlistset}[1]{%
%% Allow the user to set specific keys and generate styles
%% etc. for the \drawlist command.
%%
%% Parameters:
%%
%% #1: pgfkey statements, all occuring in the
%% /drawlist family by default.
%%
%% Example:
%%
%% \drawlistset{my index/.style={show index, index style={font=\tiny\ttfamily}}}
%% \begin{tikzpicture}
%% \drawlist[my index]{my list}{1}{2}{3}
%% \end{tikzpicture}
%%
\pgfkeys{/drawlist, #1}%
}
%% Options and settings for \drawlist
\pgfkeys{
%% Create a family for \drawlist
/drawlist/.is family,
%% set /drawlist as the current family
/drawlist,
%% Assign the default values for each key
default/.style = {
x = 0,
y = 0,
offset=0.5cm,
xshift = 0,
yshift = 0,
anchor = center,
index style={},
index start=0,
index position=south,
element = {},
element size = 1.5em,
container = {},
fill=none,
},
%% x position of list
x/.estore in = \list@X,
%% y position of list
y/.estore in = \list@Y,
%% the offset between each node
offset/.estore in = \list@Offset,
position/.estore in = \list@Position,
%% shift operations
xshift/.estore in = \list@xshift,
yshift/.estore in = \list@yshift,
%% Which anchor of the first element should be used
anchor/.estore in = \list@Anchor,
%% Custom element settings
element/.code = {\tikzset{list element/.style={#1}}},
%% element size determines the size of the element
element size/.estore in = \list@ElementSize,
%% Custom styling for the container node
container/.code = {\tikzset{list container/.style={#1}}},
%% Show the index for each element
show index/.code = {\def\list@ShowIndex{1}},
index start/.estore in = \list@StartIndex,%
%% Position of the styling: takes the same options as 'direction'
index position/.style = {directions/#1/.get = \list@IndexPosition,%
opposites/#1/.get = \list@IndexAnchor},
%% Customize index style
index style/.code = {\tikzset{list index/.style={#1}}},
%% Customized styling
fill/.estore in = \list@Fill,%
%% define each option for direction
/drawlist,
directions/.cd,
east/.initial = east,
west/.initial = west,
north/.initial = north,
south/.initial = south,
right/.initial = east,
left/.initial = west,
up/.initial = north,
down/.initial = south,
%% define the different opposite directions
/drawlist,
opposites/.cd,
east/.initial = west,
west/.initial = east,
north/.initial = south,
south/.initial = north,
right/.initial = west,
left/.initial = east,
up/.initial = south,
down/.initial = north
}
\newcommand{\drawlist@Element}[2][$(\ExpandWithSpaceBetween{\list@Name}{\list@PrevIndex}.east) + (\list@Offset, 0)$]{%
%% Save the nodes in a list so that we can pass to fit later
\xdef\list@Nodes{\list@Nodes(\ExpandWithSpaceBetween{\list@Name}{\list@Index})}%
\edef\list@CurrentValue{\ExpandWithSpaceBetween{\ExpandWithSpaceBetween{\list@Name}{value}}{\list@Index}}
\edef\list@CurrentNext{\ExpandWithSpaceBetween{\ExpandWithSpaceBetween{\list@Name}{next}}{\list@Index}}
\pgfmathtruncatemacro{\list@PrevIndex}{\list@Index - 1}%
\node[minimum width=\list@ElementSize,%
minimum height=\list@ElementSize,%
fill=\list@Fill,%
rectangle, draw=black, anchor=west,%
list element]%
(\list@CurrentValue)%
at (#1) {#2};%
\node[minimum width=\list@ElementSize,%
minimum height=\list@ElementSize,%
xshift=-\pgflinewidth,%
fill=\list@Fill,%
rectangle, draw=black, anchor=west,%
list element]%
(\list@CurrentNext)%
at (\list@CurrentValue.east) {};%
\node[inner sep=0, outer sep=0, fit=(\list@CurrentValue)(\list@CurrentNext)]%
(\ExpandWithSpaceBetween{\list@Name}{\list@Index}) {};%
\ifx\list@ShowIndex\undefined%
% Do nothing
\else%
\node[list index, anchor=\list@IndexAnchor]%
(\ExpandWithSpaceBetween{\list@Name label}{\list@Index})%
at (\ExpandWithSpaceBetween{\list@Name}{\list@Index}.\list@IndexPosition) {\list@Index};%
\fi%
}
%% Check if next character after the macro is a opening
%% curly brace, if it is expand \drawlist@GobbleArg to
%% process the content of those braces, otherwise: generate
%% the container node.
\newcommand{\drawlist@NextArg}{%
\@ifnextchar\bgroup{%
\drawlist@GobbleArg%
}{%
\node[inner sep=0, outer sep=0,%
list container, fit=\list@Nodes] (\list@Name) {};%
}%
}%
\newcommand{\drawlist@GobbleArg}[1]{%
\drawlist@Element{#1}%
\draw[->] (\ExpandWithSpaceBetween{\ExpandWithSpaceBetween{\list@Name}{next}}{\list@PrevIndex}.center) -- (\ExpandWithSpaceBetween{\list@Name}{\list@Index});
\pgfmathtruncatemacro{\list@Index}{\list@Index + 1}%
\drawlist@NextArg
}
\makeatother
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment