Coding Poet, Coding Science

MathGL绘图工具

该绘图工具的应用也很广,但是之前一直都没有能够接触到。现在才将相关的内容补充到笔记当中,希望这是一个非常有效的绘图的工具。

MathGL在Ubuntu下的安装是一件简单的事,安装mathgl关键字开头的程序、文档、头文件与库文件、面向Python语言的python-mathgl库,以及udav可视化工具即可。MathGL支持几种不同的编程方式,面向C、C++、Fortran、Octave或者Python。根据需要,应当安装上不同的接口。也许有一天,支持R也是可行的。此外,MathGL工具还可以分析一种称为MGL的脚本文件,该脚本文件可以方便地完成MathGL中的一些绘图操作,是值得学习的一种操作方式。

在绘图过程中,MathGL有自己的字体系统,但是尽量可以做到与TeX的兼容。不仅可以方便地选择字体,而且提供了大量的数学符号。这些对于初学者及一般的应用都是非常充足的。

MathGL有很多优点。如脚本化的绘图语言、C++的执行效率、多语言接口、支持输出多种图形格式、内建多种绘图方法、支持MPI等编程接口、与C++等宿主语言的数据结构融合。这些使它成为很有吸引力的一个绘图库。

MathGL基础操作

首先我们介绍在MathGL在C++下的使用。像C++这类接口,一般要安装lib*这样的软件包,而不只mathgl软件包一个,这样g++在编译的时候,才可能与正确的库文件链接。这是一个需要注意的地方。如果下面的MathGL C++程序出错,根据编译器反馈的错误信息,我们应当能够发现错误、纠正错误。

#include <mgl2/mgl.h>
int main(int argc, char* argv[])
{
    mglGraph graph;
    graph.FPlot("sin(pi*x)");
    graph.WriteFrame("test.png");
    return 0;
}

编译、运行mathgl1.cpp,查看图片所使用的Bash命令建议为:

g++ -lmgl mathgl1.cpp -o mathgl1
./mathgl1
eog sample.png

注:如果使用了其它的绘图接口,可能需要使用 -lmgl-wnd 等库。

MathGL的绘图画布中使用帧、子图等概念,因此一个 mglGraph对象可以创造出多个子图的组合,以及多个帧的组合。后者使MathGL有制作动画的能力。

使用MGL脚本的时候,MathGL中有 mglconv 工具和 mglview工具可以使用。它们 是运行MGL脚本的宿主环境。

MathGL库支持的输出方式,包括向图形终端输出,此方式下可以完成一些交互式的动作;直接写入到点阵或者矢量图形格式,此方式下直接运行MathGL的C++程序以得到结果。

窗口绘图、GLUT绘图与输出到文件

绘图到一个单独的窗口,或者使用GLUT库输出,道理是一样的。

#include <mgl2/mgl.h>
#include <mgl2/window.h>

int do_draw(mglGraph *graph)
{
    graph->Rotate(60,40);
    graph->Box();
    return 0;
}

