11

enter image description here

I have a problem using pattern to fill shapes:

I've a shape thats filled with a self-defined pattern (simple dots with the same distance) and i want to zoom at one corner of the shape. I do this with spy (magnification=10). Now inside the magnification the distance between the dots is the same as in the real picture, but i want them to be scaled also. Does anyone know how to do this? Thanks for helping

\documentclass[10pt,a4paper]{article}
\usepackage[latin1]{inputenc}
\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amssymb}
\usepackage{graphicx}
\usepackage{tikz}
\usetikzlibrary{arrows, plotmarks, patterns, spy}
\usepackage{pgfplots}


\pgfdeclarepatternformonly{dots-wide}% name
{\pgfpointorigin}% bottom left
{\pgfqpoint{6mm}{6mm}}% top right
{\pgfqpoint{6mm}{6mm}}% tile size
{% code
    \pgfpathcircle{\pgfpoint{0.3mm}{0.3mm}}{0.3mm} \pgfusepath{fill}
    \pgfusepath{fill}
}

\begin{document}


\begin{tikzpicture}[spy using outlines={circle, magnification=10, connect spies, transform shape}]
    \draw[pattern=dots-wide] (0,0) rectangle (3,3);
    \spy[size=5cm] on (2,2) in node at (8,2);
\end{tikzpicture}
\end{document}
  • 4
    Welcome to TeX.SX! Please help us help you and add a minimal working example (MWE) that illustrates your problem. Reproducing the problem and finding out what the issue is will be much easier when we see compilable code, starting with \documentclass{...} and ending with \end{document}. – BambOo Sep 3 '18 at 17:10
  • I can't reproduce the error, in the tikzEdt visor, but when I open in another pdf reader the error apears, A brute force solution could be drawing the patern manually using draw and foreach then use clip ... – J Leon V. Sep 3 '18 at 17:59
  • I think it's no error... Just hopped someone knows a workaround – Johannes Wendel Sep 3 '18 at 18:31
  • My common fallback for issues with spy is to use a standalone TikZ figure included as PDF. I then spy on the PDF includegraphics'ed inside a node. – Daniel Sep 3 '18 at 20:07
7

I am not sure there is an elegant answer using patterns. This is because patterns are fast, but have certain pitfalls. The pgfmanual says in section 104.1:

There are a number of pitfalls and restrictions when using patterns. First, once a pattern has been declared, you cannot change it anymore. In particular, it is not possible to enlarge it or change the line width. Such flexibility would require that the repeating of the pattern were not done by the graphic language, but on the pgf level. This would make patterns orders of magnitude slower to produce and to render.

If you want to use spy, you may just resort to more conventional (but also much slower) methods to draw the dots. (There are many ways to make this more elegant, e.g. using a path picture, but here I just present a rather clumsy workaround which will be shown in the next code.) In what follows I list some attempts that show that patterns are not transformed easily, and provide a first clumsy workaround.

\documentclass[10pt,a4paper]{article}
\usepackage[latin1]{inputenc}
\usepackage{amsmath}
\usepackage{amsfonts}
\usepackage{amssymb}
\usepackage{tikz}
\usetikzlibrary{arrows, plotmarks, patterns, spy}
% made the pattern a bit more flexible but am not using it
\pgfdeclarepatternformonly[\dotradius,\dotdistance]{dots-wide}% name
{\pgfpointorigin}% bottom left
{\pgfqpoint{\dotdistance}{\dotdistance}}% top right
{\pgfqpoint{\dotdistance}{\dotdistance}}% tile size
{% code
    \pgfpathcircle{\pgfpoint{\dotradius}{\dotradius}}{\dotradius} \pgfusepath{fill}
    \pgfusepath{fill}
}
\tikzset{
dot radius/.store in=\dotradius, 
dot radius=0.3mm,
dot distance/.store in=\dotdistance, 
dot distance=6mm
}

\newsavebox{\mybox}
\begin{document}

\section*{Resistance of patterns against transformations}

