(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:

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:

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:

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