int main(int argc, char* argv[])
{
    mglWindow window(do_draw, "MathGL Window example');
    int status = window.Run();
    return status;
}

原理就是如此了,不过在Ubuntu上有些错误,可能是安装MathGL的问题。

使用GLUT作为后端的时候,更为简单,因为不用WX组件了。

#include <mgl2/mgl.h>
#include <mgl2/glut.h>

int do_draw(mglGraph *graph)
{
    graph->Rotate(60,40);
    graph->Box();
    return 0;
}

int main(int argc, char* argv[])
{
    mglGLUT window(do_draw, "MathGL Window example");
    return 0;
}

在编译的时候,调用

g++ mglut.cpp -o mglut -lmgl -lmgl-glut

如果将 -lmgl 选项放在 mglut.cpp的前面,似乎总会出现连接错误。这个问题 目前还不知道是怎么样的一回事。

在GLUT界面中,使用aswd按键旋转图形,使用r与f切换透明(transparency)与光照(lighting)状态。按键x可以退出窗口。

说明:以后我们的绘图的时候,就使用上面的代码所提供的方法,把绘图的代码都放在 do\_draw() 函数里面,然后由 mglGLUT对象调用。这样可以使我们更 专心于图形的绘制。

如果是输出到文件,那么调用 mglGraph 对象的 WritePNG()WriteEPS() 等 函数即可。在使用g++编译的时候,除了-lmgl 选项,其它都不用。

动画的制作使用 mglGraph对象的多帧功能。如果是保存GIF格式,在绘出每帧 之前,就应当使用mglGraphStartGIF() 函数保存文件,并在完成所有的帧的绘制之后使用 CloseGIF() 函数关闭GIF文件的描述符。

保存动画还有另一种方法,就是绘制完当前帧后,以frameXX.jpg的格式保存当前帧,然后切换到下一帧。所有的帧都各自输出成JPEG格式后,使用ImageMagick提供的convert命令将JPEG制作成MPG。

MathGL也支持在Qt绘图画布中绘图,图形作为Qt窗口的一部分。但是那并非MathGL绘图语言的核心。现在不讲,或许以后结合PyQt库使用MathGL的时候这个功能我们会用到。

MathGL对子图绘制的支持

MathGL采用“选中-绘制”的方法支持子图的绘制,也就是一个mglGraph 对象使用 SubPlot()方法选中一块子图区域,之后还是使用 mglGraph 对象的Title()等函数来操作绘图,此时图形会自动绘制在当前的子图上。在绘制子图的时候,另一种编程实现是“切换-绘制”的方法。该方法是调用当前绘图对象的方法产生一个子图对象,通过操作子图对象的方法在子图上绘图。Asymptote与Mathplotlib等都是采用这种方法。MathGL是前一种方法,因此要与后一种实现方式区分开。

int do_draw(mglGraph *g)
{
    g->SubPlot(2,2,0); g->Box();
    g->Puts(mglPoint(-1,1,1),:"Just box", ":L");
    g->InPlot(0.2,0.5,0.7,1,false);
    g->Box();
    g->Puts(mglPoint(0,1.2), "InPlot Example");

    g->SubPlot(2,2,1);
    g->Title("Rotate only");
    g->Rotate(50,60);
    g->Box();

    g->SubPlot(2,2,2);
    g->Title("Rotate and Aspect");
    g->Rotate(50,60);
    g->Aspect(1,1,2);
    g->Box();

    g->SubPlot(2,2,3);
    g->Title("Aspect in other direction");
    g->Rotate(50,60);
    g->Aspect(1,2,2);
    g->Box();

    return 0;
}

更复杂的子图绘制方式,可以参考 MultiPlot()StickPlot() 等函数。

剩下的就是具体各种图形的绘制了。

使用MathGL绘制动画示例

int do_draw(mglGraph *gr)
{
    gr->NewFrame();
    gr->Rotate(60,40);
    gr->Box();
    gr->EndFrame();
    gr->NewFrame();
    gr->Box();
    gr->Axis("xy");
    gr->EndFrame();
    return gr->GetNumFrame();
}

使用MGL脚本语言

MGL脚本语言其实是很方便的一个东西。该语言每一行都代表一个命令,类似于tcl脚本,而且连注释的格式都是与tcl语言相同的:从#开始直到行尾。另一方面,MGL脚本按行分析语句,语句的第一个单词代表一个命令,像Bash中那样。而MGL脚本中的字符串用成对的单引号括起来,如果需要跨行,则使用反斜杠加换行表示将本行与下一行联系在一起。

MGL脚本中的命令都来自 mglGraph的成员函数,因此只要我们了解了MathGL面向C++的库的用法,自然就知道如何写MGL脚本文件。

除此之外,MGL还有一些定义函数、声明变量的语句。它们都转成相应的C++的语法。在C++中使用MGL语言也是可行的,因为MGL脚本会通过 Parse()函数 被解析,由 Execute()函数执行。(两个函数都在mgl2/mgl.h头文件中)。

A. A. Balakin所蓍的《MathGL官方文档》中的《MathGL core》一章,既介绍了MathGL语言(C++库的函数),同时又介绍了MGL的命令,是MGL语言与MathGL库的最标准的参考手册。

MathGL所提供的mglconv工具可以将MGL脚本转换成PGF/TikZ、EPS、PRC、SVG等格式,因此也可以作为一个实用的面向TeX文档的接口。这样一来,可能在MathGL中文字处理变得更简单了。