\begin{tikzpicture}[font=\sffamily]
 \pattern [pattern=bricks] (-1,-1) rectangle (1,1);
 \node[anchor=south] at (0,1.1) {original};
 \begin{scope}[xshift=3cm]
  \pattern [pattern=bricks,rotate=45] (-1,-1) rectangle (1,1);
  \node[anchor=south] at (0,1.4) {\verb|rotate=45|};
 \end{scope}
 % these weird shifts are necessary because transform canvas is rather brutal
 \begin{scope}[xshift={cos(45)*8cm},yshift=-{sin(45)*8cm},transform canvas={rotate=45}]
  \pattern [pattern=bricks] (-1,-1) rectangle (1,1);
  \node[anchor=south,rotate=-45] at (1,1) {\verb|transform canvas={rotate=45}|};
 \end{scope}
\end{tikzpicture}

\section*{Not even saveboxes help}

\begin{lrbox}{\mybox}%
\begin{tikzpicture}[font=\sffamily]
 \pattern [pattern=bricks] (-1,-1) rectangle (1,1);
\end{tikzpicture}%
\end{lrbox}

\begin{tikzpicture}[font=\sffamily]
 \node at (0,0){\usebox{\mybox}};
 \node[anchor=south] at (0,1.1) {original};
 \begin{scope}[xshift=3cm]
  \node[rotate=45] at (0,0){\usebox{\mybox}};
  \node[anchor=south] at (0,1.4) {\verb|rotate=45|};
 \end{scope}
 % these weird shifts are necessary because transform canvas is rather brutal
 \begin{scope}[xshift={cos(45)*8cm},yshift=-{sin(45)*8cm},transform canvas={rotate=45}]
  \node at (0,0){\usebox{\mybox}};
  \node[anchor=south,rotate=-45] at (1,1) {\verb|transform canvas={rotate=45}|};
 \end{scope}
\end{tikzpicture}

% \section*{Resistance of patterns against transformations}
% 
% \begin{tikzpicture}[font=\sffamily]
%  \pattern [pattern=dots-wide] (-1,-1) rectangle (1,1);
%  \node[anchor=south] at (0,1.1) {original};
%  \begin{scope}[xshift=3cm]
%   \pattern [pattern=dots-wide,rotate=45] (-1,-1) rectangle (1,1);
%   \node[anchor=south] at (0,1.4) {\verb|rotate=45|};
%  \end{scope}
%  % these weird shifts are necessary because transform canvas is rather brutal
%  \begin{scope}[xshift={cos(45)*8cm},yshift=-{sin(45)*8cm},transform canvas={rotate=45}]
%   \pattern [pattern=dots-wide] (-1,-1) rectangle (1,1);
%   \node[anchor=south,rotate=-45] at (1,1) {\verb|transform canvas={rotate=45}|};
%  \end{scope}
% \end{tikzpicture}
% 
% \section*{Not even saveboxes help}
% 
% \begin{lrbox}{\mybox}%
% \begin{tikzpicture}[font=\sffamily]
%  \pattern [pattern=dots-wide] (-1,-1) rectangle (1,1);
% \end{tikzpicture}%
% \end{lrbox}
% 
% \begin{tikzpicture}[font=\sffamily]
%  \node at (0,0){\usebox{\mybox}};
%  \node[anchor=south] at (0,1.1) {original};
%  \begin{scope}[xshift=3cm]
%   \node[rotate=45] at (0,0){\usebox{\mybox}};
%   \node[anchor=south] at (0,1.4) {\verb|rotate=45|};
%  \end{scope}
%  % these weird shifts are necessary because transform canvas is rather brutal
%  \begin{scope}[xshift={cos(45)*8cm},yshift=-{sin(45)*8cm},transform canvas={rotate=45}]
%   \node at (0,0){\usebox{\mybox}};
%   \node[anchor=south,rotate=-45] at (1,1) {\verb|transform canvas={rotate=45}|};
%  \end{scope}
% \end{tikzpicture}

\section*{A workaround}

\begin{tikzpicture}[spy using outlines={circle, magnification=10, connect spies, transform shape}]
    \draw (0,0) rectangle (3,3);
    \foreach \X in {0.3,0.9,...,2.7}
     {\foreach \Y in {0.3,0.9,...,2.7}
      {
      \fill (\X,\Y) circle (0.3mm);
      }}
    \spy[size=5cm] on (2,2) in node at (8,2);
