10

I have a document with some TikZ images in it, which are automatically generated from a program I have written. All images are fairly similar, and each of them contains of a large number of squares with different, arbitrary rgb colors. My question is: What is the best way of coloring all this large number of squares?

The method I have used so far consists of defining the color fillcolor by using the macro \definecolor, before drawing each square, and of using that color to fill the square. That means that each time I'm drawing a new square, I'm defining fillcolor again.

However, I'm not so sure that is the way to do it. pdflatex seems to require very much memory when compiling the document, and, for some strange reason, runs out of memory if I include more than 5 of the images (even if I increase the amount of memory tenfold when going from 5 to 6 images). Is there any chance that it stores all the colors I have defined, and that I need to make pdflatex "forget" about fillcolor before calling \definecolor again?

Update: Below is an example of one of the TikZ images I want in my document (it's a vizualization of the phase fraction field in a two-phase fluid simulation using the finite volume method and the volume of fluid method, and yes, I know, the fluid interface isn't exactly crisp):

TikZ image containing 1990 squares in different colors

This particular image consists of 1990 filled squares and 4 unfilled squares.

First, for each square, fillcolor is defined with \definecolor, and the square is filled with that color:

\definecolor{fillcolor}{rgb}{1.000000,0.241735,0.000000}
\fill[fillcolor] (0.203125,0.578125) rectangle (0.218750,0.593750);

Then the sides of each square is drawn, for example

\draw (0.203125,0.578125) rectangle (0.218750,0.593750);
  • 1
    Please, show us your code to generate the figures. Maybe you can make use of \foreach. – Sigur Dec 21 '13 at 13:19
  • I guess that the colors defined via \definecolor are stored in a internal macro. If so, redefining it will recycle the same macro, and thus will not use more memory. – JLDiaz Dec 21 '13 at 13:20
  • @JLDiaz your assumption is correct: \definecolor simply redefines fillcolor; there is no memory leak in it. – Christian Feuersänger Dec 21 '13 at 14:45
  • What you do sounds very much as if you visualize data in a mesh of rectangles. Your approach sounds reasonable to me. How many rectangles do you have? Can you show us an extract of your images, perhaps combined with a brief idea how the extract generalizes to the problematic huge files? – Christian Feuersänger Dec 21 '13 at 14:48
  • @Christian: You are completely right! By your request I uploaded one of the images. What do you mean by "generalizes the problematic huge files"? I have no huge files, in fact, the TikZ code for the largest image only takes up 361 kB of disk space. pdflatex just uses extremely much memory when compiling, for some unknown reason. I don't even use loops in the LaTeX code for the images (because I don't need any). – StrawberryFieldsForever Dec 21 '13 at 20:40
8

As I said in a comment, if \definecolor, simply stores the color definition in a TeX macro, then redefining it will recycle the same macro, and thus will not use more memory.

However it is also possible that \defincolor is pushing some kind of literal PDF code which defines the color for the PDF engine. In this case, as far as I can understand it, no more memory should be used either for redefining a lot of times the same color.

Anyway, just in case, it is possible to use a RGB color without previous definition. Here is how.

  • In principle, tikz recognizes a color specificacion of the form {rgb:red,255;green,255;blue:255}. But it is not interpreted as one would expect. For example [fill={rgb:red,255;green,255;blue,0}] will not produce yellow, but a black tinted yellow instead (see figure below). I could not find documentation about how this specification works. Apparently all red, green, blue values are summed up, and then each particular value divided by the total. That, is, what we specify aren't absolute byte values, but relative weights. So, to get yellow we can write: {rgb:red,1;green:1;blue:-1}. I find this syntax rather inconvenient (moreover, how could we get "white"?)

  • PGF uses a internal macro: \pgfsys@color@rgb@fill which has the expected behaviour. It receives three numeric values for the r, g and b components, as 0-255 values. So we can use this macro to set the fill color just before the filling command.

Here is a code which shows several options:

\documentclass{article}
\usepackage{tikz}

% Define a more convenient macro without @ in its name
\makeatletter
\def\fillRGB#1#2#3{\pgfsys@color@rgb@fill{#1}{#2}{#3}}
\makeatother
\begin{document}

% Standard xcolor defined color
\definecolor{RGByellow}{RGB}{255,255,0}

% Some examples
\tikz{\node[draw,fill=RGByellow]{defined colour};}

\tikz{\node[draw,fill={rgb:red,255;green,255;blue,0}]{Tikz rgb: 255,255,0};}

\tikz{\node[draw,fill={rgb:red,1;green,1;blue,-1}]{Tikz rgb: 1,1,-1};}

\tikz{\fillRGB{255}{255}{0}\node[fill,draw]{{\tt fillRGB: 255,255,0} };}

\end{document}

The result:

Result

|improve this answer|||||
6

I believe that your approach with \definecolor is fine.

My suggestion would be to use the PGF basic layer rather than tikz, i.e. to use something like

