[Tex/LaTex] Tikz: Color Option For Edges Without Effect

colortikz-pgf

I've just seen this representation of pi and wanted to create something similar.

Minimal example

Create data.csv:

#!/usr/bin/env python

"""Create a data.csv file."""

import csv

try:
    # import version included with old SymPy
    from sympy.mpmath import mp
except ImportError:
    # import newer version
    from mpmath import mp

mp.dps = 1000  # set number of digits
pi = mp.pi
print(pi)

# Split pi in groups of two digits
pi = str(pi)[2:]
split_pi = []
for i in range(0, len(pi), 2):
    part = pi[i:i + 2]
    if len(part) != 2:
        continue
    split_pi.append(part)

# Representation of pi
data = [("x", "y", "color")]  # header
for e1, e2 in zip(split_pi, split_pi[1:]):
    tuple_date = (int(e1), int(e2), "c{}".format(int(int(e1) / 10)))
    data.append(tuple_date)

# Write data to csv
with open('data.csv', 'w') as fp:
    writer = csv.writer(fp, delimiter=',')
    writer.writerows(data)

Create graph:

\documentclass{standalone}
\usepackage{amssymb}
\usepackage{tikz}
\usepackage[utf8]{inputenc}
\usepackage{csvsimple}
\usepackage{xcolor}

\definecolor{c0}{HTML}{5A311D}
\definecolor{c1}{HTML}{E18B4E}
\definecolor{c2}{HTML}{4A1776}
\definecolor{c3}{HTML}{C966DA}
\definecolor{c4}{HTML}{04676C}
\definecolor{c5}{HTML}{0CE7E1}
\definecolor{c6}{HTML}{004692}
\definecolor{c7}{HTML}{0082FF}
\definecolor{c8}{HTML}{355128}
\definecolor{c9}{HTML}{DF1C24}

\begin{document}
\newcommand{\distance}{6}

\begin{tikzpicture}
    \foreach \a in {0,1,...,100}{
        \node[draw=none](\a) at (\a/100*360: \distance) {} ;
    }
    \csvreader[ head to column names]%
                {data.csv}{}{%
    \path (\x) edge [bend right, \color] (\y);
    }
\end{tikzpicture}
\end{document}

Image:

enter image description here

Question

Why are all edges black and how can I fix it? (It's ok for me to adjust the Python code)

Edit: The CSV file starts like

x,y,color
14,15,c1
15,92,c1
92,65,c9
65,35,c6
35,89,c3
89,79,c8
79,32,c7
32,38,c3
38,46,c3
46,26,c4

Best Answer

I know basically nothing about TikZ, but your main stuff happens in this line:

\path (\x) edge [bend right, \color] (\y)

where \x, \y and \color are defined by the csvsimple package, based on the headers in the CSV file. Well, think about it: if a particular line has 14,15,c1 (as the first line does), then your above expression expands to

\path (14) edge [bend right, c1] (15)

in which there is nothing letting TikZ know that it should interpret c1 as the colour c1. So simply replacing that line with the following works:

\path (\x) edge [bend right, color=\color] (\y);

and gives this image:

colourful transitions

which solves your colour problem.

An interesting challenge might be to:

  • implement all of this in Lua code, without needing a Python script

  • more closely match the original image, by not having everything go to the same point.


Edit: Pursuing the above: first I found out how the original image mentioned in the question was generated. The method is given here, here and especially here (also here but it's hard to find on the page):

Cristian Ilies Vasile had the idea of representing the digits of π as a path traced by links between successive digits. Each digit is assigned a segment around the circle…

It turns out we can do all this in code that we can compile with just lualatex, without needing an external CSV data file generated with Python. (I incorporated the suggestion by Torbjørn T. of using \coordinate rather than \node, and changed the positions.)

\documentclass{standalone}
\usepackage{tikz}
\usepackage{xcolor}
\usepackage{luacode}

\definecolor{c0}{HTML}{5A311D}
\definecolor{c1}{HTML}{E18B4E}
\definecolor{c2}{HTML}{4A1776}
\definecolor{c3}{HTML}{C966DA}
\definecolor{c4}{HTML}{04676C}
\definecolor{c5}{HTML}{0CE7E1}
\definecolor{c6}{HTML}{004692}
\definecolor{c7}{HTML}{0082FF}
\definecolor{c8}{HTML}{355128}
\definecolor{c9}{HTML}{DF1C24}

\begin{document}
\newcommand{\distance}{6}

\begin{tikzpicture}
  \foreach \digit in {0,1,...,9}{
    \foreach \position in {0,1,...,1000}{
      % Want to allocate range [36d .. 36(d+1)) to digit, but use only say 90% of the range.
      \pgfmathsetlengthmacro{\angle}{\digit/10*360 + 0.9*\position/1000/10*360}
      \coordinate (\digit-\position) at (\angle: \distance);
    }
  }
  \begin{luacode}
    dofile("pidigits.lua")
    function print_edges()
        local position = -1   -- Starting at -1 because pi_digits yields 0314159265...
        local previous = nil
        for digit in coroutine.wrap(pi_digits) do
            if position >= 1 then
                tex.print(string.format("\\path (%d-%d) edge [bend right, color=c%d] (%d-%d);",
                                        previous, position-1, previous, digit, position))
            end
            previous = digit
            position = position + 1
        end
    end
    print_edges()
  \end{luacode}
\end{tikzpicture}
\end{document}

where the file pidigits.lua (which I could have inlined into the same file, but prefer to keep separate) is the following that simply returns the digits of π:

function pi_digits()
    -- Spigot algorithm by Rabinowitz and Wagon:
    -- http://www.cecm.sfu.ca/~jborwein/Expbook/Manuscript/Related%20files/spigot.pdf
    -- The idea of the algorithm: we can say that
    -- pi = 3 + (1, 4, 1, 5, 9, ...) in base (1/10, 1/10, 1/10, 1/10, 1/10, ...)
    -- Similarly, from a well-known formula,
    -- pi = 2 + (2, 2, 2, 2, 2, 2, ...) in base (1/3, 2/5, 3/7, 4/9, 5/11, 6/13,...)
    -- So to get the digits of pi, we just have to convert to the familiar base.

    local n = 1000  -- The number of digits we want.
    local len = math.floor(10 * n / 3) + 1  -- A value high enough (see Gibbons)
    local a = {}  -- Holds the number pi in base C. (Later: pi * 10^k after k steps.)
    for j = 1, len do
        a[j] = 2
    end
    local nines = 0
    local predigit = 0
    for k = 1, n do
        local carry = 0  -- We're about to multiply by 10
        for i = len, 1, -1 do
            local x = 10 * a[i] + carry * i
            a[i] = math.fmod(x, 2 * i - 1)
            carry = math.modf(x / (2 * i - 1))
        end
        a[1] = math.fmod(carry, 10)
        carry = math.modf(carry / 10)
        if carry < 9 then
            coroutine.yield(predigit)
            for k = 1, nines do
                coroutine.yield(9)
            end
            nines = 0
            predigit = carry
        elseif carry == 9 then
            nines = nines + 1  -- Too early to know what digits to print.
        else  -- If we got here, it means carry = 10
            coroutine.yield(predigit + 1)
            for k = 1, nines do
                coroutine.yield(0)
            end
            nines = 0
            predigit = 0
        end
    end
    coroutine.yield(predigit)  -- The remaining digit, let's not throw it away.
end

With this, we can get something like the following image: flow of 1000 digits of π at widths 0.9

(Or we could use a random stream of digits instead of the digits of π, and get an essentially similar image.)