ISSN 1991-3087
Рейтинг@Mail.ru Rambler's Top100
Яндекс.Метрика

НА ГЛАВНУЮ

Программное обеспечение для создания рисунков в редакторе Latex

 

Беженцев Роман Вадимович,

студент Комсомольского-на-Амуре государственного технического университета.

 

Данная работа посвящена разработке программного обеспечения, предназначенного для облегчения создания рисунков в среде LaTeX. Здесь описываются проблемы рисования изображений стандартными средствами LaTeX и основные принципы работы программы PaintTeX.

 

Введение

 

Как известно, для получения пользователем изображения в среде LaTeX предусмотрены команды, которые после компиляции текста пользователя выглядят в виде композиции примитивов, составляющих вместе готовый рисунок. О том, как создавать динамические рисунки писал Francesc Sunol [Francesc Sunol].

Проблема рисования в LaTeX, а также мотивация рисовать в нём, а не вставлять готовое изображение, хорошо описаны в диссертации Jie Xiao [Jie Xiao]. Поскольку процесс создания рисунков в редакторе LaTeX не является WYSIWYG («What You See Is what You Get» - «что Вы видите, то и получите»), а сводится к ручному набору команд вывода графики на языке TeX, пользователю приходится лишь представлять, как будет выглядеть готовый рисунок, а также приблизительно подбирать координаты опорных точек.

В данной работе описывается разработанное автором программное обеспечение PaintTeX, призванное для решения этой проблемы. Оно было разработано на языке C++ и WinAPI, с применением методов многопоточной обработки данных, что гарантирует высокую производительность работы программы. Для упрощения создания рисунков другими авторами также разрабатывались программные обеспечения Graphviz [Jie Xiao] для рисования графов, Drawlets [Jie Xiao] для рисования произвольных рисунков, а также FeynEdit [Hahn T.] и JaxoDraw [Binosi D.] для рисования диаграмм Фейнмана.

 

Вывод прямолинейного отрезка

 

Для вывода прямолинейного отрезка или вектора в тексте пользователя, помимо координаты опорной точки, приходится указывать ещё и угол наклона с помощью отношения ширины к высоте. В языке TeX команда вывода отрезка выглядит следующим образом:

\put(60,50){\line(1,-2){20}}

где (60,50) – координаты начальной точки отрезка, (1,-2) - угол наклона в виде соотношения длинны к высоте, 20 - длина проекции на ось $ОХ$. Значения в отношении, задающем наклон, не должны превосходить 6 по абсолютной величине у отрезков, и 4 у векторов, а также не должны иметь общих делителей, кроме 1. Подробности можно найти в книгах С. М. Львовского [Львовский С. М.] и Д. Е. Кнута [Кнут Д. Е.].

Созданное автором программное обеспечение PaintTeX предоставляет WYSIWYG интерфейс для рисования изображения с помощью примитивов, а затем преобразует каждый примитив в соответствующую команду вывода графики на языке TeX. Например, чтобы нарисовать изображение, показанное на рисунке 1, нужно долгое время рассчитывать координаты опорных точек и другие параметры команд вывода графики для каждого примитива, или подгонять их приблизительно. Данный рисунок был нарисован в программе PaintTeX, код вывода следующий:

\begin{picture}(215,283)

\qbezier(99,172)(105,172)(112,172)

\qbezier(112,172)(108,174)(105,175)

\qbezier(112,172)(108,171)(105,169)

\qbezier(63,193)(82,170)(102,147)

\qbezier(102,147)(100,151)(99,155)

\qbezier(102,147)(98,149)(95,151)

\qbezier(0,14)(111,14)(209,14)

\qbezier(209,14)(205,16)(202,17)

\qbezier(209,14)(205,13)(202,11)

\qbezier(168,22)(89,129)(8,234)

\qbezier(8,22)(93,22)(168,22)

\qbezier(8,234)(8,128)(8,22)

\qbezier(0,13)(0,145)(0,276)

\qbezier(0,276)(-1,273)(-3,269)

\qbezier(0,276)(1,273)(3,270)

\put(64,192){\circle{38}}

\put(101,160){V}

\put(8,2){O}

\put(10,283){Y}

\put(215,0){Х}

\end{picture}

 

Рис. 1. Пример рисунка в LaTeX.

 

