Курсовой проект
.pdfТермин LaTeX относится только к языку разметки, он не является текстовым редактором. Для того, чтобы создать документ с его помощью, надо набрать tex-файл с помощью какого-нибудь текстового редактора. В принципе, подойдёт любой редактор, но большая часть людей предпочитает использовать специализированные, которые так или иначе облегчают работу по набору текста LaTeX-разметки.
LaTeX имеет следующие достоинства:
1.Имеется несколько стандартных стилей (книга, статья, доклад, письмо), с помощью которых получаются документы очень высокого полиграфического качества.
2.Набирать математические формулы очень просто. Пользователю нужно знать всего несколько команд, которые определяют логическую структуру текста, и почти ничего не надо знать о том, как документ форматируется.
3.Без особых трудностей можно получить сноски, список литературы, оглавление, список таблиц, указатель и т. п., а также простые рисунки.
Недостатки LaTeX:
1.Потребление значительных машинных ресурсов (процессорного времени и дисковой памяти), больших, чем у примитивных текстовых процессоров.
2.При серьезных отклонениях от стандартных стилей документов требуется достаточно сложное программирование.
Библиотека MathJax.
MathJax – это кроссбраузерное решение для отображения математических формул и символов с открытым исходным кодом. Основывается на jax и обладает следующими преимуществами:
1.Высокое качество отображения математических знаков LaTeX и MathML на HTML странице;
2.Поддержка всех современных браузеров, без установки каких-либо расширений или специальных шрифтов
3.Мощное PI.
Кнедостаткам данной библиотеки можно отнести:
1.Увеличение нагрузки на компьютер пользователя.
2.Увеличение веса html-страницы.
10
Описание метода решения поставленной задачи.
Формирование дерева грамматического разбора.
Рассмотрим следущий пример символьно заданной формулы:
x_i = {-b pm sqrt{D}} / {2 * a}
Данной записе соответствует изображение формулы нахождения корней квадратного уравнения:
√
2
Алгоритм получения дерева грамматического разбора:
1.Для получения массива PHP-токенов воспользуемся функцией token_get_all(). Для данного примера, вызов token_get_all(‘<?php $formula=x_i = {-b pm sqrt{D}} / {2 * a} ?>’) даст следующий массив:
1Array
2(
3array(T_OPEN_TAG, '<?php '),
4array(T_VARIABLE, '$formula'),
5'=',
6array(T_STRING, 'x'),
7array(T_STRING, '_'),
8array(T_STRING, 'i'),
9array(T_WHITESPACE, ' '),
10'=',
11array(T_WHITESPACE, ' '),
12'{',
13'-',
14array(T_STRING, 'b'),
15array(T_WHITESPACE, ' '),
16array(T_STRING, 'pm'),
17array(T_WHITESPACE, ' '),
18array(T_STRING, 'sqrt'),
19'{',
20array(T_STRING, 'D'),
21'}',
22'}',
23array(T_WHITESPACE, ' '),
24'/',
25array(T_WHITESPACE, ' '),
26'{',
27array(T_LNUMBER, '2'),
28array(T_WHITESPACE, ' '),
29'*',
30array(T_WHITESPACE, ' '),
31array(T_STRING, 'a'),
32'}',
33array(T_WHITESPACE, ' '),
34array(T_CLOSE_TAG, '?>')
35)
2.Обходим массив с четвёртого элемента до предпоследнего:
2.1.Если элемент является массивом, то заменяем его на содержимое его второго элемента.
11
2.2.Если перед элементом с символом – находится элемент с одним из символов ^, _, *, /, +, (, то объединяем символ – с содержимым следующего элемента.
2.3.Знаки неравенств, состоящие из двух символов: <=, != ( или <>),
>=, заменяем их буквенными эквивалентами le, ne и ge соответственно.
3.После всех преобразований удаляем пустые, первые три ('<?php ', '$formula', '=') и последний (T_CLOSE_TAG) элементы массива.
Для данного примера в результате получаем массив:
array('x', '_', 'i', '=', '{', '-', 'b', 'pm', 'sqrt', '{', 'D', '}', '}', '/', '{', '2', '*', 'a', '}');
4.Для каждого элемента массива создаём экземпляр объекта Expression_text, присвоив его свойству $text значение соответствующего элемента массива.
5.Далее конструктор класса Expression_ ath преобразуем сформированную последовательность объектов:
5.1.Все объекты массива Expression_text, содержащие открывающиеся скобки (выводимые и не выводимые) заменяются экземплярами объекта Expression_math, в содержимое узлов которых помещаются все объекты вплоть до закрывающей скобки или конца массива.
5.2.Далее особым образом обрабатываются все функции, то есть экземпляры объекта Expression_text, содержащие строки: sqrt, vec,
int, int2, int3, oint, prod, su , bigcup, bigcap, deli , li , root или matrix. Назначения и требуемые графические интерпретации перечисленных функций см. в Приложении 1.
1class Expression_math extends Expression
2{
3var $nodes;
4
5function Expression_math($exp)
6{
7$this->nodes = $exp;
8$this->nodes = $this->parse();
9}
10
11function parse()
12{
13if(count($this->nodes) <= 3)
14 |
return $this->nodes; |
15$ret = array();
16$parentheses = array();
17for($i = 0; $i < count($this->nodes); $i++)
18{
19 |
$node_text = $this->nodes[$i]->text; |
20 |
if($node_text == '(' || $node_text == '{') |
21 |
array_push($parentheses, $i); |
22 |
elseif($node_text == ')' || $node_text == '}') |
23 |
{ |
24 |
$pos = array_pop($parentheses); |
25 |
if(count($parentheses) == 0) |
26 |
{ |
|
12 |
27 |
$sub = array_slice($this->nodes, $pos + 1, $i - $pos - 1); |
28 |
if($this->nodes[$i]->text == ')') |
29 |
$ret[] = new Expression_math(array( |
30 |
new Expression_text("("), |
31 |
new Expression_math($sub), |
32 |
new Expression_text(")")) |
33 |
); |
34 |
else |
35 |
$ret[] = new Expression_math($sub); |
36 |
} |
37 |
} |
38 |
elseif(count($parentheses) == 0) |
39 |
$ret[] = $this->nodes[$i]; |
40 |
} |
41 |
|
42 |
$functions = array( |
43 |
array('sqrt', 1), |
44 |
array('vec', 1), |
45 |
array('int', 3), |
46 |
array('int2', 3), |
47 |
array('int3', 3), |
48 |
array('oint', 3), |
49 |
array('prod', 3), |
50 |
array('sum', 3), |
51 |
array('bigcup', 3), |
52 |
array('bigcap', 3), |
53 |
array('delim', 3), |
54 |
array('lim', 2), |
55 |
array('root', 2), |
56 |
array('matrix', 3) |
57 |
); |
58 |
|
59 |
foreach($functions as $function) |
60 |
$ret = $this->parse_function($ret, $function[0], $function[1]); |
61 |
|
62return $ret;
63}
64
65function parse_function($nodes, $function, $numarg)
66{
67if(count($nodes) <= $numarg + 1)
68 |
return $nodes; |
69$ret = array();
70for($i = 0; $i < count($nodes); $i++)
71{
72 |
if($i < count($nodes) - $nbarg && $nodes[$i]->text == $function) |
73 |
{ |
74 |
$a = array(); |
75 |
for($j = $i; $j <= $i + $numarg; $j++) |
76 |
$a[] = $nodes[$j]; |
77 |
$ret[] = new Expression_math($a); |
78 |
$i += $numarg; |
79 |
} |
80 |
else |
81 |
$ret[] = $nodes[$i]; |
82}
83return $ret;
84}
85
86// ...
87}
Врезультате формируется дерево грамматического разбора [1, стр. 82]: 1. Корень и узлы дерева всегда являются экзмеплярами класса
Expression_math (нетерминалы).
2. Все листья являются экземплярами класса Expression_text (терминалами).
13
3. Все узлы упорядочены слева направо.
|
|
|
|
|
|
|
|
|
|
Expression_math |
|
|
|
|
|
|
|
|
|||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||
|
|
|
[0] |
|
|
[1] |
|
|
|
|
|
|
|
|
[2] |
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Expression_math |
|
|
Expression_text |
|
|
|
|
|
|
Expression_math |
|
|
|||||||||
|
|
|
|
|
|
|
|
|
|
|
|
$text = ‘i’ |
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
[0] |
|
[1] |
|
|
[2] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Expression_text |
|
|
|
Expression_text |
Expression_text |
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||||
|
$text = ‘x’ |
|
|
|
$text = ‘_’ |
|
$text = ‘i’ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[2] |
|
|
|
|
|
|
|
|
|
|
[0] |
|
|
[1] |
|
|
|
|
|
|
|
|
|
|
|
|
|
||||||||
|
|
|
|
|
|
Expression_math |
|
|
|
Expression_text |
|
|
|
|
Expression_math |
|
|
||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
$text = ‘/’ |
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[1] |
|
|
[2] |
|||||
|
|
|
|
|
|
|
|
|
[0] |
|
|
|
|
|
|
|
|
||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
Expression_text |
|
|
|
Expression_text |
|
Expression_text |
||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$text = ‘2’ |
|
|
|
$text = ‘*’ |
|
$text = ‘a’ |
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
|
[0] |
|
[1] |
|
|
[2] |
|
|
|
|
|
|
[3] |
|
|
|
|
|
|
|
|
|
|
||||
|
Expression_text |
|
|
|
Expression_text |
|
Expression_text |
|
|
Expression_math |
|
|
|||||||||||||||
|
$text = ‘-’ |
|
|
|
$text = ‘b’ |
|
$text = ‘pm’ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
[0] |
|
|
|
|
|
|
|
[1] |
|
|
|
|
||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
|
Expression_text |
|
|
|
|
Expression_math |
|
|
||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
$text = ‘sqrt’ |
|
|
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[0] |
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Expression_text |
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$text = ‘D’ |
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Рис. 3. Дерево грамматического разбора формулы нахождения корней квадратного уравнения.
Для упрощения ориентирования в объектно-ориентированном представлении результатов работы алгоритмов, приведём описание структуры классов с помощью UML диаграммы классов1.
1 В UML (англ. Unified Modeling Language – унифицированный язык моделирования) диаграмма классов является типом диаграммы статической структуры. Она описывает структуру системы, показывая её классы, их атрибуты и операторы, а также взаимосвязи этих классов.
14
Expression
#text: string #image: resource #base_verticale: int
Expression_text
#desine(size: int = 14) : void
Expression_math
+nodes: array
+parse() : array
+parse_function(nodes: array, function: string, numargs: int) +desine(size: int = 14) : void
-desine_expression(size: int = 14) : void -desine_fration(size: int = 14) : void -desine_degree(size: int = 14) : void -desine_indicate(size: int = 14) : void -desine_sqrt(size: int = 14) : void -desine_root(size: int = 14) : void -desine_grandoperations(size: int, op: string) : void -desine_matrix(size: int = 14) : void -desine_vec(size: int = 14) : void
-desine_lim(size: int = 14) : void
Создание изображения формулы по дереву грамматического разбора.
Пусть N – число дочерних узлов корневого объекта Expression_math сформированного дерева грамматического разбора. Рассмотрим следующие случаи:
1.N = 1
Принимаем единственный узел за корневой и возвращаемся к пункту 1. Значение базовой вертикали для текущего корневого объекта принимает значение базовой вертикали своего узла.
2.N = 2
Вэтом случае, возможно, что текущий корневой объект содержит узлы одной из двух функций одного аргумента: sqrt или vec. Проверяем первый (левый) узел и если он равен sqrt или vec, то передаём управление методу соответствующей функции (см. далее). Иначе содержимое узлов является простым текстом (обрабатываем соответствующим методом).
15
3.N = 3
Аналогично предыдущему пункту рассматриваются функции с двумя аргументами: lim и root. Если узлы не соответствуют данным функциям, центральный узел сравнивается с символами ^, /, _, которые также влияют на положение элементов на формуле. Иначе содержимое узлов является простым текстом.
4.N = 4
Аналогично рассматриваются функции трёх аргументов: int, int2, int3, oint, prod, sum, bigcap, bigcup, delim и matrix или root. Иначе содержимое узлов является простым текстом.
5.N > 4
Всилу отсутствия функций от четырёх и более аргументов содержимое всех узлов является простым текстом.
Витоге получаем десткриптор результирующего изображения, который с помощью функции ImagePNG() библиотеки GDLib сохраняем в файл.
Создание изображений элементов формулы.
Отрисовка каждой из функций требует собственного, часто уникального подхода. Чтобы не перегружать текст работы, подробно рассмотрим только алгоритм отрисовки квадратного корня.
Пусть содержимое свойства $nodes экземпляра класса Expression_math представляет собой массив, соответствующий символьной записи sqrt{D}:
1Array
2(
3[0] => Expression_text Object
4(
5[text] => ’sqrt’
6[image] =>
7[base_verticale] =>
8)
9
10[1] => Expression_math Object
11(
12[nodes] => Array
13(
14 |
[0] => Expression_text Object |
15 |
( |
16 |
[text] => ’D’ |
17 |
[image] => |
18 |
[base_verticale] => |
19 |
) |
20)
21[image] =>
22[base_verticale] =>
23)
24)
Алгоритм формирования изображения функции квадратного корня:
16
1.Создание изображения аргумента $nodes[1], который может иметь сложную структуру и сохранение его дескриптора в свойстве $nodes[1]->image. Также в процессе отрисовки аргумента формируется значение свойства базовой вертикали для аргумента $nodes[1]->base_verticale.
1Array
2(
3[0] => Expression_text Object
4(
5[text] => ’sqrt’
6[image] =>
7[base_verticale] =>
8)
9
10[1] => Expression_math Object
11(
12[nodes] => Array
13(
14 |
[0] => Expression_text Object |
|
|
15 |
( |
16 |
[text] => ’D’ |
17 |
[image] => Resource id #12 |
18 |
[base_verticale] => 10 |
19 |
) |
|
20)
21[image] => Resource id #12
22[base_verticale] => 10
23)
24)
2.Создание изображения символа квадратного корня √ высотой, равной высоте изображения аргумента.
3.Определяется дескриптор пустого изображения: высота равна большей из высоты изображения аргумента и высоты изображения корня, а ширина – сумме ширины изображений аргумента и корня.
4.Установка прозрачности будущего изображения.
5.Копирование и позиционирование составных частей (корня и аргумента) на «холсте» результирующего изображения.
6.Рисование верхней линии корня.
Реализация данного алгоритма на языке PHP как метода класса Expression_math осуществляется с помощью библиотеки GDLib. Данная библиотека позволяет создавать динамические изображения: программных изображений или наложения графики на уже существующее изображение [4, стр. 8 4].
17
1class Expression_math extends Expression
2{
3 |
|
4 |
// ... |
5 |
|
6 |
private function dessine_sqrt($size = 14) |
|
|
7{
8$this->nodes[1]->dessine($size);
10$arg_image = $this->nodes[1]->image;
11$arg_basev = $this->nodes[1]->base_verticale;
12$arg_width = imagesx($arg_image);
13$arg_height = imagesy($arg_image);
15$root_image = affiche_symbol('_racine', $arg_height + 2);
16$root_width = imagesx($root_image);
17$root_height = imagesy($root_image);
19$width = $root_width + $arg_width;
20$height = max($arg_height, $root_height);
21$image = ImageCreate(max($width, 1), max($height, 1));
23$noir = ImageColorAllocate($image, 0, 0, 0);
24$blanc = ImageColorAllocate($image, 255, 255, 255);
25$blanc = imagecolortransparent($image, $blanc);
26ImageFilledRectangle($image, 0, 0, $width - 1, $height - 1, $blanc);
28ImageCopy($image, $root_image, 0, 0, 0, 0, $root_width, $root_height);
29ImageCopy($image, $arg_image, $root_width, $height - $arg_height, 0,
30 |
0, $arg_width, $arg_height); |
31 |
|
|
|
32imagesetthickness($image, 1);
33imageline($image, $root_width-2, 2, $root_width + $arg_width + 2, 2, $noir);
35$this->base_verticale = $height - $arg_height + $arg_basev;
36$this->image = $image;
37}
38}
18
Разработка веб-приложения.
Для эффективного тестирования и последующей демонстрации работы вышеописанного алгоритма было разработано веб-приложение, формирующее графические изображения введённых пользователем формул. В процессе реализации алгоритма это позволило быстро отлавливать любые ошибки и недоччёты в работе алгоритма.
Также приложение было дополнено инструментами для фиксации ошибочных результатов его работы. В случае публикации данного приложения в сети это позволит всем его пользователям принять участие в его доработке.
Рис. 4. Внешний вид веб-приложения во время работы.
При разработке были использованы каскадные таблицы стилей CSS, язык программирования JavaScript и шаблонизатор Smarty.
19