Работаем в Octave с MathGL

Apr 20, 2012 12:25

Алексей Балакин (профиль на ЛОРе) давно уже разрабатывает проект MathGL, предназначенный для построения разнообразных графиков: как простых, так и очень сложных. Недавно он выпустил вторую версию этой библиотеки.
Библиотека предназначена для работы с разными ЯП, но для меня прежде всего интересен порт ее в Octave (чтобы можно было "по-быстрому нашлепать" красивых графиков для отчетов/презентаций и т.п.). О нем я и расскажу.
В репозитории моего арча самый свежий mathgl имеет версию 1.11, поэтому версию 2.0 я скачал с оф. сайта проекта и, не парясь со всякими makepkg, просто собрал его и установил.
Внимание! По умолчанию поддержки Octave в mathgl нет, поэтому cmake надо запускать с ключом -Denable-octave=1, или же не париться и указать ключ -Denable-all=1 (однако, в этом случае должны быть установлены все прочие зависимости). У меня всех зависимостей не было (да и не нужны мне все опции), а писать уйму -D… мне было лень, поэтому я просто подправил файл CMakeLists.txt:
option(enable-double "Enable double precision in MathGL library") option(enable-all "Enable all features") option(enable-langall "Enable all language interfaces") option(enable-lgpl "Enable only LGPL part of MathGL" OFF) option(enable-ltdl "Enable loading modules support" ON) option(enable-pthread "Enable POSIX threads support" ON) option(enable-gsl "Enable gsl support" ON) option(enable-jpeg "Enable jpeg support" ON) #option(enable-u3d "Enable u3d support") option(enable-pdf "Enable pdf support") option(enable-gif "Enable gif support" ON) option(enable-hdf4 "Enable hdf4 support") option(enable-hdf5 "Enable hdf5 1.6 support") option(enable-hdf5_18 "Enable hdf5 1.8 support") option(enable-opengl "Enable OpenGL support" ON) option(enable-glut "Enable glut support" ON) option(enable-fltk "Enable fltk widget") option(enable-wx "Enable wxWidget widget") option(enable-qt "Enable Qt4 widget") option(enable-python "Enable python interface") option(enable-octave "Enable octave interface" ON) option(enable-doc "Enable documentation building") Далее запускаем сборку и установку
cmake . make su -c "make install" Компилируется библиотека довольно-таки долго.
Одним только make install'ом мы не отделаемся: устанавливаются только файлы библиотеки, а порт для октавы надо поставить вручную. Первый вариант - в директории с проектом от имени рута запустить octave, а в нем (как написано в мануалах) - дать команду
pkg install lang/mathgl.tar.gz Да, устанавливается библиотека по умолчанию черт знает куда в совершенно "левую" директорию: /usr/local/. Поэтому для нормальной работы с ней нужно установить в ~/.bashrc или ~/.bash_profile переменную
export LD_LIBRARY_PATH="/usr/local/lib" (это же надо сделать руту перед установкой порта для октавы), а для разработки с подключением этой библиотеки придется написать свой конфигурационный файл для pkg-config'а (иначе надо будет вручную все эти нестандартные директории указывать).
Второй вариант установки порта для октавы - вручную распаковать файл lang/mathgl.tar.gz и поместить его содержимое в общее дерево октавовских пакетов.
После установки пакета запустим octave. Команда whos покажет нам, что у нас появилась уйма переменных, имена которых начинаются с mgl. Удалять их командой clear (или модифицировать) нельзя - лишимся mathgl'я. Главной переменной является структура mathgl, без которой у нас ни один скрипт не заработает и которую мы вынуждены будем передавать в каждый скрипт, использующий MathGL, как параметр.
По идее, mathGl позволяет работать напрямую с векторами данных, однако, в порте для Octave это не так. Ну, а т.к. графики рисуются с использованием внутренних структур mathGL, прежде всего нам стоит написать скрипт, который наши матрицы будет конвертировать в матрицы mathGL'я:
function D = newMglData(m, X) % % создаем структуру mglData размером, равным размеру матрицы X, % и заполняем ее данными % X может иметь размерность от 1 до 3 % % m - объект mathgl % [ny nx nz] = size(X); D = m.mglData(nx, ny, nz); for zz = 1:nz for xx = 1:nx for yy = 1:ny D.SetVal(X(yy, xx, zz), xx-1, yy-1, zz-1); endfor endfor endfor X(1) = 1; endfunction Этот скриптик принимает одно, двух- или трехмерную матрицу X и по ее размеру заполняет структуру mglData, которую и возвращает.

