[Tex/LaTex] drawing patterns based on prime factorization

diagramspstrickstikz-pgf

can someone help me generate code to type the diagram for any given natural number based on its prime factorization such as these diagramsenter image description here

consider the next number 36 and its prime factorization, $36=3^2\cdot 2^2$, writing the primes in descending order… then, start with the pattern for a 3, which is 3 circles 120 degrees apart, now, replace each circle with 3 more circles in the same pattern to account for the $3^2$, this part would look like the pattern for 9 which is also $3^2$. now.. to finish the pattern for 36, we take the pattern for 9 and in each circle replace it with the pattern for 4… that is each of the 9 circles gets replaced with 4 tinier circles arranged in the same pattern as the pattern for 4

Best Answer

(See update at the end for an alternative output)

Done with Lualatex and tikz, simply converting the code from this javascript fiddle

This is a sample result for number 36:

Result

Which was generated with:

\begin{tikzpicture}[y=-1cm]
\draw[red] (0,0) rectangle (5,5);
\primediagram{36}{2.5}{2.5}{2.5};
\end{tikzpicture}

The macro \primediagram receives four parameters. The first one is the number to represent. The remaining three are the center and radius of the diagram to be generated.

A more interesting example:

Result2

Produced by a tikz loop:

\begin{tikzpicture}[y=-1cm]
\foreach \y in {0,...,6} {
  \foreach \x in {0,...,4} {
    \draw(3.1*\x,3.1*\y) rectangle +(3,3);
    \pgfmathtruncatemacro{\number}{\x+5*\y}
    \primediagram{\number}{3.1*\x+1.55}{3.1*\y+1.55}{1.3};
    \node[black!50, below right] at (3.1*\x, 3.1*\y)  {\number};
  }
}
\end{tikzpicture}

The complete code

To produce these figures, three files are required:

primediagram.sty

This one simply defines the tex macro which interfaces with the lua code:

% This is primediagram.sty
\directlua{dofile("primediagram.lua")}
\newcommand{\primediagram}[4]{
    \directlua{draw(#1,#2,#3,#4)}
}

primediagram.lua

This one contains the lua code which actually performs the computations and outputs the required \draw commands for tikz. It is a direct translation to lua of the mentioned javascript code.

-- This is primediagram.lua
local smallfirst = false
local off2 = 0

function circle(cx, cy, s) 
    tex.print(string.format("\\fill (%f, %f) circle(%f);", cx, cy, s))
end

function draw(N, cx, cy, s)
  if N==0 then
    return
  end
  if N==1 then
     circle(cx,cy,s)
  else
    local f, r, d, oy, x, y
    f = primefactor(N)
    if f == 2 then
        oy = 0;
        if N % 4 == 0 then
            f = 4;
            r = 2 * s / (f + 2);
            d = f * s / (f + 2);
        else 
            f = 2;
            r = 0.75 * 2 * s / (2 + 2);
            d = 2 * s / (2 + 2);
        end
    else
        r = 2 * s / (f + 2);
        d = f * s / (f + 2);
        oy = d / 2 * (1 - math.cos(math.pi / f));
    end
    for i = 0, f do
        x = math.sin(math.pi + 2 * math.pi * (i + 0.5) / f + off2);
        y = math.cos(math.pi + 2 * math.pi * (i + 0.5) / f + off2);
        draw(N / f, cx + x * d, cy - y * d + oy, r);
    end
  end
end

local primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 
    47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 
    113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 
    191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 
    263, 269, 271}

function primefactor(N) 
    local ans = N;
    for i,pi in pairs(primes) do
        if N % pi == 0 then
            ans = pi;
            if smallfirst then
                return ans
            end
        end
    end
    return ans
end

The main document

You only need to include the sty package, as for example:

\documentclass{article}
\usepackage{nopageno}
\usepackage[margin=1cm]{geometry}
\usepackage{tikz}
\usepackage{primediagram}

\begin{document}
\begin{tikzpicture}[y=-1cm]
\foreach \y in {0,...,6} {
  \foreach \x in {0,...,4} {
    \draw(3.1*\x,3.1*\y) rectangle +(3,3);
    \pgfmathtruncatemacro{\number}{\x+5*\y}
    \primediagram{\number}{3.1*\x+1.55}{3.1*\y+1.55}{1.3};
    \node[black!50, below right] at (3.1*\x, 3.1*\y)  {\number};
  }
}
\end{tikzpicture}
\end{document}

Which produces the figure already shown.

Update: Rotating sub-groups

If each sub-groups is rotated according to the angle at which it is drawn, the output is different, nicer, and more close to the one shown by the OP. I also removed the black filling of the dots:

New result

This is the new code (only primediagram.lua has to be replaced):

-- This is primediagram.lua
local smallfirst = false
local off2 = 0

function circle(cx, cy, s) 
    tex.print(string.format("\\draw (%f, %f) circle(%f);", cx, cy, s))
end

function draw(N, cx, cy, s, a)
  if N==0 then
    return
  end
  if N==1 then
     circle(cx,cy,s)
  else
    local f, r, d, x, y, off
    off = 0
    a = a or 0
    tex.print(string.format("\\begin{scope}[xshift=%fcm, yshift=-%fcm, rotate=%f]", cx, cy, a))
    f = primefactor(N)
    if f == 2 then
        oy = 0;
        if N % 4 == 0 then
            f = 4;
            r = 2 * s / (f + 2);
            d = f * s / (f + 2);
        else 
            f = 2;
            r = 0.75 * 2 * s / (2 + 2);
            d = 2 * s / (2 + 2);
            off = math.pi/2
        end
    else
        r = 2 * s / (f + 2);
        d = f * s / (f + 2);
    end
    for i = 0, f-1 do
        local angle = math.pi + 2 * math.pi * (i + 0.5) / f + off
        x = math.sin(angle);
        y = math.cos(angle);
        draw(N / f, x * d, y * d , r, angle*180/math.pi+180);
    end
    tex.print("\\end{scope}")
  end
end

local primes = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 
    47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 
    113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 
    191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 
    263, 269, 271}

function primefactor(N) 
    local ans = N;
    for i,pi in pairs(primes) do
        if N % pi == 0 then
            ans = pi;
            if smallfirst then
                return ans
            end
        end
    end
    return ans
end