Счетные карточки Г. Домана от 1 до 100

Однажды возникла у меня задача сгенерировать документ, на каждой странице которого должны были быть последовательно  размещены круги яркого цвета от  1 до 100. Т.е. документ должен содержать 100 страниц: на первой странице один залитый круг, на втором — два, на третьем — три и так далее до 100. Единственное требование, чтобы все круги размещались случайным и независимым от предыдущих "страниц" образом.

Вручную "решать" такую задачу при помощи Word'а не хотелось — как то это совсем не красиво, "учиться" писать специальные макросы для Word, тем более, что я стараюсь не использовать ни Word, ни Oowriter (Open/LibreOffice) при написании текстов, также особого интереса не представляло. Было решено реализовать генерацию 100 страничного pdf-файла, используя  Python, Latex и Tikz.

Tikz макропакет Latex, позволяющий существенно упростить процесс "рисования" в системе Latex. Его функционал весьма широк и вполне достаточен для решения поставленной задачи не прибегая к сторонним инструментам, типа Python. В то время, однако, я осваивал Python, планируя "пересесть" на него с MatLab. Поэтому было решено в качестве упражнения реализовать генерацию latex-файла на Python. Впоследствии из latex-файла создавался 100 страничный pdf-документ.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import numpy as np


def checkplace(x, y, X, Y, rad):
    '''Check for empty place.'''
    if X==[] or Y==[]:
        return False
    X1=np.array(X)
    Y1=np.array(Y)
    xb1=X1-rad
    xb2=X1+rad
    yb1=Y1-rad
    yb2=Y1+rad
    xin=((x+rad>xb1)&(x+rad<xb2))|((x-rad<xb2)&(x-rad>xb1))
    yin=((y+rad>yb1)&(y+rad<yb2))|((y-rad<yb2)&(y-rad>yb1))
    return np.any(xin&yin)


bf=open('circ.tex','w+')

sizex=9.0
sizey=12.0
rad=0.7

stack=[]
for k in xrange(1,101,1):
    X=[]
    Y=[]
    count=0
    while count<k:
        x=2*(np.random.rand()-0.5)*sizex
        y=2*(np.random.rand()-0.5)*sizey
        if ~checkplace(x,y,X,Y,rad):
            X.append(x) #save current circle position 
            Y.append(y) #for next empty place calculation
            count+=1
    print '------------k=',k       
    print X,Y
    bf.write(r'%'+'----------------%s-------------------\n'%k)
    bf.write(r'\clearpage\newpage'+'\n')
    bf.write(r'\begin{tikzpicture}[remember picture,overlay]'+'\n')
    for a,b in zip(X,Y):
        bf.write('\draw [scale=1,fill=red] ($(current page.center)+(%1.3gcm,%1.3gcm)$) circle (%1.3gcm);\n'%(a,b,rad))
    bf.write(r'\end{tikzpicture}'+'\n')

bf.close()  

Важным моментом при генерации размещений кругов было остуствие их наложений. Для этого размещение кругов на каждой отдельно взятой странице производилось последовательно,  при этом положения уже размещенных кругов запоминалось и исходя из этого выбиралось свободное место для вновь добавляемого круга.

По результатам работы скрипта генерировался latex-файл следующего вида:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
%----------------1-------------------
\clearpage\newpage
\begin{tikzpicture}[remember picture,overlay]
\draw [scale=1,fill=red] ($(current page.center)+(-5.89cm,-7.17cm)$) circle (0.7cm);
\end{tikzpicture}
%----------------2-------------------
\clearpage\newpage
\begin{tikzpicture}[remember picture,overlay]
\draw [scale=1,fill=red] ($(current page.center)+(-1.45cm,-5.46cm)$) circle (0.7cm);
\draw [scale=1,fill=red] ($(current page.center)+(0.409cm,6.79cm)$) circle (0.7cm);
\end{tikzpicture}
%----------------3-------------------
\clearpage\newpage
\begin{tikzpicture}[remember picture,overlay]
\draw [scale=1,fill=red] ($(current page.center)+(-2.78cm,-1.64cm)$) circle (0.7cm);
\draw [scale=1,fill=red] ($(current page.center)+(-1.57cm,-10.8cm)$) circle (0.7cm);
\draw [scale=1,fill=red] ($(current page.center)+(0.339cm,-0.168cm)$) circle (0.7cm);
\end{tikzpicture}
%----------------4-------------------
\clearpage\newpage
\begin{tikzpicture}[remember picture,overlay]
\draw [scale=1,fill=red] ($(current page.center)+(7.4cm,-4.8cm)$) circle (0.7cm);
\draw [scale=1,fill=red] ($(current page.center)+(1.29cm,-5.37cm)$) circle (0.7cm);
\draw [scale=1,fill=red] ($(current page.center)+(-2.03cm,6.99cm)$) circle (0.7cm);
\draw [scale=1,fill=red] ($(current page.center)+(-8.07cm,4.39cm)$) circle (0.7cm);
\end{tikzpicture}
%----------------5-------------------
\clearpage\newpage
\begin{tikzpicture}[remember picture,overlay]
\draw [scale=1,fill=red] ($(current page.center)+(0.681cm,-4.62cm)$) circle (0.7cm);
\draw [scale=1,fill=red] ($(current page.center)+(-4.57cm,-5.78cm)$) circle (0.7cm);
\draw [scale=1,fill=red] ($(current page.center)+(-5.19cm,-10.4cm)$) circle (0.7cm);
\draw [scale=1,fill=red] ($(current page.center)+(8.87cm,-0.488cm)$) circle (0.7cm);
\draw [scale=1,fill=red] ($(current page.center)+(4.1cm,-2.65cm)$) circle (0.7cm);
\end{tikzpicture}
%----------------6------------------- etc

Сгенерированный latex-файл интегрировался с основным latex-файлом, собственно, "сборка" которого создавала pdf-документ.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
\documentclass[12pt,a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage[russian]{babel}
\usepackage{amsmath,amsfonts}
\usepackage{pgf,tikz,pgfplots}
\usepackage[paperheight=27.94cm, paperwidth=21.59cm, includeheadfoot, top=0.5cm, bottom=0.5cm, left=0.5cm, right=0.5cm]{geometry}

%\usetikzlibrary{decorations}
\usetikzlibrary{decorations.pathreplacing}
\usetikzlibrary{decorations.markings}
\usetikzlibrary{calc}
\usetikzlibrary{shapes,snakes}
%--------------------------------

\pagestyle{empty}

\begin{document}
\include{circ}


\end{document}

Генерация pdf-документа производилась при помощи команды:

pdflatex main.tex

Скачать pdf-файл Счетные карточки Домана от 1 до 100 (490,2 KB)

blog comments powered by Disqus