Для построения простейших одномерных графиков (правда, здесь и mathGL не нужен - гнуплота за глаза хватит) можно использовать вот такую функцию:
function plot_MGL1D(X, Y, m, Text, name) % рисуем график при помощи mathGL, если name != null - сохраняем % X, Y - массивы координат точек графика % m - структура mathgl, переданная пользователем (без нее не работает) % Text - необязательное поле - структура вида % Text.Title - заголовок графика % Text.xLabel - подпись оси X % Text.yLabel - подпись оси Y % ... что-нибудь еще можно будет воткнуть if(size(X) != size(Y) || size(X,1) != 1 || size(Y,1) != 1) printf("Error! X and Y have different or bad sizes!\n"); return; endif L = size(X, 2); g = m.mglGraph(); x = m.mglData(L); y = m.mglData(L); for i = 1:L x.SetVal(X(i), i-1); y.SetVal(Y(i), i-1); endfor Title = "Sample plot"; yLabel = "axis Y"; xLabel = "axis X"; if(nargin > 3) if(isfield(Text, 'Title')) Title = Text.Title; endif if(isfield(Text, 'xLabel')) xLabel = Text.xLabel; endif if(isfield(Text, 'yLabel')) yLabel = Text.yLabel; endif endif g.Title(Title); g.Label('y', yLabel, 0); g.Label('x', xLabel, 0); Xr = m.mglPoint(); Yr = m.mglPoint(); m.mglPoint_x_set(Xr, min(X)); m.mglPoint_y_set(Xr, min(Y)); m.mglPoint_x_set(Yr, max(X)); m.mglPoint_y_set(Yr, max(Y)); g.SetRanges(Xr, Yr); g.Axis(); g.Box(); g.Plot(x, y); g.ShowImage('feh'); if(nargin == 5) g.WritePNG(name); endif endfunction Нарисуем простенький график:
X = log([1:100]); Y = cos(X); plot_MGL1D(X,Y, mathgl, struct('Title', "Sample MathGL graph", 'xLabel', "X axis", 'yLabel', "yAxis"), "/tmp/sample.png") На экране отобразится наш график, а после того, как мы закроем его (функция ShowImage "подвисает", пока график отображается на экране), сохранит вот такой png-файл:


В документации mathGL полным-полно примеров. Для 1D-графиков приведу только лишь еще один (т.к. он используется довольно часто): построение графиков с ошибками.
Итак, открываем пример и строим два варианта графиков: с отметкой ошибок по Y отрезком и с отметкой доверительных интервалов прямоугольником.
Первый график:
x0 = newMglData(mathgl, [1:10]); y0 = newMglData(mathgl, log([1:10])+rand(1,10)*0.3); e0 = newMglData(mathgl, rand(1,10)*0.3+0.1); x = newMglData(mathgl, [1:0.1:10]); y = newMglData(mathgl, log([1:0.1:10])); gr = mglGraph(); gr.Title("Error plot (default)"); gr.Label('x', 'X',0); gr.Label('y', 'Y = \ln x', 0); gr.SetRanges(1,10,0,3); gr.Axis(); gr.Box(); gr.Plot(x,y); gr.Error(x0,y0,e0,"ko"); gr.ShowImage('feh');

И второй график (структуры x, y, x0, y0, e0 используем из предыдущего):
ex0 = newMglData(mathgl, rand(1,10)*0.15+0.05); gr.Clf(); gr.Title("'\\@' style"); gr.Label('x', 'X',0); gr.Label('y', 'Y = \ln x', 0); gr.SetRanges(0,11,-0.5,3); gr.Axis(); gr.Plot(x,y); gr.Error(x0,y0,ex0,e0,"@","alpha 0.5"); gr.ShowImage('feh');


Итак, примеры из документации легко реализовать в Octave при помощи макроса-посредника, формирующего структуры данных.
Теперь перейдем к примерам двумерных графиков.
Подчистим ненужные данные и подготовим массивы для построения 2D-поверхностей:
clear x0 y0 ex0 e0 x y; [X Y] = meshgrid([-5:5], [-5:5]); a = newMglData(mathgl , sin(0.1*pi*X).*cos(0.2*pi*Y)+0.4*cos(0.2*pi*X.*Y)); b = newMglData(mathgl , cos(0.1*pi*X).*sin(0.2*pi*Y)+0.4*sin(0.2*pi*X.*Y)); v = newMglData(mathgl, [-1.5:0.2:1.5]); И начинаем строить графики. Начнем с простой поверхности:
gr.Clf(); gr.Title("Surf plot (default)"); gr.Light(true); gr.Rotate(50,60); gr.Box(); gr.Surf(a); gr.ShowImage('feh');

Теперь построим контурную карту поверхности (если не писать gr.Surf(a);, будут отображены только изолинии):
gr.Clf(); gr.SetSize(1000,700); gr.Title("manual levels"); gr.Rotate(50,60); gr.Box(); gr.Surf(a); gr.Cont(v,a); gr.ShowImage('feh');

Вариантов поверхностей довольно много. Вот еще один, в котором прозрачность изменяется, исходя из пользовательской матрицы:
clear gr gr = mglGraph(); gr.Alpha(true); gr.Light(true); gr.Title("Manual alpha"); gr.Rotate(50,60); gr.Box(); gr.SurfA(a,b); gr.ShowImage('feh');


Я не рассказал еще о 3D-графиках и векторных полях, но это оставлю на потом.

octave, mathgl

Previous post Next post
Up