tikz - positioning pics just like a node
I think this question came up before. The trick is to wrap the pic
in a matrix
node (to be clear, this is a 1x1 matrix). This works unless you want to put the pic
in a matrix, but there you can use the pic
itself, or want to use a matrix in the pic
, at least as of now.
\documentclass[tikz,border=2mm]{standalone}
\usetikzlibrary{positioning}
\tikzset{
pics/mypic/.style={code={
\tikzset{mypic/.cd,#1} \def\pv##1{\pgfkeysvalueof{/tikz/mypic/##1}}%
\edef\m{\pv{m}}%
\foreach \row [count=\j] in \m {
\foreach \r/\g/\b [count=\i] in \row {
\fill[fill={rgb,255:red,\r; green,\g; blue,\b}] (\i-1,\j-1) rectangle ++(1,1);
}%foreach
}%foreach
}},mypic/.cd,m/.initial={{255/0/0}},/tikz/.cd,
%
}
\begin{document}
\begin{tikzpicture}
\tikzset{
box/.style={draw,minimum width=1cm,minimum height=1cm}
}
\def\clst{
{31/18/12}%
}
\node[box] (A) at (0,0) {A};
\foreach \pos in {left,right,above,below} {
\node[\pos=1 of A,matrix] (B-\pos) {\pic{mypic={m=\clst}};\\};
}
\node[left=6 of A,box] (C) at (0,0) {C};
\foreach \pos in {left,right,above,below} {
\node[\pos=1 of C,box] (D) {D};
}
\end{tikzpicture}
\end{document}
One simple way of dealing with larger pics is to count the entries and making them symmetric.
\documentclass[tikz,border=2mm]{standalone}
\usetikzlibrary{positioning}
\tikzset{
% colmat
pics/colmat/.style={code={
\tikzset{colmat/.cd,#1}
\def\pv##1{\pgfkeysvalueof{/tikz/colmat/##1}}%
\edef\m{\pv{m}}%
\foreach \row [count=\j] in \m {\xdef\mydimj{\j}
\foreach \r/\g/\b [count=\i] in \row {\xdef\mydimi{\i}}}
\typeout{\m,\mydimi,\mydimj}
\edef\w{2}%
\foreach \row [count=\j] in \m {
\foreach \r/\g/\b [count=\i] in \row {
\fill[fill={rgb,255:red,\r; green,\g; blue,\b}]
(\i*\w-\mydimi/2,\j*\w-\mydimj/2) rectangle ++(\w,\w);
}%foreach
}%foreach
}},colmat/.cd,m/.initial={{255/0/0}},/tikz/.cd,
%
}
\begin{document}
\begin{tikzpicture}
\tikzset{
box/.style={draw,minimum width=1cm,minimum height=1cm}
}
\def\clst{
{200/10/10},
{10/10/200},
{10/200/200}%
}
\node[box] (A) at (0,0) {A};
\foreach \pos in {left,right,above,below} {
\node[\pos=1 of A,matrix] (B-\pos) {\pic{colmat={m=\clst}};\\};
}
\node[left=6 of A,box] (C) at (0,0) {C};
\foreach \pos in {left,right,above,below} {
\node[\pos=1 of C,box] (D) {D};
}
\end{tikzpicture}
\end{document}
Like this?
Just change right=1 of A
to right=0 of A
, i.e.:
\pic[right=0 of A] (B) {mypic={m=\clst}};
Another possibility is in tikzpicture
options (or in tikzset
) define
node distance=0cm
and then write
\pic[right=of A] (B) {mypic={m=\clst}};
Updated:
With the drawing code
\foreach \row [count=\j] in \m {
\foreach \r/\g/\b [count=\i] in \row {
\fill[fill={rgb,255:red,\r; green,\g; blue,\b}]
(\i * \w, \j * \w) rectangle ++(\w,\w);
}
}
- every filled square has its south west corner at
(\i*\w, \j*\w)
and north east corner at(\i*\w + \w, \j*\w + \w)
. - For
\i
ranging from1
to\mydimi
andj
ranging from1
to\mydimj
(inclusive), the constructed bigger rectangle has its south west corner at(1*\w, 1*\w)
and north east corner at(\mydimi*\w + \w, \mydimj*\w + \w)
. - Hence the center of that bigger rectangle is at
(.5*\mydimi*\w + \w, 0.*\mydimj*\w + \w)
. To put that center at the origin(0, 0)
, we shift the coordinate by using
shift={(-0.5*\mydimi*\w - \w, -0.5*\mydimj*\w - \w)}
A full example, note that I have slightly simplified the pic
definition.
\documentclass[tikz,border=2mm]{standalone}
\usetikzlibrary{positioning}
\makeatletter
\tikzset{
% colmat
pics/colmat/.style={code={
\tikzset{colmat/#1}
\foreach \row [count=\j] in \pic@colmat@m {
\xdef\mydimj{\j}
\foreach \r/\g/\b [count=\i] in \row {\xdef\mydimi{\i}}
}
\edef\w{2}%
\foreach \row [count=\j] in \pic@colmat@m {
\foreach \r/\g/\b [count=\i] in \row {
\fill[fill={rgb,255:red,\r; green,\g; blue,\b},
shift={(-0.5*\mydimi*\w - \w, -0.5*\mydimj*\w - \w)}]
(\i * \w, \j * \w) rectangle ++(\w,\w);
}%foreach
}%foreach
}},
colmat/m/.estore in=\pic@colmat@m,
colmat/m/.initial={{255/0/0}}
}
\makeatother
\begin{document}
\begin{tikzpicture}
\tikzset{
box/.style={draw,minimum width=1cm,minimum height=1cm}
}
\def\clst{
{200/10/10},
{10/10/200},
{10/200/200}%
}
\node[box] (A) at (0,0) {A};
\foreach \pos in {left,right,above,below} {
\pic[\pos=2 of A] (B-\pos) {colmat={m=\clst}};
}
\node[left=6 of A,box] (C) at (0,0) {C};
\foreach \pos in {left,right,above,below} {
\node[\pos=1 of C,box] (D-\pos) {D};
}
\end{tikzpicture}
\end{document}
Old answer:
Unlike node, a pic has a fixed anchor. That anchor is the (0, 0)
inside its drawing commands. See pgf manual, sec. 18.2, paragraph The location of a pic.
In your pic definition, (0, 0)
is at the south west corner of filled square. Hence tikz puts every south west corner of pic mypic
to positions like left=1 of A
.
Inside the definition of pic, moving (0, 0)
to the center of filled square solves your problem. Here I use a shift
option to offset. It is better to adjust the coordinates used in drawing commands of mypic
, to keep its center at origin.
% before
\fill[fill={...}, ] (\i-1,\j-1) rectangle ++(1,1);
% after
\fill[fill={...}, shift={(-.5, -.5)}] (\i-1,\j-1) rectangle ++(1,1);
% for updated example in question, a shift of (0, -3) is required.