Сайт Романа ПарпалакаЗаметкиТехнологииПрограммированиеDelphi и EPS

Delphi и EPS

4 декабря 2009 года

Векторная графика

В этой статье мы рассмотрим простой программный способ создания векторных изображений. Иногда возникает необходимость создания картинок (например, построение графиков) для наблюдения каких-нибудь процессов. Если результат нужно наблюдать только на экране, можно ограничиться стандартными средствами языка программирования. Но если картинку нужно приложить к отчету и распечатать, ее приходится, как минимум, экспортировать в растровый файл большого размера.

Разрабатывая одну программу на Delphi, я оказался в похожей ситуации. Сначала я сделал экспорт в BMP размером 10000*10000. Но это не самое красивое и удобное решение, и я захотел перейти к векторным изображениям. Встроенных средств для работы с векторной графикой в Delphi нет. Бесплатных дополнений я не нашел. Мне пришлось написать свою библиотечку.

Проще всего работать с текстовыми векторными форматами. Я выбирал между SVG и EPS. Первый формат новее, поддерживает больше возможностей (например, в нем есть градиенты, в отличие от EPS). Поддержка второго формата шире. Я выбрал именно EPS, поскольку картинки в этом формате можно напрямую использовать в TeX. Но описанный метод можно применить для любого текстового формата.

Еще одно достоинство векторных форматов — их можно просматривать в специальных программах, приближать отдельные области и т. д. При этом у программы, создавшей такой файл, совсем не обязательно делать графический интерфейс. SVG-файлы можно просматривать в Опере (правда, увеличение будет максимум десятикратным), а файлы EPS — в GSView.

Библиотека delphi_eps

Чтение обзора формата EPS и просмотр простых EPS-файлов, созданных в векторном редакторе, показывают, что сгенерировать EPS-файл нетрудно. Мы рассмотрим этот процесс на примере закрашенного многоугольника.

Итак, просматривая простейший EPS-файл, находим, что некоторому многоугольнику (точнее, треугольнику) соответствуют строчки

gsave
1 0 0 setrgbcolor
newpath
141 301 moveto
131 311 lineto
141 321 lineto
141 301 lineto
closepath
fill
grestore

Команда setrgbcolor отвечает за цвет заливки. Три числа — доли красного, зеленого и синего цвета (от 0 до 1). Вершины многоугольника задаются командами moveto и lineto.

Команды вроде setrgbcolor или lineto мы будем хранить в шаблоне — текстовом файле. Вот его фрагмент:

<!-- filled_polygon -->
gsave
*COLOR*
newpath
*BODY*
closepath
fill
grestore
<!-- filled_polygon -->

<!-- lineto -->
*X* *Y* lineto
<!-- lineto -->

<!-- moveto -->
*X* *Y* moveto
<!-- moveto -->

<!-- color -->
*R* *G* *B* setrgbcolor
<!-- color -->

Этот файл легко прочитать (функции explode и str_replace максимально приближены к аналогичным функциям из PHP: первая разбивает строку на части по разделителю, а вторая производит замену одной подстроки на другую):

var
	sa, sa2: TStrings;
	s: string;

...

	s := '';
	sa := TStringList.Create;
	try
		sa.LoadFromFile(filename);
		s := sa.text;
	finally
		sa.Free;
	end;

	s := str_replace(#10, '', s);

	sa2 := explode('<!-- filled_polygon -->', s);
	if sa2.Count <> 3 then
		exit;
	polygon_pattern := trim(sa2[1]) + #13;
	sa2.Free;

	sa2 := explode('<!-- lineto -->', s);
	if sa2.Count <> 3 then
		exit;
	lineto_pattern := trim(sa2[1]) + #13;
	sa2.Free;

	sa2 := explode('<!-- moveto -->', s);
	if sa2.Count <> 3 then
		exit;
	moveto_pattern := trim(sa2[1]) + #13;
	sa2.Free;

Когда шаблоны прочитаны, в них легко вставить настоящие данные:

type

TPointF = record
	x, y: double;
	end;

procedure TEPS.SetFillColor(const r, g, b: double);
begin
	fill_color := color_pattern;
	fill_color := str_replace('*R*', MyFloatToStr(r), fill_color);
	fill_color := str_replace('*G*', MyFloatToStr(g), fill_color);
	fill_color := str_replace('*B*', MyFloatToStr(b), fill_color);
end;

procedure TEPS.FillPolygon(const points: array of TPointF);
var
	i: integer;
	s, current_path: string;
begin
	if Length(points) < 2 then
		exit;

	s := moveto_pattern;
	s := str_replace('*X*', MyFloatToStr(points[0].x), s);
	s := str_replace('*Y*', MyFloatToStr(points[0].y), s);
	current_path := s;

	for i := Length(points) - 1 downto 0 do
		begin
		s := lineto_pattern;
		s := str_replace('*X*', MyFloatToStr(points[i].x), s);
		s := str_replace('*Y*', MyFloatToStr(points[i].y), s);
		current_path := current_path + s;
		end;

	s := str_replace('*BODY*', current_path, polygon_pattern);
	s := str_replace('*COLOR*', fill_color, s);

	current_body := current_body + s;
end;

Переменная fill_color содержит текущий цвет заполнения областей. В переменной current_body собирается всё «тело» EPS-файла.

Теперь достаточно вызвать следующую последовательность команд, чтобы нарисовать красный треугольник, с которого мы и начали рассмотрение:

var
	tr: array[0..2] of TPointF;

...

	eps.SetFillColor(1, 0, 0);

	tr[0].x := 141;
	tr[0].y := 301;
	tr[1].x := 141;
	tr[1].y := 321;
	tr[2].x := 131;
	tr[2].y := 311;

	eps.FillPolygon(tr);

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

В архиве с библиотечкой есть пример использования, более приближенный к реальности. Мне как-то потребовалось нарисовать касательные к параболе, расположенные под равными углами друг к другу. Несколько строчек на Паскале — и нужная картинка уже сохранена в файл. Дальше с ней можно делать всё, что угодно, например, привести ее здесь в качестве примера.

Сгенерировано Delphi EPS

Скачать

delphi_eps.zip, 3 Кб.

Поделиться

Читайте также


Миниатюры на PHP
В ходе разработки и обслуживания сайтов часто возникает необходимость в создании миниатюр — уменьшенных копий изображений. В тексте страницы вместо большой картинки можно поместить такую копию, являющуюся ссылкой на исходный файл.
2007
По мотивам нового движка блога
PHP меня радует такими вещами (хотя заслуги PHP в этом особой нет, это типичный синтаксис C): while ($row = mysql_fetch_row($result))
2007
Простое резервное копирование
Давайте я вам расскажу, как работает резервное копирование (бекап) на моем сервере. Самая ценная информация хранится в базе данных.
2011
Правильная организация RSS
В пользе RSS уже давно никто не сомневается. Я позволю себе сказать пару слов о том, как правильно сделать RSS-канал на вашем сайте.
2007

Оставьте свой комментарий


Формулы на латехе: $$f(x) = x^2-\sqrt{x}$$ превратится в $$f(x) = x^2-\sqrt{x}$$.
Выделение текста: [i]курсивом[/i] или [b]жирным[/b].
Цитату оформляйте так: [q = имя автора]цитата[/q] или [q]еще цитата[/q].
Других команд или HTML-тегов здесь нет.