\definecolor{fillcolor}{...}
\pgfsetfillcolor{fillcolor}
\pgfsetstrokecolor{fillcolor!80!black}
\pgfpathrectangle{...}{...}
\pgfusepathqfillstroke
% next one, no scope in-between...
\definecolor{fillcolor}{...}
\pgfsetfillcolor{fillcolor}
\pgfsetstrokecolor{fillcolor!80!black}
\pgfpathrectangle{...}{...}
\pgfusepathqfillstroke

Please look up the mentioned commands in pgfmanual.pdf as I might have misspelled some of them (I cited by heart).

My suggestion to combine stroke and fill path and vary only the color might make sense for you and could be applied to the tikz path as well. This should also reduce memory consumption and typesetting time.

One remark which is actually out of scope of your question: my suggestion to use the the draw color fillcolor!80!black is inspired by the default of pgfplots which I find less harsh compared to black outlines. And pgfplots has the same use-case as here: what you write here is actually a kind of mesh/surface with faceted shading and explicit color input (in pgfplots: \addplot3[surf], compare pgfplots: color a (3D) surf using arbitrary RGB colors) .

Even after memory reduction, you have 'a lot' of data points (speaking in terms of what TeX is supposed to handle). So, even reducing memory might reach limits of TeX eventually. If one image works but more than one fails, you may benefit from using image externalization. You may also benefit from using lualatex instead of pdflatex as lualatex has dynamic memory allocation. You should also experiment with \clearpage between adjacent figure environments to ensure that TeX does not have old figures in its memory (as @JLDiaz suggested in a comment). The details for image externalization and memory consumption are summarized in Section "Memory and Speed considerations" in http://pgfplots.sourceforge.net/pgfplots.pdf

|improve this answer|||||
  • Thanks, Chistian. I already use image externalization, so I get a separate pdf for each of the images. But I can try your suggestion to use only pgf and no tikz to see if the memory consumption is reduced. – StrawberryFieldsForever Dec 22 '13 at 0:46
  • I would have assumed that image externalization eliminates any scalability issues which depend on the number of tikzpictures. Is it right that you observed memory problems if you switch from 4 to 5 pictures? Is this related to tikz or is it simply related to \includegraphics? – Christian Feuersänger Dec 22 '13 at 18:48
  • I don't know. What I do know is that if I skip including a page from a 196 kB large pdf file using the pdfpages package, the memory consumption does not run amok. Wired. So what I have to do if I don't already have succeeded to compile the document with the images in it to get a separate pdf for each image, is to skip including the pdf page, compile the document so all image files are generated as pdf, and then include the page again and recompile. – StrawberryFieldsForever Dec 23 '13 at 10:43
6

To make a smooth and simple shading between red and blue (via orange, yellow, green and cyan), you can use the Hsb color model (provided by xcolor package):

\documentclass[tikz]{standalone}
\begin{document}
\begin{tikzpicture}
  \foreach \x in {0,...,9}{
    \foreach \y in {0,...,9}{
      \pgfmathtruncatemacro{\myhue}{(\y*10+\x)*2.5}
      \definecolor{col}{rgb:Hsb}{\myhue,1,1}
      \draw[fill=col] (\x,\y) rectangle ++(1,1);
    }
  }
\end{tikzpicture}
\end{document}

Or (with another syntax):

\documentclass[tikz]{standalone}
\begin{document}
\begin{tikzpicture}
  \colorlet{redhsb}[hsb]{red}%
  \colorlet{bluehsb}[hsb]{blue}%
  \foreach \x in {0,...,9}{
    \foreach \y in {0,...,9}{
      \pgfmathtruncatemacro{\rat}{\y*10+\x}
      \colorlet{col}[rgb]{bluehsb!\rat!redhsb}
      \draw[fill=col] (\x,\y) rectangle ++(1,1);
    }
  }
\end{tikzpicture}
\end{document}

enter image description here

This method can be used with a large number of small squares (here, 100 x 100):

\documentclass[tikz]{standalone}
\begin{document}
\begin{tikzpicture}
  \colorlet{redhsb}[hsb]{red}%
  \colorlet{bluehsb}[hsb]{blue}%
  \foreach \x in {0,...,100}{
    \foreach \y in {0,...,100}{
      \pgfmathtruncatemacro{\rat}{(cos(\x*4)+sin(\y*4))*25+50}
      \colorlet{col}[rgb]{bluehsb!\rat!redhsb}
      \draw[fill=col,line width=.1pt] (\x*.1,\y*.1) rectangle ++(.1,.1);
    }
  }
\end{tikzpicture}
\end{document}

enter image description here

|improve this answer|||||
  • Sweet. I could have used this method if I knew it existed. But since I also visualize the simulation in my program, I calculate the rgb colors there, and then it was simplest to just print those rgb colors to the output file. But I will consider using this method if I do something similar in LaTeX again. – StrawberryFieldsForever Dec 22 '13 at 0:53

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.