\end{tikzpicture}
\end{document}

enter image description here

This may, of course, be made much more elegant using path pictures. I think one can redo any decoration that way. Arguably, it is even simpler to use that syntax, but the price one has to pay is performance. On the other hand, it is more straightforward to define the origin of the pattern. Another advantage of this is that it can be used in 3D projections. (I wish I would have known about this workaround when I created this... ;-) (EDIT: Got rid of the dangerous \pgfextra after learning that one can strip off the units with the function scalar.)

\documentclass{article}
\usepackage{tikz}

\usetikzlibrary{spy,calc}
% these parameters can be adusted
\tikzset{
dot radius/.store in=\dotradius, 
dot radius=0.3mm,
dot distance/.store in=\dotdistance, 
dot distance=6mm
}

\tikzset{dot pattern/.style={path picture={
\fill let \p1=($(path picture bounding box.north east)-(path picture bounding
box.south west)$), \n1={int(scalar(\x1/\dotdistance)))},
\n2={int(scalar(\y1/\dotdistance))} in
foreach \DotX in {1,...,\n1}
 {foreach \DotY in {1,...,\n2}
 {($(path picture bounding box.south west)+({(\DotX-1/2)*\dotdistance},
 {(\DotY-1/2)*\dotdistance})$) circle (\dotradius)}
};}}}

\begin{document}
\section*{A slightly more elegant version of the workaround}

\begin{tikzpicture}[spy using outlines={circle, magnification=10, connect spies, transform shape}]
    \draw[dot pattern] (0,0) rectangle (3,3);
    \spy[size=5cm] on (2,2) in node at (8,2);
\end{tikzpicture}

\begin{tikzpicture}[spy using outlines={circle, magnification=10, connect spies, transform shape}]
    \draw[dot pattern] plot[smooth cycle] coordinates {(0,0) (1,2) (3,3) (3,0)
    (2,-1)};
    \spy[size=5cm] on (2,1.5) in node at (8,2);
\end{tikzpicture}

\end{document}

enter image description here

Another workaround works as follows: create a pdf of pattern, and use it (with clip, say). Then you can zoom in without the need of a foreach loop. Make sure that you enable the externalization, e.g. by compiling with pdflatex -shell-escape.

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{external}
\tikzexternalize 

\usetikzlibrary{arrows, plotmarks, patterns, spy,external}
% made the pattern a bit more flexible but am not using the flexibility here
\pgfdeclarepatternformonly[\dotradius,\dotdistance]{dots-wide}% name
{\pgfpointorigin}% bottom left
{\pgfqpoint{\dotdistance}{\dotdistance}}% top right
{\pgfqpoint{\dotdistance}{\dotdistance}}% tile size
{% code
    \pgfpathcircle{\pgfpoint{\dotradius}{\dotradius}}{\dotradius} \pgfusepath{fill}
    \pgfusepath{fill}
}
\tikzset{
dot radius/.store in=\dotradius, 
dot radius=0.3mm,
dot distance/.store in=\dotdistance, 
dot distance=6mm
}

\newsavebox{\mybox}
\tikzexternalize

\begin{document}
% this is a trick to create some graphics without adding it to the document
% this trick is inspired by https://tex.stackexchange.com/a/433461/121799
\begin{lrbox}{\mybox}
\tikzsetnextfilename{mypattern}
\begin{tikzpicture}
  \fill [pattern=dots-wide] (-10,-10) rectangle (10,10);
\end{tikzpicture}
\end{lrbox}
\tikzexternaldisable
\section*{Another workaround}

\begin{tikzpicture}[spy using outlines={circle, magnification=10, connect spies, transform shape}]
    \begin{scope}
    \draw[clip] (0,0) rectangle (3,3);
     \node at (0,0) {\includegraphics{mypattern.pdf}};
    \end{scope}
    \spy[size=5cm] on (2,2) in node at (8,2);
\end{tikzpicture}
\end{document}

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.