Емельянов Эдуард Владимирович (eddy_em) wrote,
Емельянов Эдуард Владимирович
eddy_em

Categories:

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

Алексей Балакин (профиль на ЛОРе) давно уже разрабатывает проект 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-файл:
Sample 1D graph
В документации 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');
Simple Err
И второй график (структуры 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');
Boxed Err

Итак, примеры из документации легко реализовать в 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');
Surf default
Теперь построим контурную карту поверхности (если не писать 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');
2D contour
Вариантов поверхностей довольно много. Вот еще один, в котором прозрачность изменяется, исходя из пользовательской матрицы:

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');
Manual alpha

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

  • Контроллер вентиляторов

    Потихоньку допиливаю контроллер. Забыл сегодня на работе взять платиновый терморезистор для аттестации терморезисторов, которые там используются…

  • "Звезды" на STM8

    Я уже писал о том, что на новогодний концерт сделал управление "звездами" на основе STM8. Сам концерт можете посмотреть на youtube (на всякий…

  • Андроидоуправлялка

    Хоть ардуина - редкостная дрянь, но благодаря ей появилась уйма приложений под андроид, позволяющих через bluetooth-uart модуль контролировать разную…

promo eddy_em august 17, 2019 12:33 3
Buy for 10 tokens
Юра намедни напечатал корпус для хронометра. Для первого блина получилось неплохо: И еще немного фотографий:
  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic
  • 1 comment