десктоп,
интерфейс,
обзор
39 коммент.
В этом блоге публикуются заметки и решения, найденные в процессе работы, освоения и жизни в дистрибутиве Debian GNU/Linux.
Благодаря ЛаТеХу научные работники (и не только) получили возможность создавать красивые, уравновешенные документы с чётко просматривающейся структурой. Однако "сфера влияния" ЛаТеХа распространяется только на текст и формулы, а внешний вид графиков и иллюстраций находится полностью на совести автора. И вот здесь-то и начинаются полные разброд и шатания: на некоторые рисунки трудно смотреть без слёз :)
Целью данного поста является продемонстрировать один из способов создания математических графиков гармонично сочетающихся с окружающим их текстом.
Что отличает рисунок гармонично вписывающийся в основной текст от рисунка плохого? Безусловно, содержание, размер и расположение являются важными эстетическими параметрами. Однако наиболее бросающимся в глаза диссонансом, свойственным львиной доле естественно-научных рисунков, являются шрифты. Да-да, именно шрифты... Речь идёт о подписях к осям графиков, о легендах и т.п. Порой создается ощущение, что у автора серьёзные проблемы со зрением: скажем, рядом с текстом набранным Times 12pt вдруг оказывается график с подписями 16-ым шрифтом... Или, ещё лучше, нечитаемым 8-ым... А о соответствии типа шрифта (начертания) между рисунками и текстом и мечтать не приходится!
Итак, наша основная задача: графики, шрифт на которых совпадает по размеру и начертанию с основным шрифтом документа.
Достичь нашей цели можно разными путями. Наиболее общеизвестные из них, в порядке убывания ортодоксальности:
pgfplots
и tikz
,
либо pstricks
.
tikz
.
Более конкретно: например,
matlab2tikz.
Или
tikz терминал для Gnuplot.
Каждый из этих способов имеет свои достоинства и недостатки, обсуждение которых выходит за рамки данного поста. Последний способ является, по мнению автора, оптимальным по параметру "отношение качества результата к затраченному времени". Поэтому, именно ему и будет уделено внимание.
В этом посту мы рассмотрим алгоритм экспорта графиков из MATLAB в LaTeX.
Выбор Матлаба для построения графиков диктуется исключительно личными предпочтениями автора. Аналогичные методы применимы и к другим программам — об этом в самом конце.
Собственно: экспорт графиков из Матлаба в ЛаТеХ. При этом предполагается, что подписи и т.п. на графике должны сочетаться с основным текстом.
Желаемый результат изображен ниже. Специально выбран нестандартный
фонт (Kepler-подобный из kpfonts
вместо стандартного Computer Modern), чтобы подчернуть желаемый эффект.
Матлаб, версии новее 7.6 (release name 2008a).
Кроме того, скрипт
matlabfrag.m.
Его можно скачать самостоятельно, но он (вернее его улучшенная версия)
есть и в архиве с примерами из
этого поста: tinyurl.com/amorua-matlabfrag.
Поместите его в текущую директорию:
туда где будут находиться ваши скрипты для построения графиков.
Поместите туда же файлы
myfigure.m
и mylegend.m
из того же
архива с примерами.
ЛаТеХ. Нам понадобится пакет
pstool
, причём версия 2013/02/11 v1.5 или новее.
В старых версиях пакета будет работать всё, кроме ссылок из картинок
на основной документ, например, на уравнения или номера страниц.
Файл pstool.sty
нужной версии находится в архиве с примерами.
Все примеры можно компилировать либо
pdflatex -shel-escape имя-файла
либо
latex -shell-escape имя-файла
dvips имя-файла
pstopdf имя-файла.ps
но предпочтение должно быть отдано первому варианту.
Если -schell-escape
опция не работает, возможно
в вашей версии ЛаТеХ она называется -enable-write18
.
Здесь всё почти как обычно:
01:
%% example 1
02:
close
all ;clear
all;clc
;03:
%% Интерпретатот текста, по умолчанию,
latex
04:
set
(0,'DefaultTextInterpreter
', 'latex
');05:
% Размер шрифта такой же как в ЛаТеХ-документе
06:
set
(0,'DefaultAxesFontSize',12);07:
set
(0,'DefaultTextFontSize',12);08:
09:
%% Создаём картинку
10:
fig=figure;11:
%% Рисуем графики ...
12:
x=0:0.1:pi;13:
y=sin(x); plot(x,y,'r'); hold on;14:
z=cos(x); plot(x,z,'b');15:
16:
%% Подписи к осям
17:
xlabel
('$t$, s');ylabel
('$l$, m');18:
%% Легенда
19:
l=leg
('$end
\sin
x$','$\cos
x$');20:
21:
%% Дополнительный текст на графике
22:
text
(0.5,-0.5,'$\int
\limits
_0^{2\pi
}\sin
x\
, dx = 0$');23:
text
(3,-0.5,'$\gamma
=\frac
{\alpha
}{\zeta
}$');24:
25:
%% Экспортируем график
26:
matlabfrag
('figs/example1','epspad',[10,10,10,10]);
Теперь по порядку:
latex
установлен как
интерпретатор текста по умолчанию. Благодаря этому, в подписях к осям
(строка 17), легенде (строка 19) и дополнительном
тексте (строки 22, 23) мы можем спокойно использовать ЛаТеХ-комманды.
matlabfrag
из архива с примерами, то устанавливать
размер шрифта не надо — будет автоматически подхвачен текущий размер
из ЛаТеХ-документа).
matlabfrag()
для сохранения графика. Она создаст два файла:
figs/example1.eps и figs/example1.tex
(предполагается, что в текущей директории вы создали
поддиректорию figs/,
куда мы будем складывать все картинки).
Если посмотреть на их содержимое, то станет понятно, как это всё работает.
Как видно из предыдущего рисунка, ЛаТеХ-текст будет вставлен
с помощью команды \psfrag
из одноимённого пакета. Однако,
гораздо удобнее пользоваться им не напрямую, а через интерфейс,
предоставляемый пакетом pstool
. Делается это так: подключаем
пакет pstool
и вставляем картинку командой
\psfragfig{}
.
01:
\documentclass
[12pt]{article}02:
...03:
\usepackage
{pstool}04:
...05:
\begin
{document}06:
...07:
\begin
{figure
}\centering
08:
% вставляем картинку:
09:
% имя файла без расширения!!!
10:
\psfragfig
{figs/example1}11:
\caption
{Example of a Matlab plot.}\label
{fig:ex1}12:
\end
{figure
}13:
...14:
\end
{document}
Обратите внимание, что имя файла картинки без расширения. Если картинка находится не в текущей директории, то нужно указать путь к ней.
Кроме того, размер шрифта в самом первой строке установлен в 12pt, в соответствии с размером шрифта на рисунке в Матлабе.
Компилируем командой
pdflatex -shell-escape имя-тех-файла
Пакет pstool
сделает за нас всю чёрную работу: он сам
сконвертирует на лету связку файлов eps+tex в pdf картинку и вставит её:
он прогонит автоматически каждую картинку через ЛаТеХ, dvips
и pstopdf. Именно для этого и надо указать ключ -shell-escape
при запуске pdflatex
— он разрешает пакету запускать
внешние программы.
Пакет pstool
достаточно разумен: он смотрит на дату создания
файлов картинок и "конвертирует" только изменившиеся со времени последнего
запуска. Чтобы принудительно переконвертировать все картинки, надо указать
опцию process=all
, а именно
Результат наших усилий показан внизу. Как видите, на графике использованы Computer Modern шрифты ЛаТеХа, размер которых совпадает с размером, использованным в тексте (см. подпись к рисунку).01:
...02:
\usepackage
[process=all]{pstool}03:
...
Теперь, когда последовательность действий в достаточной степени понятна из предыдущего примера, можно обратить чуть больше внимания на детали.
Деталь, к matlabfrag()
прямого отношения не
имеющая, но тем не менее...
Наилучшего, с эстетической точки зрения, результата можно
добиться, если вставлять в ЛаТеХ
рисунки в том масштабе, в котором они были нарисованы.
Т.е. рисовать их лучше сразу в нужном размере!
Для этого
создаём в Матлабе простенькую функцию
Если использовать01:
function h=myfigure
(width,height)02:
% создаёт figure с заданной шириной и высотой в см
03:
04:
if
nargin<2% если дана только ширина, использует золотое сечение для высоты
05:
height = width * 2/(1+sqrt(5));% высота=ширина/золотое сечение
06:
end
07:
08:
set
(0,'units','centimeters')09:
scrsz=get(0,'screensize');% размер экрана в см
10:
% положение и размер картинки
11:
position=[scrsz(3)/2-width/2 scrsz(4)/2-height/2 width height];12:
h=figure;13:
set
(h,'units','centimeters')14:
% устанавливаем размер
15:
set
(h,'position',position)16:
% Set screen and figure units back to pixels
17:
set
(0,'units','pixel')18:
set
(h,'units','pixel')
myfigure(10,8)
, то размер картинки
будет 10см на 8см. Если задать только первый аргумент
myfigure(10)
, то функция сама посчитает высоту исходя
из золотого сечения. Файл myfigure.m
находится в архиве
с примерами.
В принципе, необязательно пользоваться
интерпретатором
latex в Матлабе. Функция matlabfrag()
смотрит на
свойство UserData
и, если оно имеет вид
matlabfrag:XXX
, вставляет XXX
в качестве
ЛаТеХ-текста. Например, код ниже делает в точности то же самое, как и
разобранный выше пример
Недостаток этого способа в том, что придётся набирать больше текста. Кроме того, картинка, как её покажет Матлаб, вообще говоря, будет отличаться от той, которую вы увидите в ЛаТеХе.01:
%% example 2
02:
close
all ;clear
all;clc
;03:
%% По умолчанию, интерпретатор текста tex, а не
latex
,04:
% т.е.
set
(0,'DefaultTextInterpreter
', 'tex'); указывать не обязательно05:
06:
%% Создаём картинку
07:
fig=myfigure
(10);08:
%% Рисуем графики ...
09:
x=0:0.1:pi;10:
y=sin(x); plot(x,y,'r'); hold on;11:
z=cos(x); plot(x,z,'b');12:
13:
%% Подписи к осям
14:
xlabel
('t, s', 'UserData','matlabfrag
:$t$, s');15:
ylabel
('l, m', 'UserData','matlabfrag
:$l$, m');16:
%% Легенда
17:
l=my
({'sin x';'cos x'},...leg
end
18:
{'$\sin
x$';$\cos
x$});19:
20:
%% Дополнительный текст на графике
21:
text
(0.5,-0.5,'integral here',...22:
'UserData','matlabfrag
:$\displaystyle
\int
\limits
_0^{2\pi
}\sin
x\
,dx = 0$');23:
24:
text
(3,-0.5,'\gamma
=\alpha
/\zeta
',...25:
'UserData','matlabfrag
:$\displaystyle
\gamma
=\frac
{\alpha
}{\zeta
}$');
Зато такой способ гораздо более гибок. Он позволяет вставлять в текст подписей команды, определённые в ЛаТеХ документе, о которых Матлаб ничего не знает. Например:
использует команду01:
text
(0.5,-0.5,'integral here',...02:
'UserData','matlabfrag
:\myint
see eq.(\ref
{eq:1})');
\myint
, а кроме того, ссылается
на уравнение из основного текста.
Обратите внимание на то, как добавляется UserData к легенде:
здесь для облегчения процесса использована функция
mylegend(легенда,латех_вариант)
.
Она принимает два аргумента, оба cell array. Первый — легенды для
Матлаба, второй — для ЛаТеХа.
01:
function l=my
(lgnds,usrdta_in)leg
end
02:
% функция для создания легенд с ЛаТеХ-вариантами
03:
% пример вызова: l=
my
({'sin(x)';'cos(x)'},{'$\sin x$';'$\cos x$'})leg
end
04:
% возвращает указатель на легенду
05:
l=leg
(lgnds);end
06:
07:
if
nargin>1% если переданы UserData
08:
usrdta=lgnds;09:
for i=1:min(length(usrdta),length(usrdta_in))10:
usrdta{i} = usrdta_in{i};11:
end
12:
13:
lchild=get(l,'children');14:
i_max=length(usrdta);15:
16:
for i=1:i_max17:
i_child=3*i_max-(i-1)*3 ;18:
set
(lchild(i_child),'Userdata',sprintf('matlabfrag
:%s',usrdta{i}));
19:
end
20:
end
21:
end
Дело в том, как Матлаб хранит текстовое содержимое легенд: если легенды две, то их текст будет сохранен в 6-м и 3-м элементах "детей легенды". Если легенды три, то в 9-м, 6-м и 3-м. И так далее.
Файл mylegend.m
может быть найдена в архиве с примерами.
Функция matlabfrag()
настолько сообразительна, что
автоматически подхватит эффекты, такие как вращение текста, центрирование,
цвет, размер и т.п.
Например, такой код
даст эффект, очевидный из картинки ниже.01:
%% example 3
02:
%% Text effects
03:
text
(2.5,-0.5,{'Rotated';'text
'},'Rotation',90);04:
05:
text
(0.5,-0.5,{'Colored';'centered';'italic';'text
'},...06:
'Color','r',...07:
'HorizontalAlignment','center',...08:
'FontAngle','italic');09:
10:
text
(1,-0.5,{'Big Text:';'16pt'},'FontSize',16)
Обратите внимание, что для набора текста в несколько строк
использованны cell arrays, вроде
{'строка один';'строка два'}
.
Этот трюк работает не только для функции text()
, но и для,
например, подписей на осях:
01:
xlabel
({'$t$, s'; 'time to goal'});02:
ylabel
({'$l$, m'; 'distance to goal'});
С русским тоже никаких проблем: просто, набираем подписи и текст на русском. Только не забываем в ЛаТеХ-документ вставить в преамбулу
01:
\usepackage
[utf8]{inputenc}02:
\usepackage
[T2A]{fontenc}03:
\usepackage
[russian]{babel}
Результат:
Иногда хочется заполучить у меток на осях не числа типа 3.14, а что-то посимпатичнее, вроде числа "пи". Делается это просто:
01:
%% example 4
02:
...03:
%% подписи у меток, aka tick marks
04:
set
(gca,'XLim',[0 pi],'XTick',[0 pi/2 pi],...05:
'XTickLabel',{'$0$','$\pi
/2$','$\pi
$'});
Всё остальное — как прежде. Результат:
matlabfrag()
Функция matlabfrag()
, помимо обязательного имени файла,
принимает ещё несколько опций: handle
, epspad
, renderer
и dpi
.
Например,
сохранит в файлы01:
matlabfrag
('figs/myfig','handle',h,'epspad',[10 10 10 10])
figs/myfig.tex
и figs/myfig.eps
не активную в данный момент картинку, а ту,
на которую указывает h
; кроме того, добавит по 10px со всех
сторон. Про renderer
и dpi
можно почитать
в документации.
\psfragfig{}
Прежде всего, поведением pstool
можно управлять задав
опции при загрузке пакета. Одну из них, которая принудительно перекодирует
все картинки, мы уже обсуждали: process=all
.
Иногда метки, выдающиеся далеко за пределы графика, оказываются
"обрезанными". Предотвратить такое поведение можно задав опцию
crop=pdfcrop
.
Если возникают непонятные проблемы со вставкой картинок, можно задать
опцию mode=errorstop
, чтобы процесс комиляции останавливался
при возникновении ошибки.
На этом список опций не исчерпывается: за подробностями всегда можно обратиться к документации.
Все эти опции можно задавать не только при загрузке пакета, но и после, а именно:
\pstoolsetup{}
; опция будет применена с момента её объявления
\psfragfig{}
; при этом опция будет применена только к текущей картинке;
например
01:
\psfragfig
[crop=pdfcrop]{figs/myfig}
matlabfrag()
Одним (пожалуй, единственным)
из недостатков matlabfrag()
является то, что всегда надо
указывать явно размер шрифта (посмотрите на строки 6 и 7
самого первого примера, см. также ниже),
чтобы он совпадал с размером шрифта в ЛаТеХе. Тогда как гораздо более
удобным, по мнению автора этих строк, поведением было бы следующее: (1)
если размер шрифта не указан, то просто используется установленный
ЛаТеХом; (2) если же мы указываем в Матлабе размер шрифта явно, через
FontSize
, то и в ЛаТеХе надо этот размер насильно применить.
К сожалению, в "ванильной" версии matlabfrag()
такая
возможность отсутствует. Однако в версии, которую можно найти в файле
с примерами к этому посту, я взял на себя смелость и изменил код
этой функции соответствующим образом. Т.е., если вы используете эту
"хакнутую" версию, то указывать
не нужно.01:
% Размер шрифта такой же как в ЛаТеХ-документе
02:
set
(0,'DefaultAxesFontSize',12);03:
set
(0,'DefaultTextFontSize',12);
До настоящего момента мы стремились использовать на графиках шрифт того же начертания, что и в основном тексте. Однако "в узких кругах существует мнение", что хотя в основном тексте лучше использовать шрифт с насечками (serif font), на рисунках надо отдать предпочтение шрифту без насечек (san serif font). Выражаясь языком понятным пользователям Ворда, "в тексте: Times New Roman, на рисунках: Arial". Якобы, такое разделение привлекает внимание к рисункам и выделяет их в массе текста.
Хотя эстетическая ценность такого решения весьма сомнительна,
в "улучшенной" версии matlabfrag.m
реализована такая
возможность. В преамбулу ЛаТеХ-документа помещаем
и наслаждаемся результатами. Заметьте, что сами рисунки для этого "перерисовывать" не надо. Результат с и без01:
\usepackage
{sansmath}02:
\providecommand
{\MATLABFont
}{\sffamily
\sansmath
}
\MATLABFont
приведён ниже. Обратите внимание, что греческие буквы недоступны
в начертании "без насечек".
В принципе, выше были рассмотрены все основные шаги и опции для сохранения графиков и их импорта в ЛаТеХ. Интересующиеся более подробно деталями процесса могут ознакомиться с документацией к matlabfrag и pstool.
Итак, подведём итоги. Описанный метод позволяет при сравнительно малых затратах времени добиться более чем удовлетворительных результатов: векторных графиков с надписями, выполненными ЛаТеХом. Это позволяет не только добиться гармоничного соответствия между шрифтами на рисунке и в тексте, но и использовать на рисунке формулы и ссылки на уравнения, другие рисунки и т.д.
В случае Матлаба, необходимая функциональность доступна через
matlabfrag()
.
Использование пакета pstool
позволяет существенно
упростить процесс импорта сгенерированных таким образом
eps-файлов в pdfLaTeX
.
Хотя мы рассмотрели данный рецепт на примере Матлаба,
аналогичные методы могут быть использованы и с другими программами.
Например,
MathPSfrag является аналогом mathlabfrag()
для
мат-пакета Mathematica. Для Gnuplot использование
set terminal latex
и экспорт через pstool
также
позволяют добиться сходного результата.
Здесь мы рассмотрели экспорт графиков функций из MATLAB, однако схожий трюк применим и к рисункам и схемам созданным, например, в Inkscape. Об этом в одном из следующих постов.
Все примеры из данного поста доступны для скачивания по адресу tinyurl.com/amorua-matlabfrag.
Материалы сайта «Записки дебианщика» написаны virens’ом и доступны на условиях лицензии Creative Commons Attribution-Non-Commercial-Share Alike 3.0 Unported License. Вы можете копировать, распространять, показывать эту работу, и создавать производные работы в некоммерческих целях на условиях:
1) обязательной ссылки на автора (virens, http://mydebianblog.blogspot.com/);
2) распространении любых производных работ на условиях этой же лицензии (ссылка на эту лицензию обязательна!).
Пожалуйста соблюдайте условия лицензии Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License. Авторские права - Михаил Конник aka virens.