
Есть ли какой-нибудь пакет для рисования хороших RDF-графиков, например, как показано ниже (взято изтам)?
Можно ли создать такой график (в том же стиле) в TikZ?
решение1
В первую очередь я бы начал с определения стиля, способного представлять вершины; в основном нам нужно:
- эллипс;
- некоторые определения размеров;
- сделать границу толще и установить цвет заливки ярче по отношению к границе;
- сделать текст белым;
- возможно, стоит немного уменьшить размер шрифта текста.
Определение стиля в TikZ можно выполнить с помощью \tikzset
:
\tikzset{vertex style/.style={
draw=#1,
thick,
fill=#1!70,
text=white,
ellipse,
minimum width=2cm,
minimum height=0.75cm,
font=\small,
outer sep=3pt, % the usage of this option will be clear later on
},
}
Обратите внимание на две вещи: этот стиль получает аргумент, цвет, и устанавливает цвет заливки ярче, смешивая цвет с белым. Во-вторых, фигура ellipse
требует библиотеку:
\usetikzlibrary{shapes.geometric}
Давайте теперь создадим первую вершину. Я бы начал с "Righteous Kill", от которой отходит больше всего связей.
\documentclass[dvipsnames,png,border=10pt,tikz]{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes.geometric} % required for the ellipse shape
\tikzset{vertex style/.style={
draw=#1,
thick,
fill=#1!70,
text=white,
ellipse,
minimum width=2cm,
minimum height=0.75cm,
font=\small,
outer sep=3pt,
},
}
\begin{document}
\begin{tikzpicture}
\node[vertex style=Turquoise] (Rk) {Righteous Kill};
\end{tikzpicture}
\end{document}
Будьте осторожны с этой опцией dvipsnames
: ее необходимо было установить в параметрах класса, чтобы предотвратить возможную option clash
ошибку, поскольку TikZ загружает сам себя xcolor
.
Узел использует определенный выше стиль, устанавливая свой цвет (определение в xcolor
руководстве), свое имя (Rk)
и свой текст {Righteous Kill}
.
Результат:
С этого момента я не буду показывать преамбулу, а только код изображения.
Вторым шагом является определение местоположения других узлов. Можно использовать множество возможностей (используя GraphViz, используя объектно-ориентированный модуль PGF и создавая некоторый пользовательский класс - см.:Построение связей между элементами базы данных): здесь я использую positioning
библиотеку TikZ. Каждая вершина, таким образом, будет располагаться, начиная с положения других вершин, ссылаясь на их имена.
Примечание: поскольку каждая вершина должна быть соединена с другой, синтаксис
\node[options] (a) {text a} ... edge[options] node[options]{text conn} (b);
соединит вершины a
и b
установит метку для соединения text conn
. Крайне полезно в этом случае. Действительно, давайте начнем добавлять еще один узел:
\begin{tikzpicture}[node distance=2.75cm,>=stealth']
\node[vertex style=Turquoise] (Rk) {Righteous Kill};
\node[vertex style=BurntOrange, above of=Rk,xshift=2em] (BD) {Bryan Dennehy}
edge [<-,cyan!60!blue] node[text style,above]{starring} (Rk);
\end{tikzpicture}
Параметры node distance=2.75cm,>=stealth'
касаются базового расстояния вершин и типа наконечника стрелки, используемого для соединения. В зависимости от выбранного наконечника стрелки arrows
должна быть загружена библиотека.
Обратите внимание, что текст соединения требует стиля: text style
. Его определение:
\tikzset{
text style/.style={
sloped, % the text will be parallel to the connection
text=black,
font=\footnotesize,
above
}
}
Местоположение нового узла задается с помощью опций above of=Rk,xshift=2em
: первая задает позицию относительно имени предыдущего узла, который мы создали ранее, вторая немного сдвигает эту позицию вправо.
Результат:
Поняв этот механизм, можно найти все остальные узлы:
\begin{tikzpicture}[node distance=2.75cm,>=stealth']
\node[vertex style=Turquoise] (Rk) {Righteous Kill};
\node[vertex style=BurntOrange, above of=Rk,xshift=2em] (BD) {Bryan Dennehy}
edge [<-,cyan!60!blue] node[text style]{starring} (Rk);
\node[vertex style=BurntOrange, right=1.5cm of Rk,yshift=4ex] (AP) {Al Pacino}
edge [<-,cyan!60!blue] node[text style]{starring} (Rk);
\node[vertex style=red, below right of=Rk,xshift=2em] (JA) {John Avnet}
edge [<-,cyan!60!blue] node[text style]{director} (Rk);
\node[vertex style=BurntOrange, right=1.5cm of Rk,yshift=-4ex] (RN) {Robert De Niro}
edge [<-,cyan!60!blue] node[text style]{starring} (Rk);
\node[vertex style=MidnightBlue, above right of=Rk,xshift=2em] (Dr) {Drama}
edge [<-,cyan!60!blue] node[text style]{genre} (Rk);
\node[vertex style=Maroon, below of=Rk,xshift=-2em] (Skf) {Serial Killer Films}
edge [<-,cyan!60!blue] node[text style]{subject} (Rk);
\node[vertex style=Maroon, below right of=Skf] (Cf) {Crime Films}
edge [<-,cyan!60!blue] node[text style]{broader} (Skf);
\end{tikzpicture}
После того, как эта задача выполнена, можно приступить к добавлению «жидкого» фона вокруг некоторых вершин.
Чтобы разместить что-то на заднем плане, библиотека backgrounds
поможет, а также библиотека Эндрю Стейси для рисования плавной кривой. Более того, часто требуются некоторые вычисления, поэтому также следует загрузить hobby
библиотеку .calc
Определение пути должно быть сделано следующим образом: начиная с севера узла (Rk)
, обходят все возможные якоря интересующих узлов, которые должны быть выделены. Существуют некоторые автоматические инструменты, см.Реализация хобби-пути в подходе выпуклой оболочки, но они не могут быть точными и тонкими, как некоторые ручные работы. И, в данном случае, это позволяет достичь наилучшего результата.
\begin{pgfonlayer}{background}
\draw[Maroon,fill=Maroon,dashed,fill opacity=0.1](Rk.north)
to[closed,curve through={(Rk.north west).. (Rk.west) .. (Rk.south west)
..($(Rk.south west)!0.5!(Skf.north)$) .. (Skf.north west).. (Skf.west)
.. (Skf.south west) .. ($(Skf.south)!0.75!(Cf.west)$) .. (Cf.west)
.. (Cf.south west) .. (Cf.south) .. (Cf.south east) .. (Cf.east)
.. ($(Cf.north east)!0.65!(Skf.south east)$) .. (Skf.east)
.. (Skf.north east).. ($(Skf.north)!0.35!(Rk.south east)$)
.. (Rk.south east) .. (Rk.east)..(Rk.north east)}](Rk.north);
\end{pgfonlayer}
Примерно таков код... но вот результат:
Таинственная опция outer sep=3pt
позволяет не располагать "жидкий фон" слишком близко к границе формы. И вот все!
Полный код для справки:
\documentclass[dvipsnames,png,border=10pt,tikz]{standalone}
\usepackage{tikz}
\usetikzlibrary{shapes.geometric} % required for the ellipse shape
\usetikzlibrary{arrows, backgrounds, calc, hobby, positioning}
% this avoides some problem with the hobby implementation
% egreg's code from:
% http://www.guitex.org/home/it/forum/5-tex-e-latex/83195-la-libreria-hobby-tikz-non-funziona-piu#83203
\ExplSyntaxOn
\cs_if_exist:NF \prg_stepwise_function:nnnN { \cs_gset_eq:NN \prg_stepwise_function:nnnN \int_step_function:nnnN }
\cs_if_exist:NF \prg_stepwise_inline:nnnn { \cs_gset_eq:NN \prg_stepwise_inline:nnnn \int_step_inline:nnnn }
\ExplSyntaxOff
\tikzset{vertex style/.style={
draw=#1,
thick,
fill=#1!70,
text=white,
ellipse,
minimum width=2cm,
minimum height=0.75cm,
font=\small,
outer sep=3pt,
},
text style/.style={
sloped,
text=black,
font=\footnotesize,
above
}
}
\begin{document}
\begin{tikzpicture}[node distance=2.75cm,>=stealth']
\node[vertex style=Turquoise] (Rk) {Righteous Kill};
\node[vertex style=BurntOrange, above of=Rk,xshift=2em] (BD) {Bryan Dennehy}
edge [<-,cyan!60!blue] node[text style]{starring} (Rk);
\node[vertex style=BurntOrange, right=1.5cm of Rk,yshift=4ex] (AP) {Al Pacino}
edge [<-,cyan!60!blue] node[text style]{starring} (Rk);
\node[vertex style=red, below right of=Rk,xshift=2em] (JA) {John Avnet}
edge [<-,cyan!60!blue] node[text style]{director} (Rk);
\node[vertex style=BurntOrange, right=1.5cm of Rk,yshift=-4ex] (RN) {Robert De Niro}
edge [<-,cyan!60!blue] node[text style]{starring} (Rk);
\node[vertex style=MidnightBlue, above right of=Rk,xshift=2em] (Dr) {Drama}
edge [<-,cyan!60!blue] node[text style]{genre} (Rk);
\node[vertex style=Maroon, below of=Rk,xshift=-2em] (Skf) {Serial Killer Films}
edge [<-,cyan!60!blue] node[text style]{subject} (Rk);
\node[vertex style=Maroon, below right of=Skf] (Cf) {Crime Films}
edge [<-,cyan!60!blue] node[text style]{broader} (Skf);
\begin{pgfonlayer}{background}
\draw[Maroon,fill=Maroon,dashed,fill opacity=0.1](Rk.north)
to[closed,curve through={(Rk.north west).. (Rk.west) .. (Rk.south west)
..($(Rk.south west)!0.5!(Skf.north)$) .. (Skf.north west).. (Skf.west)
.. (Skf.south west) .. ($(Skf.south)!0.75!(Cf.west)$) .. (Cf.west)
.. (Cf.south west) .. (Cf.south) .. (Cf.south east) .. (Cf.east)
.. ($(Cf.north east)!0.65!(Skf.south east)$) .. (Skf.east)
.. (Skf.north east).. ($(Skf.north)!0.35!(Rk.south east)$)
.. (Rk.south east) .. (Rk.east)..(Rk.north east)}](Rk.north);
\end{pgfonlayer}
\end{tikzpicture}
\end{document}