
У меня есть следующий фрагмент кода TikZ, и мне интересно, можно ли его оптимизировать? (Я на самом деле уверен, что это возможно!)
Основная проблема заключается в следующем: когда я поворачиваю первую группу узлов, окруженных эллипсом, чтобы создать вторую группу, эллипс оказывается не на своем месте. (Он не там, где я ожидал. Я не уверен, какая точка зафиксирована во вращении.) Мне приходится настраивать его вслепую, чтобы переместить в нужное место.
То же самое касается меток "Сообщество 2" и "Сообщество 3". Повернутые координаты, похоже, не соответствуют моим ожиданиям, и мне приходится вслепую корректировать их с ограниченным успехом.
Не по теме: я также экспериментировал с циклами for, но поскольку я хочу, чтобы ребра в каждом сообществе немного отличались, кажется, проще просто скопировать-вставить?
Другие оптимизации также приветствуются.
код
\documentclass[tikz]{standalone}
\begin{document}
\begin{tikzpicture}[scale = 1,node distance = 10mm]
\tikzset{
every node/.append style={circle, thick,
inner sep=0pt, minimum size = 3mm},
every label/.append style={red},
c1/.style={draw=blue!50,fill=blue!20},
c2/.style={draw=green!80,fill=green!40},
c3/.style={draw=red!80,fill=red!40}
}
\filldraw[rotate=30,blue!10] (0.8,0.1) ellipse (30pt and 25pt);
\node at (1,1.7) {Community 1};
\node[c1] (1) at (0,0) {};
\node[c1] (2) at (1,1) {}
edge (1);
\node[c1] (3) at (0.7,0.2) {}
edge (2)
edge (1);
\node[c1] (4) at (0.2,0.7) {}
edge (3)
edge (2);
\node[c1] (5) at (1.3,0.5) {}
edge (2)
edge (4);
\begin{scope}[yshift=-2cm,rotate around={-40:(0,0)}]
\filldraw[rotate=30,green!10] (0.8,0.1) ellipse (30pt and 25pt);
\node at (1.2,-0.2) {Community 2};
\node[c2] (A) at (0,0) {};
\node[c2] (B) at (1,1) {}
edge (A);
\node[c2] (C) at (0.7,0.2) {}
edge (B)
edge (A);
\node[c2] (D) at (0.2,0.7) {}
edge (C)
edge (B);
\node[c2] (E) at (1.3,0.5) {}
edge (C)
edge (D);
\end{scope}
\begin{scope}[xshift=2cm,yshift=-0.5cm,rotate around={-40:(0,0)}]
\filldraw[rotate=30,red!10] (0.8,0.1) ellipse (30pt and 25pt);
\node[c3] (a) at (0,0) {};
\node[c3] (b) at (1,1) {}
edge (a);
\node[c3] (c) at (0.7,0.2) {}
edge (b)
edge (a);
\node[c3] (d) at (0.2,0.7) {}
edge (a)
edge (b);
\node[c3] (e) at (1.3,0.5) {}
edge (c)
edge (d);
\node[above of=b] {Community 3};
\end{scope}
\draw (3) -- (A);
\draw (4) -- (D);
\draw (5) -- (a);
\draw (c) -- (E);
\draw (e) -- (B);
\end{tikzpicture}
\end{document}
Выход
решение1
Вам следует определить макрос для чертежа, чтобы его можно было использовать повторно:
Примечания:
- Поскольку я не знаю цели рисунка и того, насколько тщательно вы хотите выбирать размещение и связность, я взял за основу приведенный код.
- При выполнении повторяющихся действий следует помнить, что каждая задача должна рассматриваться схожим образом. Например, при размещении текста узла вы дважды используете абсолютные координаты, а в третий раз используете относительное размещение. Я решил выбрать абсолютное положение, но это может быть неидеально. Конечно, это сделало размещение метки третьего узла чем-то вроде догадки методом проб и ошибок. Смотрите
Relative Placement of Labels
раздел ниже, который может быть лучшим вариантом. - Узлы помечены как
A<color>
, ...D<color>
(где<color>
третий параметр —\MyNodes
), чтобы их можно было систематически именовать и затем ссылаться на них по желанию за пределами чертежа узлов.
Код: Фиксированное размещение этикеток
\documentclass[tikz, border=2pt]{standalone}
\begin{document}
\begin{tikzpicture}[scale = 1,node distance = 10mm]
\tikzset{
every node/.append style={circle, thick,
inner sep=0pt, minimum size = 3mm},
every label/.append style={red},
c1/.style={draw=blue!50,fill=blue!20},
c2/.style={draw=green!80,fill=green!40},
c3/.style={draw=red!80,fill=red!40}
}
\newcommand*{\MyNodes}[6]{%
% #1 = style
% #2 = style
% #3 = node name suffix.
% #4 = node to connect to last node
% #5 = label position
% #6 = label text
\filldraw[rotate=30,#1] (0.8,0.1) ellipse (30pt and 25pt);
\node at #5 {#6};
\node[#2] (A#3) at (0,0) {};
\node[#2] (B#3) at (1,1) {}
edge (A#3);
\node[#2] (C#3) at (0.7,0.2) {}
edge (B#3)
edge (A#3);
\node[#2] (D#3) at (0.2,0.7) {}
edge (C#3)
edge (B#3);
\node[#2] (E#3) at (1.3,0.5) {}
edge (#4#3)
edge (D#3);
}%
\MyNodes{blue!10}{c1}{Blue}{B}{(1,1.7)}{Community 1}
\begin{scope}[yshift=-2cm,rotate around={-40:(0,0)}]
\MyNodes{green!10}{c2}{Green}{C}{(1.2,-0.2)}{Community 2}
\end{scope}
\begin{scope}[xshift=2cm,yshift=-0.5cm,rotate around={-40:(0,0)}]
\MyNodes{red!10}{c3}{Red}{C}{(0.5,1.75)}{Community 3}
\end{scope}
\draw (CBlue) -- (AGreen);
\draw (DBlue) -- (DGreen);
\draw (EBlue) -- (ARed);
\draw (CRed) -- (EGreen);
\draw (ERed) -- (BGreen);
\end{tikzpicture}
\end{document}
Вы также можете разместить метки узлов относительно, что, по-моему, является лучшим вариантом. Чтобы определить, где размещать метки, полезно знать, какие метки где находятся, и макрос \Debug
ниже позволяет вам это увидеть. Если вы раскомментируете последующую строку в MWE, метки узлов будут подавлены.
Код: Относительное размещение меток
\documentclass{article}
\usepackage{tikz}
\tikzset{%
every node/.append style={circle, thick,
inner sep=0pt, minimum size = 3mm},
every label/.append style={red},
c1/.style={draw=blue!50,fill=blue!20},
c2/.style={draw=green!80,fill=green!40},
c3/.style={draw=red!80,fill=red!40}
}
\newcommand*{\Debug}[1]{\tiny#1}%
%\renewcommand*{\Debug}[1]{}% Comment this out for debugging
\newcommand*{\MyNodes}[6]{%
% #1 = style
% #2 = style
% #3 = node name sufffix.
% #4 = node to connect to last node
% #5 = label position
% #6 = label text
\filldraw[rotate=30,#1] (0.8,0.1) ellipse (30pt and 25pt);
\node[#2] (A#3) at (0,0) {\Debug{A}};
\node[#2] (B#3) at (1,1) {\Debug{B}}
edge (A#3);
\node[#2] (C#3) at (0.7,0.2) {\Debug{C}}
edge (B#3)
edge (A#3);
\node[#2] (D#3) at (0.2,0.7) {\Debug{D}}
edge (C#3)
edge (B#3);
\node[#2] (E#3) at (1.3,0.5) {\Debug{E}}
edge (#4#3)
edge (D#3);
\node [#5#3] {#6};
}%
\begin{document}
\begin{tikzpicture}[scale = 1,node distance = 10mm, thick]
\MyNodes{blue!10}{c1}{Blue}{B}{above of=D}{Community 1}
\begin{scope}[yshift=-2cm,rotate around={-40:(0,0)}]
\MyNodes{green!10}{c2}{Green}{C}{below of=C}{Community 2}
\end{scope}
\begin{scope}[xshift=2cm,yshift=-0.5cm,rotate around={-40:(0,0)}]
\MyNodes{red!10}{c3}{Red}{C}{above of=B}{Community 3}
\end{scope}
\draw (CBlue) -- (AGreen);
\draw (DBlue) -- (DGreen);
\draw (EBlue) -- (ARed);
\draw (CRed) -- (EGreen);
\draw (ERed) -- (BGreen);
\end{tikzpicture}
\end{document}
решение2
Еще одна попытка оптимизации. Здесь используются ключи PGF.
Отказ от ответственности: результат не совсем тот же, так как я использовал узел ellipse
вместо ellipse
пути (и мне пришлось немного подкорректировать координаты).
Преимущество, которое я здесь увидел, было использование опции label
добавления меток «Сообщество». Плюс вы можете ссылаться на него для дальнейшего использования.
Есть одна команда:
\drawBlob[<optional arguments](<coordinate>);
Это (<coordinate>)
то, где (0,0)
находится локальная система координат.
Поскольку на картинке происходит несколько преобразований, я не совсем уверен, где находятся капли. Обратите внимание только, что без вращения первый мини-клякса ( A
) будет в (<coordinate>)
.
Вы можете использовать стили
every blob picture
,every mini blob
,every blob
, иevery mini blob edge
для настройки контента. Существуют настройки, похожие на every node
стиль. Для каждого every <something>
стиля существует <something>
стиль, который добавляет свой элемент к соответствующему every
стилю.
Кроме того, есть еще три ключа:
connect mini blobs
,blob name
, иrotate blob
.
Клавиша rotate blob
вращает всю картинку (большую каплю и маленькие капли) вокруг центра большой капли.
Значение ключа blob name
будет использоваться для наименования большого объекта (который назван в честь ключа) и мини-объектов (которые названы <value of blob name>-<char>
в <char>
соответствии A
с E
).
Если blob name
не указано (например, пусто, если не указано имя по умолчанию), узлы получат внутреннее имя (по которому на них можно будет ссылаться позже благодаря внутреннему счетчику).
Счетчик на самом деле не нужен, когда на узлы не будут ссылаться позже…
Наконец, есть connect mini blobs
. Этот ключ должен быть предоставлен списку мини-клякс, которые должны быть связаны внутри блоба.
Поскольку ваш пример, похоже, намекает, что они всегда связаны одинаково, я предварительно установил этот стиль с помощью
\tikzset{connect mini blobs={A/B,A/C,B/C,B/D,C/D,C/E,D/E}}
Это выявляет преимущество использования ключей PGF. Вы можете задать значения по умолчанию, можете изменить их в середине документа, можете задать .append
настройки и можете иметь разные настройки для каждого нового блоба.
Вы также можете задать mini blob <char>
стили, чтобы еще больше настраивать каждый мини-блоб.
И есть every minin blob edge
, для линии, нарисованной внутри краев между мини-каплями.
УлучшенияилиМожет быть, fit
библиотека?
Заметили, что при рисовании мини-капель используются похожие линии?
Одним из улучшений было бы также настроить количество и местоположение этих мини-капель. Несколько ключей и a \foreach
было бы достаточно.
Эллипс, который можно — с помощью библиотеки background
— нарисовать с помощью fit
(например fit=(\qrr@blob@name-A)(\qrr@blob@name-B)…
), хотя вращение этого эллипса придется определять вручную, а фактический размер будет зависеть от rotate blob
. Опять же, происходит слишком много преобразований.
Код
\documentclass[tikz]{standalone}
\usetikzlibrary{shapes.geometric}
\makeatletter
%% Setup
\tikzset{
connect mini blobs/.store in=\qrr@blob@connections,
connect mini blobs=,
blob name/.store in=\qrr@blob@name,
blob name=,
rotate blob/.store in=\qrr@blob@rotate,
rotate blob=0,
% short-cut styles
blob picture/.style={every blob picture/.append style={#1}},
mini blob/.style={every mini blob/.append style={#1}},
blob/.style={every blob/.append style={#1}},
mini blob edge/.style={every mini blob edge/.append style=#1},
% a few defaults
every blob picture/.style={},
every mini blob/.style={shape=circle, thick, draw, minimum size=3mm},
every blob/.style={shape=ellipse, draw, fill, inner sep=0pt, minimum width=60pt, minimum height=50pt},
every mini blob edge/.style={thick},
}
\newcount\c@qrr@blob@count
\newcommand*{\drawBlob}[1][]{\begingroup\tikzset{#1}\draw@blob}
\def\draw@blob(#1){%
\ifx\qrr@blob@name\pgfutil@empty
\edef\qrr@blob@name{qrr@mini-blob@\the\c@qrr@blob@count}%
\fi
\scope[absolute, every blob picture/.try]
\node[shift={(#1)}, rotate=30+\qrr@blob@rotate, every blob/.try] (\qrr@blob@name) at (0.6,0.5) {};
\node[every mini blob/.try, mini blob A/.try, shift={(#1)}, ] (\qrr@blob@name-A) at ([rotate around={\qrr@blob@rotate:(0.6,0.5)}] 0,0) {};
\node[every mini blob/.try, mini blob B/.try, shift={(#1)}, ] (\qrr@blob@name-B) at ([rotate around={\qrr@blob@rotate:(0.6,0.5)}] 1,1) {};
\node[every mini blob/.try, mini blob C/.try, shift={(#1)}, ] (\qrr@blob@name-C) at ([rotate around={\qrr@blob@rotate:(0.6,0.5)}] 0.7,0.2) {};
\node[every mini blob/.try, mini blob D/.try, shift={(#1)}, ] (\qrr@blob@name-D) at ([rotate around={\qrr@blob@rotate:(0.6,0.5)}] 0.2,0.7) {};
\node[every mini blob/.try, mini blob E/.try, shift={(#1)}, ] (\qrr@blob@name-E) at ([rotate around={\qrr@blob@rotate:(0.6,0.5)}] 1.3,0.5) {};
\foreach \qrr@blob@connection@start/\qrr@blob@connection@target in \qrr@blob@connections {
\path[every mini blob edge/.try] (\qrr@blob@name-\qrr@blob@connection@start) edge (\qrr@blob@name-\qrr@blob@connection@target);}
\endscope
\endgroup
\advance\c@qrr@blob@count\@ne
}
\makeatother
%%% Standard connections
\tikzset{connect mini blobs={A/B,A/C,B/C,B/D,C/D,C/E,D/E}} % that's always the same
%%% Custom styles
\tikzset{
c1/.style={draw=blue!50,fill=blue!20},
c2/.style={draw=green!80,fill=green!40},
c3/.style={draw=red!80,fill=red!40}
}
\begin{document}
\begin{tikzpicture}
\drawBlob[
mini blob=c1,
blob={color=blue!10, label=above:Community 1},
blob name=Comm1
](0,0)
\drawBlob[
mini blob=c2,
blob={color=green!10, label=below:Community 2},
blob name=Comm2,
rotate blob=-40
](0,-3)
\drawBlob[
mini blob=c3,
blob={color=red!10, label=above:Community 3},
blob name=Comm3,
rotate blob=-40
](2.5,-.5)
\foreach \start/\target in {1-C/2-A,1-D/2-D,1-E/3-A,3-C/2-E,3-E/2-B} \draw[every mini blob edge] (Comm\start) -- (Comm\target);
\end{tikzpicture}
\end{document}