Рассмотрим PaintTeX в действии. Пользователь выбирает желаемый примитив и рисует его, указывая координаты опорных точек, по которым программа рисует примитив и сохраняет их в памяти. При сохранении рисунка программа вставляет в файл текст команды вывода примитива с сохранёнными координатами опорных точек. Сложные примитивы выводятся в виде композиции более простых примитивов, например вектор – это 3 прямых отрезка, соеденённых концами в одной точке, что образует стрелку, а прямоугольник – 4 прямых отрезка. Таким методом можно выводить огромное множество фигур, включая и трёхмерные.

 

Принцип работы программы

 

Принцип работы разрабатываемой программы состоит в следующем. При запуске программы появляется окно с меню, панелью инструментов и областью для рисования. Пользователь выбирает примитив на панели инструментов, а затем курсором мыши задаёт координаты опорных точек. Координаты опорных точек сохраняются в экземпляре класса выбранной фигуры и по ним рисуется сам примитив. При выборе пункта меню «Сохранить», программа сохраняет команды вывода примитивов в файл результатов, вставляя необходимые параметры (координаты, радиус) из координат опорных точек.

Под каждый примитив в программе выделяется объект класса для данного примитива. В ходе текущей стадии разработки существует 7 классов: VETREX, LINE, LABEL, BIZE, SQVR, CIRKLE, FISH. Каждый из этих классов является дочерним от базового класса FIGURE. Содержание класса FIGURE следующее:

class FIGURE

{

public:

            POINT *pt;

            FIGURE *nextFig;

            virtual void print() = 0;

};

Благодаря механизму наследования, каждый дочерний класс наследует от базового указатели типов POINT, FIGURE и виртуальную функцию print(). При создании примитива запускается функция инициализации, которая преобразовывает указатель *pt в массив точек необходимой размерности для заданного примитива. То есть, если создаётся отрезок, в конструкторе LINE срабатывает команда pt = new POINT[2], а если прямоугольник - pt = new POINT[4] в конструкторе SQVR. Указатель *nextFig служит для формирования стека примитивов. Благодаря механизму наследования он может указывать на любой класс примитива.

В каждом описании классов примитивов по-своему переопределена функция вывода print(). Эта функция отвечает за вывод команды рисования примитива в текстовый файл, из которого набор этих команд можно копировать в статью и скомпилировать средствами LaTeX. В каждом классе данная функция выводит в файл собственную команду и параметры, содержащиеся в выбранном объекте. Ниже для примера приведено содержание класса примитивов «метка»:

class LABEL : public FIGURE

{

public:

            char *lab;

            void ini (int x, int y, char *str, int len, HDC hdc)

            {

                        pt = new POINT;

                        pt[0].x = x;

                        pt[0].y = y;

                        lab = new char[len+1];

                        strcpy(lab, str);

                        TextOutA(hdc, pt->x, pt->y, lab, strlen(lab));

            }

            LABEL ()

            void print()

            {

            ofile << "\\put(" << pt[0].x - Canv_left << ","         

 << Canv_top - pt[0].y << "){" << lab << "}" << endl;

            }

}*label;

Этот класс содержит указатель *lab, который преобразуется в строку для хранения текста метки, функцию инициализации, которая сохраняет данные в структуре и рисует текст, функцию print(), преобразующую рисунок в команды вывода графики с учётом кадрирования, и указатель *label, отвечающего за работу стека. Функция создания примитива «метка» выглядит следующим образом:

LABEL *new_label(int x, int y, char *str, int len, HDC hdc)

{

            LABEL *label_new = new LABEL;

            label_new->ini(x, y, str, len, hdc);

            if (!labelcount++) label_new->nextFig = 0;

            else label_new->nextFig = label;

            return label_new;

}

Когда пользователь рисует примитив, в данном случае метку, функции создания передаются координаты опорной точки, строка текста, длина строки и дескриптор устройства, куда будет рисоваться текст. Поскольку для каждого нового примитива память выделяется динамически, то для инициализации пришлось использовать функцию инициализации, а не конструктор.

При сохранении команд, программа на каждый класс примитива создаёт отдельный поток. Каждый поток запускает функцию, которая при помощи мютекса синхронизирует вывод каждой команды. Объявление данной функции следующее.

void save(FIGURE *curfig, int counter)

Как видим, аргумент *curfig – указатель на стек примитивов, а counter - их общее количество. Благодаря механизму наследования, каждый класс примитива является классом FIGURE, а значит, для синхронного вывода примитивов любого класса достаточно использовать одну функцию. Так же, благодаря виртуальной функции print(), при помощи curfig->print(); можно обращаться к функции вывода каждого примитива, а программа уже будет знать, какой именно примитив надо вывести в файл.

Математические модели были взяты из книги «Математические основы машинной графики»[Роджерс Д.]. Например, Кривая Безье — параметрическая кривая, задаваемая выражением

где Pi — функция компонент векторов опорных вершин, а bi,n(t) — базисные функции кривой Безье, называемые также полиномами Бернштейна.

Поскольку синтаксис языка TeX позволяет выводить кривые только по трём точкам, формула для их вывода была упрощена.

X = (1-t) * (1-t) * pt[0].x + 2*t*(1-t)*pt[1].x + t*t*pt[2].x;

Y = (1-t) * (1-t) * pt[0].y + 2*t*(1-t)*pt[1].y + t*t*pt[2].y;

где pt[0].x, pt[1].x, pt[2].x - координаты опорных точек по оси OX, а pt[0].y, pt[1].y, pt[2].y - координаты опорных точек по оси OY. При построении кривой, программа с шагом t = t + 0.01 находит точки, расположенные на кривой, и затем соединяет их маленькими отрезками.

 

Проблемы в ходе реализации программного обеспечения.

 

В ходе работы над программным обеспечением появились следующие проблемы. Поскольку значения, отвечающие за наклон в примитивах «отрезок» и «вектор» должны быть целыми, и их количество сильно ограничено, то для наклона примитива существует ограниченное количество углов. В разрабатываемом программном обеспечении пользователь рисует отрезки и векторы, задавая координаты начальной и конечной точки. Конвертировать их координаты в команду вывода на языке TeX не удалось, поэтому для вывода прямых линий было решено использовать кривые Безье, определяя начало линии, середину и конец. Поскольку для вывода кривых Безье не нужно указывать соотношение наклона и длину проекции, то через кривые можно выводить прямые отрезки и векторы под любым углом.

Так же была проблема с определением области рисунка. В LaTeX область рисования указывается вручную, и пользователю, как и примитивы, тоже приходится подбирать приблизительно, определяя, каких размеров будет рисунок. Благодаря автоматической обрезке PaintTeX определяет границы прямоугольника (холста), в котором было нарисовано изображение и обрезает рисунок до нужных размеров, вставляя соответствующие параметры в команду begin{picture}().

Другая проблема - работа с координатами Windows и LaTeX. Поскольку начальной точкой координат в Windows является верхний левый край окна, а в LaTeX нижний левый, при преобразовании рисунка в файл сохранялись координаты Windows, а после компиляции изображение выглядело в зеркальном отображении по вертикали. Теперь PaintTeX при сохранении рисунка учитывает этот нюанс.

 

Литература

 

1.                  Львовский С. М. Набор и вёрстка в системе LaTeX, СПб.: Питер, 2003.

2.                  Роджерс Д., Адамс Дж. Математические основы машинной графики, М.: Мир, 2001.

3.                  Binosi D., Colins J., Kaufhold C., Theussl L. “JaxoDraw: A graphical user interface for drawing Feynman diagrams. Version 2.0 release notes.”, Comput. Phys. Commun. 2008. 17p. Preprint, arXiv:0811.4113v1 [hep-ph], Cornell Univ. http://arxiv.org/abs/0811.4113v1.

4.                  Jie X. Extending Two Drawing Frameworks to Create : Presented in partial Fulfillment of the requirements for the degree of master of computer science, Concordia University Monreal, Quebec, Canada, 2005.

5.                  Hahn T., Lang P. “FeynEdit – a tool for drawing Feynman diagrams”, Munich, 2007. 9p. Preprint, arXiv:0711.1345v1 [hep-ph], Cornell Univ. http://arxiv.org/abs/0711.1345v1.

6.                  Knuth, D. E. The TeXbook, part А series Computers and Typesetting. - Addison-Wesley, 1994.

7.                  Sunol F. Tools for creating LaTeX-integrated graphics and animations under GNU/Linux // The PracTeX Journal, №1 (2010), P. 1-12.

 

Поступила в редакцию 29.08.2013 г.

2006-2019 © Журнал научных публикаций аспирантов и докторантов.
Все материалы, размещенные на данном сайте, охраняются авторским правом. При использовании материалов сайта активная ссылка на первоисточник обязательна.