Matlab与VC混合编程的方法有多种,为了对混合编程有一个大概认识,将常用的几种方法中介绍一下:
一、通过Matlab Engine方式
Matlab Engine是指一组Matlab提供的接口函数,支持C语言, Matlab Engine采用C/S(客户机/服务器)模式,Matlab作为后台服务器,而C程序作为前台客户机,通过Windows的动态控件与服务器通信,向Matlab Engine传递命令和数据信息,从Matlab Engine接受数据信息。用户可以在前台应用程序中调用这些接口函数,实现对Matlab Engine的控制。采用这种方法几乎能利用Matlab全部功能,但是需要在机器上安装Matlab软件,而且执行效率低,因此在实际应用中不采用这种方法,在软件开发中也不可行,我认为适合个人使用或做演示用,因此只作简单介绍。
二、直接调用Matlab的C/C++数学函数库
Matlab中提供了可以供C/C++语言调用的C/C++数学函数库,其中包含了大量用C\C++语言重新编写的Matlab数学函数,这些函数涉及到线形代数、数值分析、傅立叶变换、多项式计算、解微分方程等,并且函数库中提供了大量各种矩阵操作函数,在VC中可以直接使用这些函数,通过这些函数可以在VC中方便的实现在Matlab中矩阵运算功能。可以看出用这种方法可以很灵活的调用Matlab来编写应用程序,但要求读者对C\C++语言比较熟悉,可以看出使用这种方法调用Matlab的工具箱函数有很大困难。适合对C\C++语言比较熟悉的用户使用。
三、用Matlab自带的Matlab Compiler
Matlab Compiler的作用是将m文件转化成C/C++代码(也就是通常所用的mcc命令),这种源代码需要用C/C++编译器编译连接成独立应用程序,在将m文件转成独立应用程序的过程中生成的C/C++文件,原则上是可以被其它的C/C++代码调用的,编译器可以通过设置mcc命令的选项,将m文件编译成动态链接库文件、C/C++文件、可执行文件等一系列文件。到matlab R21.1为止,Matlab Compiler的m程序转换成C/C++代码功能有很多限制:
不能转换脚本m文件,只能转换m函数;
不能使用matlab对象;
不能用input或者eval操作matlab空间变量;
不能动态地命名变量,然后用load或者save命令来操作;
不能处理具有嵌套调用其他m文件的m文件;
不能使用MATLAB内联函数;
四、使用matlab的combuilde工具
COM是component object module的简称,它是一种通用的对象接口,任何语言只要按照这种接口标准,就可以实现调用它。matlab6.5新推出来的combuilder就是把用matlab编写的程序做成com组件,供其他语言调用。该方法实现简单,通用性强,而且几乎可以使用Matlab的任何函数(注意:不支持脚本文件,脚本文件使用时要改为函数文件),因此在程序较大、调用工具箱函数或调用函数较多时推荐使用,这也是Matlab公司(Matlab公司就是Mathworks公司)推荐的使用方法。
五、使用matcom工具(详细介绍)
这是个第三方控件,很小(8M多),原本属于mathtool公司,后来被Mathworks公司合并了,使用该工具可以将m脚本文件和m函数转化成相同功能的C\C++文件,相比其它方法使用matcom具有如下优点:
转换过程简单(由matcom工具自动实现),容易实现;
可以方便的生成动态链接库文件(dll)和可执行文件(exe);
不仅可以转换独立的脚本文件,也可以转换嵌套脚本文件;
设置环境后,可以使用Matlab的工具箱函数;
但matcom也有以下不足:
对struct等类的支持有缺陷,对class数据类型;
部分绘图语句无法实现或得不到准确图象,尤其是三维图象;
因此在不涉及到三维做图以及m文件不大的情况下推荐使用。
以上几种方法可以单独使用,也可以混合使用。这里简单的介绍了VC和matlab接口的几种方法,读者可以根据需要选择适合自己的方法。需要说明的是上以上几种方法并不是相互独立的,而是有相互联系的,比如使用C/C++函数库与使用编译器,对于实现相同功能的程序,直接调用函数库与使用编译器最终生成的代码可能相差不大,只不过一个是直接在VC中写C/C++代码,一个是在Matlab中写好m代码,然后通过编译器将m代码转化成相同功能的C/C++代码,而在转化的过程中也需要调用相应的C/C++函数库文件。在后面的文章中将从第五种方法开始进行详细介绍,其他的方法只作简单介绍。
Matcom是Mathworks公司推出的第一个由Matlab到C++的编译开发软件平台,它的可视化界面,方便丰富的调试功能和对数学库的强大支持受到广大技术人员的重视。现在的最高版本为Matcom4.5。
Matcom是一个能将M文件转化成相同功能C++代码的工具。相比Matlab自带的编译器Compiler,用Matcom转化代码要简单和方便得多。
Matcom是一个十分有用的.m文件翻译器(Replacement),它的主要优点我认为有以下几点:
1> 它提供了matlab中.m文件与其他高级语言的接口,使.m文件可以编译为脱离matlab环境独立执行的可执行性程序,这样
提高了代码的复用率
提高了代码的执行速度
使纯文本的.m文件变为二进制的可执行程序,增加了知识保护的安全性
2>它提供了近千个数学函数,对于其他高级语言编译器来说,提供了一个丰富的数学库,基本上在matlab上能用的常用函数都可以在高级语言中直接调用。
数学函数主要包括:
矩阵属性函数
矩阵生成函数
矩阵操作函数
矩阵变换函数
数学函数
特殊函数
数值函数
串函数
绘图函数
颜色函数
函数函数
存盘及读文件
系统资源函数
系统操作函数
判断函数(Is函数族)
付氏变换等等
3> 提供了.m文件的方便快捷的编译调适环境,可以step, watch,breakpoint等各种调试手段。
Matcom的工作原理
Matcom的矩阵运算部分是基于一个名为Matrix的C++数学库,这个库提供了绝大多数的关于矩阵类、矩阵操作函数、数值计算函数、数学函数等的定义,在Matcom中是以lib目录下的*.lib以及windows/system/对应名称的dll文件提供的。Matcom的另一大部分就是图形部分,它是用一种非常流行的绘图OCX控件Teechart来实现的,这种控件对于一般的绘图功能都可以实现,但也存在一定缺陷。在Matcom4.5版本中使用的是TeeChart3.0。绘图函数功能主要在lib文件和window/system/ago*.dll中定义的。
Matcom编译.m文件是先将.m文件按照与matcom的Cpp库的对应关系,翻译为CPP源代码,然后用对应版本的C编译器将该CPP文件编译为exe或dll文件,所以,在第一次运行时让指定C Complier的路径是必需的,否则将无法编译。指定好的C Complier的信息写在Matcom/bin/matcom.ini文件中。
Matcom的不足
Matcom并不是全能的,对于大多数Matlab函数都可以进行CPP实现,但有些由于其功能有限,只能期待以后的版本来不断补充了。总的来说,matcom有以下缺欠:
1.对class数据类型部分支持
2.eval,feval,clear等语句不能在C中实现(如果实现的话,一个文本编辑器就可以成为
一个matlab了:))
3.图形窗口有些不仅如人意,如fill3,hide等语句无法实现,surf等语句也无法画出象
matlab中哪样精细的图像来,特别是色彩比较难看………..
Matcom的安装:
安装Matcom前要已求安装VC6.0(推荐将vc6.0安装在c盘,否则可能会出现意想不到的错误而导致无法运行)。
安装步骤:
1. 在安装时需要你输入口令,Matcom4.5的口令为FREE-4.5-1193046-80295111 。
2. 在安装过程中出现选择编译器对话框,选择“是”。
3.出现选择是否安装Matlab时,选“否”。
安装完成。
安装完成后测试:
安装完成后,启动界面MIDEVA。MIDEVA集成开发环境包括命令行窗口、变量列表窗口、命令列表窗口和编译链接信息窗口等几部分,并有详细的帮助文档。
Matcom命令输入方法与Matlab相同。如果安装在中文版操作系统时,输入命令前加一空格。测试时,随便输入两个数字让其计算,不出现异常则安装成功。
说明:1.如果按照此步骤安装没有成功,可尝试将vc卸载后重新将VC60安装在C盘。
2.在第2次启动mideva时,如果出现让你再次输入口令的对话框(如下图),
可尝试以下方法解除此限制:
按HKEY_CURRENT_USER/Software/MathTools/Matcom.50/License找到mideva在window的注册表中的位置。
在其下面添加一个键,键名默认,键值为FREE-4.5-1193046-80295111 om.50License
然后再在其下面添加一个键,键名默认,键值为FREE-4.5-1193046-80295111 你如果删除它,再次启动matcom的时候,就会再次询问口令。
在这里介绍一种将Matcom集成到VC环境的方法,即安装Visual Matcom的方式,这种方式操作更加简便易行,只需要熟悉Matlab编程,经过简单的步骤就可以轻松实现VC环境中调用Matlab。
Visual Matcom开发环境的配置步骤:
(1)拷贝\bin\usertype.dat文件(指Matcom的安装路径)到\Common\MSDev98\Bin目录(指VC的安装路径)下。
(2)运行Visual C++,从菜单条中选择Tools/Customize/Add-ins and Macro Files,选择Browse,改变文件类型为Add-ins(.dll),选定\bin\mvcide.dll文件,确定。
(图1)
(3)这样,可以在Visual C++的开发环境中看到一个如图1所示的Visual Matcom工具栏,表明安装成功。
运行环境:Win2000、Matlab70、Matcom4.5、Visual C++6.0
在配置好Visual Matcom开发环境后,本节介绍,使用单纯VC与 VC和Matcom混合编程方式分别求解线性方程组。
如:解下列线形方程组
1*x + 2*y + 3*z = 37
4*x + 5*y + 6*z = 85
7*x + 8*y + 1*z = 69
在C++控制台程序中单纯使用C++语言编写求解线性方程组的代码为:
结果如下:
下面介绍使用VC与matcom混合编程方式来完成该方程组的求解:
(1)Matlab运行环境中编写程序equation.m,并将其保存。其代码如下:
%equation 求解线性方程组的解
%线性方程组形如:A*X = B
function X = equation(A, B)
X = A\B;
(2)这里以一个简单的控制台程序为例,其它程序基本相同。在VC环境中建立一个名为MatcomEquation的Win32 Console Application工程。
(3)点击Visual Matcom工具栏上的m++图标,选择保存过的Matlab文件equation.m进行转化。如果看到的转化信息提示没有错误就可以观察到此时在FileView标签中多了m-files,C++files created from m-files,Matrix等文件。并且该工程目录下增加了equation.h,equation.cpp,equation.mak,equation.r等的4个文件。这时会在VC中出现一个转换完毕的文件,文件中如果报告有错误就要考虑是否程序有问题,可以双击C++files文件夹下的equation.m进行修改,再重新转化直到没有错误报告为止。
(4)在MatcomEquation工程下建立一个文件MatcomEquation.cpp调用转化的Matlab函数,代码为:
#include "stdio.h"
#include "equation.h"
#include "matlib.h" //提供转化后C++代码中使用的数据类型,函数原型及常数
void main()
{
initM(MATCOM_VERSION); //初始化matlib库
Mm a,b,x; //使用矩阵类Mm构造矩阵a,b,x
a = (BR(1),2,3,semi,4,5,6,semi,7,8,1);
b = zeros(3,1); //初始化矩阵b为零矩阵3行1列
b(1,1) = 37; b(2,1) = 85; b(3,1) = 69; //给矩阵b赋值
x = equation(a,b); //调用转化的函数,求解线性方程组的解
for (int i = 1; i <= x.rows(); i++)
{
for (int j=1;j<=x.cols();j++)
printf("x(%d,%d)=%f\n",i,j,x.r(i,j));
}
exitM(); //结束对matlib库的调用
return;
}
(5)代码说明:
BR是Matrix库的一个宏,用于定义一个矩阵的开始
semi是库的一个常量,用于分隔不同行的矩阵元素
程序中涉及到了两个成员函数.rows()和.cols(),它们分别返回矩阵的行数和列数;x.r(i,j)代表矩阵x的第i行第j列的元素。
(6)编译运行后结果为:
与实际结果一致。
总结:
从上面的例子可以看出:用MATLAB写出的代码十分简单,对于上面的方程,如果矩阵A和B的维数发生变化的话, C++代码还要重新编写,而用MATLAB书写的代码几乎不用改动,只需改变数据即可。
如果现在我们用VC编写的一个软件,其中要求解有5个未知数的线形方程组,如果用C\C++语言来写程序的话其编程量是很大的,如果在C\C++语言中能用MATLAB那么简单的代码来实现该功能就可以大大减少工作量,VC和MATLAB接口就是做这个工作的。 MATLAB中提供了大量用C\C++重新编写的MATLAB库函数,包括初等数学函数、线形代数函数、矩阵操作函数、数值计算函数、特殊数学函数、插值函数等等,还可以利用MATLAB的编译工具将m文件方便的转化为C\C++语言、可执行程序(exe)、动态连接库文件(dll)、COM组件等等,并可直接供C\C++语言调用,利用VC和MATLAB接口技术可以在VC中充分发挥MATLAB的数值计算功能,并且可以不依赖MATLAB软件运行,我们在编写程序时,可以在VC下做出很漂亮的界面,而把复杂的数值处理交给MATLAB去做(实际上MATLAB也能做界面GUI(Graphic User Interface),而且做出的界面我认为并不比VC逊色,但是需要MATLAB软件作为后台运行,如果不依赖MATLAB软件运行的话,可以在VC中调用GUI),然后通过接口技术将MATLAB集成到VC中,这样可以大大减轻编程的负担,并减少程序编写时间。
上面只是一个简单的控制台工程的例子,我们也可以建立其它类型的工程,只要在需要调用转换后函数的程序中包含matlib.h并且在响应函数中初始化matlib库initM(MATCOM_VERSION);结束调用后做结束工作exitM();就可以了。
在《Visual Matcom开发环境的配置(一)》中,我们转换的.m文件是一个函数,如果需要转换的.m文件不是一个函数,只是一些Matlab命令的集合,那又该怎么办呢?
同《Visual Matcom开发环境的配置(一)》几乎相同。当需要转换的.m文件不是一个函数,只是一些Matlab命令的集合时候,转换完后,另外要在工程目录下找到转换文件的.cpp文件,只需将其中的C++代码拷贝到需要调用它的函数里面。还以上一个方程组为例子:
(1)编写求解方程的.m文件,存为Config2.m ,代码如下:
A=[1,2,3;4,5,6;7,8,1];
B=[37;85;69];
X=A\B;
(2)当.m文件转换为C++代码后,程序自动生成了一个main函数,不需要的话,可直接删除,在此例中需要main函数,所以就直接在函数类别添加代码:
(3)运行结果为:
(1) 打开VC60,在VC环境中建立一个名为MatcomConfig3的Win32 Console Application工程。
(2) 设置VC++工程,打开Project|Setting 的 Link标签,在Input选项下,加入Matcom C++矩阵库需要使用的 v4501v.lib,并且根据Matcom安装目录设置路径C:\matcom45\lib。如图示:
(3) 打开Project|Setting 的 C/C++标签,在Preprocessor选项下, 根据Matcom安装目录为头文件添加目录C:\matcom45\lib,如图示:
(4) 单击OK,加入头文件matlib.h,运行程序,如果有错误的话,请仔细查看上述工程设置是否正确。
(5) 以上节中的线性方程组为例,编写程序求解线性方程组的解。添加代码如下:
(6) 运行结果:
在Matcom中所写的M文件,经编译后,会生成.h,.cpp文件,此种配置方法就是将生成的.h、.cpp(默认位置为安装目录:\matcom45\samples\Debug)和matlib.h、v4501v.lib(默认位置为安装目录:\matcom45\lib)四个文件拷贝到建立的工程目录下,然后再将这四个文件加入到VC工程中:工程->添加工程->文件,环境配置成功。参考实例2(VC中调用Matlab函数画图)。
一、创建数值矩阵
在Matcom C++矩阵库(Matrix)中,矩阵作为一个类Mm被封装了起来,这对开发人员来说使用起来非常方便的。在Mm声明对象实例的时候,构造函数会自动对生成的矩阵对象进行必要的初始化。
矩阵对象的初始化和普通的C++类对象的声明方式一样:
Mm a; //声明对象a为Mm的矩阵对象
默认的矩阵构造函数只是将矩阵对象初始化为一个空的矩阵,如上例中的对象a不包含有任何矩阵元素。如果要将矩阵初始化为m行n列的矩阵,可采用:
Mm a;
a = zeros(m,n); //初始化为m行n列的0矩阵
a = zeros(m,n,p); //初始化为m*n*p的三维0矩阵
a = czeros(isc,m,n) //初始化为m*n的复数矩阵,isc为true的话,矩阵被初始化为复数
Mm类重载了赋值操作符,当某个Mm对象被赋值以后,它会被自动初始化并重新分配矩阵的维数和元素的值,因此大部分情况下一个矩阵对象声明以后不需要采用zeros或者czeros进行初始化。如:
Mm a;
a = rand(4,4); //直接使用并自动初始化为4*4的随机矩阵
Mm对象可以通过dMm宏在构造的时候命名,这样以来当用display显示这个变量的时候,就会将变量的名字放在变量说明的最前面。例如:
Mm Matrix1;
Matrix1 = rand(4,4);
display(Matrix1);
dMm(Matrix2);
Matrix2 = rand(4,4);
display(Matrix2);
则输出为(注意加粗部分,此为两种变量声明方式的输出差别):
ans (4x4)=16 double elements real (128 bytes) =
0.9501 0.8913 0.8214 0.9218
0.2311 0.7621 0.4447 0.7382
0.6068 0.4565 0.6154 0.1763
0.486 0.0185 0.7919 0.4057
Matrix2 (4x4)=16 double elements real (128 bytes) =
0.9355 0.05789 0.1389 0.2722
0.9169 0.3529 0.2028 0.1988
0.4103 0.8132 0.1987 0.01527
0.8936 0.009861 0.6038 0.7468
变量的名称可以通过.getname()获得,由于dMm是一个宏,如果采用这种方式定义变量的话只能采用单独定义的方式,如Mm(a), Mm(b)和Mm(c)。
二、创建字符矩阵
采用函数TM可以创建一个字符矩阵,如下所示:
Mm cx;
cx = TM(“Hello\n”);
字符矩阵在Matcom C++矩阵库中是以双精度型保存的,所以字符矩阵和双精度系数矩阵可以相互转换,即矩阵到底是字符型还是双精度实型的可以动态切换。Matcom C++矩阵库用setstr来进行切换。如:
Mm b;
b = colon(65,69); //相当于Matlab的65:69,产生一个向量[65 66 67 68 69];
a.setstr(1);
display(a); //结果是矩阵中的ASCII码的字符输出“ABCDE”
其中,colon相当于Matlab的“:”操作符,colon(i,j,k)相当于i:j:k,其含义是产生一个向量,从i开始,到k结束,步长为j。
将Matcom C++矩阵库里的字符数组转换为char*类型的字符串,采用下面的函数:
void Mstr(const Mm&x,char *str,int maxlen);
其中,char *str表示目的字符串,x表示字符类型的矩阵,maxlen表示x中最大的字符数目,注意字符串最后的“\0”也算一个字符。
三、Mm矩阵对象的初始化
Mm矩阵对象可以通过构造函数的初始化,也可以通过其他具有返回值的函数如函数rand来初始化,通过构造函数进行初始化常用的实现方式有:
(1) 采用double型向量直接初始化。
(2) 采用double型常数进行初始化。
(3) 采用C/C++中构造好的数据块进行初始化。
上述3种方式的程序实现如下所示:
double data[6] = {5,1.1,2.0,4,7.1,6};
Mm x;
M_VECTOR(x,data); //形式(1),采用double型向量直接初始化
Mm y;
Y = (BR(5),4,3,semi,1,2,3,semi,7,8,9); //形式(2),采用double型常数进行初始化
Mm z;
z = zeros(1,6);
memcpy(z.addr(),data,6*sizeof(double)); //形式(3),直接复制数据
注意:在形式(2)中,第一个变量的初始化必须用BR函数开头。其中semi表示分号(;),其含义是一行结束,另一行重新开始。在形式(3)中,需要注意Matcom Mm矩阵与C/C++数组在内存中的存储顺序不一致。在Matcom Mm矩阵中,数据按列的方式进行存储;在C/C++中,数据按照行的方式进行存储。因而需要使用reshape函数达到改变Mm矩阵数据存储顺序的目的。
四、利用下标访问矩阵单元
与Matlab中的矩阵单元访问方式类似,在C++中读写矩阵单元是用两个函数来完成的,即通过a.r()来访问矩阵的实部,而通过a.i()来访问矩阵的虚部,其中a为矩阵变量。下面是几种访问形式:
Mm a;
A = magic(3); //[8,1,6;3,5,7;4,9,2]
a.r(); //8
a.r(2,2); //5
a.r(8); //7 注意:按列优先
五、获取矩阵数据的指针
通过函数.addr()和.addi()分别获得矩阵的实部和虚部的地址,其返回值都是 double*型的指针,当我们有很多的矩阵单元要进行读写操作时,如果还是利用a.r()来逐一逐一地拷贝值的话,其性能不好。C++提供了直接访问矩阵内存的方法,即通过a.addr() 返回矩阵变量的实部的指针,通过.addi()返回矩阵变量的虚部的指针,如 Mm X;
X=zeros(100,100);
X.addr()指向矩阵变量的 (1, 1)处,X.addr(n)指向矩阵变量第n个单元所在的位置,X.addr(m, n)指向矩阵变量的(m, n)处。
例如:
Mm x;
double a,b,c;
x=magic(3); //生成一个3阶幻方矩阵x = [ 8 1 6 ; 3 5 7 ; 4 9 2 ]
a=*x.addr(); // a=8
b=*x.addr(2); // b=1
c=*x.addr(2,3); // c=7
*x.addr(3,1)= - 999; // x=[ 8 1 6 ; 3 5 7 ; -999 9 2 ]
*x.addr()= - *x.addr(); // x=[ -8 1 6 ; 3 5 7 ; -999 9 2 ]
另外,同个MatcomC++矩阵库的矩阵指针可以用memcpy函数复制矩阵中的数据。例如:
double* a_v=(double*)malloc(512*sizeof(double));
Mm a=rand(512,1);
memcpy(a_v,a.addr(),512*sizeof(double));
六、Mm矩阵类的几个常用函数
Mm矩阵类的所以函数都是以.func(para)的形式调用的,其中常用的函数如下:
size() 返回矩阵的大小,返回值为Mm类型
size(dim) 返回矩阵某维的最大元素数
rows() 返回矩阵的行数
cols() 返回矩阵的列数
isstr() 判断矩阵是否是字符矩阵
setstr(is) 设置矩阵的字符矩阵特性
isc() 判断矩阵是否是复数矩阵
getndims() 判断矩阵的维数,返回为Mm
getdims() 判断矩阵的各维大小,返回int*
七、Matcom C++矩阵库常量
i,j 复数使用
pi 圆周率的值
Inf 表示无穷大,如果出现x/0的情况(x!=0),结果为Inf。
NaN Not a Number,0/0的结果以及其他无意义的算术运算结果。
eps 最小的数,在Matcom中为2-52。
semi 表示分号,即矩阵的行结束分隔符。
c_p 表示冒号操作符,用于colon不能用的地方,例如:
Mm a,b;
a = (BR(1),4,2,semi,6,3,8); //a = [1 4 2;6 3 8];
b = a(c_p,2); //与Matlab语句b = a(:,2)等价,b为第二列所有元素[4 3]’
e 自然对数的底 2.718281828459………..
i_o 当Matcom C++ 矩阵库函数新路由多个输出的时候,用于分割输入参数和输出参数,如:
Mm x,u,s,v;
Svd(x,i_o,u,s,v); //其中x为输入,u、s和v为输出参数。
八、函数调用
Matcom C++矩阵库为我们提供了500多个很实用的函数,对于这些函数的调用说明的详细资料可以参看 Matcom4.4以上版本的用户参考手册。这里我们要介绍的是这类函数的多变量输入输出的问题、返回值的问题。对于返回多个值时这样处理:
% .m code function
[a, b]=matlabfun(c) // C++ header file
Mm matlabfun(Mm c,i_o, Mm& a, Mm& b); // C++ 调用
matlabfun(c,i_o,a,b);
i_o起着位置分隔作用,它将输入和输出分成两部分,见实例:
Mm x,v,d;
x=wilkinson(7);
eig(x,i_o,v,d);
display(v);
display(d);
当函数接受可变数目的输入参量时需要用CL来包装,如:
Mm t;
t=linspace(0,2*pi);
plot((CL(t),sin(t),TM("*")));
1、 实例说明:
本实例主要用来说明Mm矩阵类的各种创建及初始化方法以及普通的Matcom函数的使用。
2、 程序开发步骤:
在VC环境中建立一个名为TestMatcom1的Win32 Console Application工程,按照Visual Matcom开发环境的配置(三)中的配置方法配置。
3、 源代码说明:
// testmatcomvc.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include "stdio.h"
#include "matlib.h"
intmain(intargc, char* argv[])
{
initM(MATCOM_VERSION);
//矩阵的索引
printf("*****Mm矩阵索引操作*****\n");
dMm(x);
x=magic(3);
printf("x = magic(3):\n");
display(x);
x.r()=-x.r();
x.r(2)+=1;
x.r(3,1)*=x.r(3,1);
printf("通过索引改变后的x:\n");
display(x);
//获取矩阵数据的指针
printf("\n*****获取矩阵数据的指针*****\n");
dMm(cdata);
cdata=czeros(1,4,4);
cdata(1)=1+3*i;
cdata(6)=6+4*i;
cdata(11)=2+9*i;
cdata(5)=1+i;
cdata(12)=5+i;
cdata(16)=i;
printf("用display(data)显示数据:\n");
display(cdata);
double * pr, * pi;
pr=cdata.addr();
pi=cdata.addi();
inti=0,j=0;
printf("获取data数据指针后显示:\n");
for(i=0;i<4;i++)
{
printf("\n");
for(j=0;j<4;j++)
{
if(pi[j*4+i]>0.0000001)
{
printf("%1.0f+%1.0fi ",pr[j*4+i],pi[j*4+i]);
}
else
{
printf("%4.0f ",pr[j*4+i]);
}
}
}
printf("\n");
//直接用double型数初始化数组
//由于Mm矩阵和C/C++数组的存储顺序不一致,因而需要作相应的转换
printf("\n*****直接用double型数初始化数组*****\n");
doublev_data[10]={1,2,3,4,5,6,7,8,9,10};
dMm(v);
M_VECTOR(v,v_data);
v=reshape(v,5,2);
v=transpose(v);
printf("输入的C数组:\n");
for(i=0;i<10;i++)
{
printf("%2.0f ",v_data[i]);
}
printf("\n根据输入的C数组构造的Mm矩阵:\n");
display(v);
//通过数据拷贝直接创建矩阵
printf("\n*****通过数据拷贝直接创建矩阵*****\n");
doublev_data1[10]={10,9,8,7,6,5,4,3,2,1};
memcpy(v.addr(),v_data1,10*sizeof(double));
v=reshape(v,5,2);
v=transpose(v);
printf("输入的C数组:\n");
for(i=0;i<10;i++)
{
printf("%2.0f ",v_data1[i]);
}
printf("\n根据输入的C数组构造的Mm矩阵:\n");
display(v);
//矩阵创建的时候直接赋值
printf("*****矩阵创建的时候直接赋值*****\n");
dMm (a);
a=(BR(1),2,3,4,5,semi,6,7,8,9,10,semi);
display(a);
printf("采用colon函数构造的矩阵索引显示矩阵内容:\n");
display(colon(1,10));
exitM();
return 0;
}
4、实例输出结果:
*****Mm矩阵索引操作*****
x = magic(3):
x (3x3)=9 double elements real (72 bytes) =
8 1 6
3 5 7
4 9 2
通过索引改变后的x:
x (3x3)=9 double elements real (72 bytes) =
-8 1 6
4 5 7
16 9 2
*****获取矩阵数据的指针*****
用display(data)显示数据:
cdata (4x4)=16 double elements complex (128 bytes) =
1 +3i 1 +1i 0 0
0 6 +4i 0 0
0 0 2 +9i 0
0 0 5 +1i 0 +1i
获取data数据指针后显示:
1+3i 1+1i 0 0
0 6+4i 0 0
0 0 2+9i 0
0 0 5+1i 0+1i
*****直接用double型数初始化数组*****
输入的C数组:
1 2 3 4 5 6 7 8 9 10
根据输入的C数组构造的Mm矩阵:
v (2x5)=10 double elements real (80 bytes) =
1 2 3 4 5
6 7 8 9 10
*****通过数据拷贝直接创建矩阵*****
输入的C数组:
10 9 8 7 6 5 4 3 2 1
根据输入的C数组构造的Mm矩阵:
v (2x5)=10 double elements real (80 bytes) =
10 9 8 7 6
5 4 3 2 1
*****矩阵创建的时候直接赋值*****
a (2x5)=10 double elements real (80 bytes) =
1 2 3 4 5
6 7 8 9 10
采用colon函数构造的矩阵索引显示矩阵内容:
ans (1x10)=10 double elements real (80 bytes) =
1 2 3 4 5 6 7 8 9 10
Press any key to continue
4、 实例说明:
本实例主要用来说明如何在VC中调用matcom编译后的函数进行图形绘制。
程序开发步骤:
a、在VC环境中建立一个名为建立一个名为TestMatcom2基于对话框的MFC(exe)工程。按照Visual Matcom开发环境的配置(四)中的配置方法配置。
b、在matcom中新建一个名为drawsinx.m文件,如图
c、点击右键save and run this file ,经运行后,Matlab语言的语句会被转换为C++语言的语句。
d、将生成的drawsinx.h、drawsinx.cpp(默认位置为安装目录:\matcom45\samples\Debug)和matlib.h、v4501v.lib(默认位置为安装目录:\matcom45\lib)四个文件拷贝到建立的工程TestMatcom2目录下。
e、将上一步拷贝的四个文件加入到VC工程中:工程->添加工程->文件,选择刚才拷贝到TestMatcom2目录下的四个文件。
f、在TestMatcom2.cpp中添加头文件,
#include "matlib.h"
#include "drawsinx.h"
如图所示:
g、为工程建立界面:添加一个Button按扭控件;添加两个Edit box控件,用于显示数据;添加三个Static text控件,两个显示文字,一个显示图形,将显示图形的Static text的ID设置为IDC_DRAWPIC。其他所有控件属性保持默认。如图所示:
h、按Ctrl+W为控件添加变量。如图所示
i、为IDC_BUTTON1按钮添加消息响应,代码如下:
void CTestMatcom2Dlg::OnButton1()
{
// TODO: Add your control notification handler code here
UpdateData(TRUE);
initM(MATCOM_VERSION); //初始化matcom库函数
CWnd *p1 = NULL;
p1=(CWnd *)GetDlgItem(IDC_DRAWPIC); //得到用于显示图像Static text控件的ID
Mm plothandle = winaxes(p1->m_hWnd); //将Static text的句柄设置赋给画图句柄
drawsinx(m_Edit1,m_Edit2);
exitM();//结束库函数的调用
}
注意:如果在编译中出现下列错误:
fatal error C1010: unexpected end of file while looking for precompiled header directive
进行下列设置:工程->设置-> C/C++ 选择precompiled headers 选择第一或第二项:自动选择预补偿页眉,如图所示
j、运行程序,输入左端点和右端点,点击”画图”按钮,结果如图所示:
5、 源代码说明:
本例介绍了在VC环境下,借助Matcom第三方工具进行混合编程的另一配置方法,并且基本上实现了VC和Matlab的无缝结合,本例中将Static text的句柄设置赋给Matlab画图句柄,使Matlab画的图形能够在VC控件上显示,关于这方面的问题请参考下一节内容《Matcom C++矩阵库的图形和图像显示功能》。
利用Matcom C++矩阵库的图形函数可以实现数据的二维和三维显示,并且可以实现图像的显示。Matcom C++矩阵库的图形函数与Matlab的图形函数的名称、属性及使用方法几乎完全一样。正因为如此,Matcom C++矩阵库为图形函数提供的说明比较简单,如果读者在使用过程中碰到问题的话,可以查看Matlab相应的函数的使用方法及效果,然后使用Matcom的图形函数即可。
Matcom C++矩阵库的图形函数可以直接向Windows窗口上绘图,因而可以在Visual C++ MFC工程中直接向MFC窗口中绘图。其实现方式如下:
Mm pos;
//采用winaxes函数构造Matcom绘图的图形窗口句柄
plot_handle = winaxes(m_pMainWnd->m_hWnd);
pos = (BR(100),100,400,200);
//设置Matcom窗口的位置
Set(plot_handle,TM(“RealPosition”),pos);
//采用plot函数向图形窗口中绘制曲线
plot((CL(rand(1,50))));
在使用Matcom C++矩阵库的图形函数的时候,经常需要用get函数得到图形绘制窗口的属性以及用set函数设置图形绘制窗口的属性。Matcom C++矩阵库通过图形窗口的属性来控制图形窗口的所有外观显示风格甚至包括图形窗口中显示曲线或者图像的数据。
Matcom C++矩阵库常用的图形窗口有两种,一种是绘制曲线Line类型图形窗口,一种是显示图像的Image类型窗口。他们与Axes和Figure窗口的对应关系如图示。即Line窗口和Image窗口是从Axes窗口派生的,Axes窗口又是从Figure窗口派生的。至于其他的图形窗口如Patch等,也都是与Line和Image窗口处于同一个派生层面上。
Figure |
Axes |
Line |
Image |
Patch |
……….. |
Matcom的Figure窗口的句柄被称为Figure类型的句柄,Matcom的Image窗口的句柄被称为之为Image类型的句柄,它们都是Mm类型的对象。Figure类型的句柄、Axes类型的句柄、Line类型的句柄和Image类型的句柄的属性有很多,读者可以参考Matcom函数说明中关于Figure、Line和Image等函数的说明,或者参考Matlab相关的帮助文档。下面通过几个例子介绍如何得到和改变Matcom窗口句柄属性。
//gcf函数可以看作get current figure的缩写,其功能是得到当前Figure图形窗口的句柄
//隐藏当前Figure图形窗口菜单中的about项
set(gcf(),TM(“MenuAbout”),TM(“Off”));
//隐藏当前Figure图形窗口菜单中的所有菜单项
set(gcf(),TM(“MenuBar”),TM(“None”));
//改变当前窗口的图标,将其更换为gm.ico所代表的图标
set(gcf(),TM(“IconFile”),TM(“gm.ico”));
1、 实例的最终显示界面:
2、实例说明
本实例采用Matcom的图形绘制函数在MFC的窗口内绘图。
(a) 用Visual C++60创建一个单文档的MFC工程MatcomGraphy。
(b)在View类中相关定义
public:
Mmm_h; //第一个图的Matcom句柄
Mmm_data; //第一个图的数据
Mmm_h1; //第二个图的Matcom句柄
Mmm_data1; //第二个图的数据
intm_hFlag;
BOOLisInitMatcom;
(c)添加Matcom及图形绘制函数的初始化函数。
在view类的OnInitialUpdate函数中加入下列初始化的语句。
voidCMatcomGraphyView::OnInitialUpdate()
{
CView::OnInitialUpdate();
// TODO: Add your specialized code here and/or call the base class
if(!isInitMatcom)
{
initM(MATCOM_VERSION);
isInitMatcom=1;
m_h = winaxes(m_hWnd);
this->m_hFlag=1;
axesposition(10,10,100,100);
doublebounddata[4]={0,2*3.1415926,-1,1};
Mmmbound;
M_VECTOR(mbound,bounddata);
//axis
title((CL(TM("COS函数图形"))));
xlabel((CL(TM("x"))));
ylabel((CL(TM("y"))));
set(m_h,(CL(TM("Color")),TM("black")));
set(m_h,(CL(TM("Box")),TM("on")));
//axis(mbound);
Mmx,y;
x=linspace(0,2*pi,100);
y=mcos(x);
m_data = y;
plot((CL(m_data),TM("y")));
m_h1 = winaxes(m_hWnd);
Mmpos;
pos=(BR(240),240,200,200);
set(m_h1,TM("RealPosition"),pos);
Mmcolor;
color = zeros(1,3);
color.r(1)=0;color.r(2)=0;color.r(3)=0;
set(m_h,TM("color"),color);
m_data1 = randn(1,50);
plot((CL(m_data1),TM("b")));
double * phandle=NULL;
phandle = m_h1.addr();
intnrow,ncol;
nrow = m_h1.rows();
ncol = m_h1.cols();
title(CL(TM("当前的图形")));
}
}
(d)为了保证绘制图形的大小能够跟随主窗口的变化而变化,在view类的OnSize函数中加入处理代码。并且IsMatcomHanleValid函数将在下面进行说明,其功能是判断当前的Matcom图形绘制句柄是否有效。
voidCMatcomGraphyView::OnSize(UINTnType, intcx, intcy)
{
CView::OnSize(nType, cx, cy);
// TODO: Add your message handler code here
if(this->m_hFlag>0)
{
//axes(m_h);
if(IsMatcomHanleValid(m_h))
{
Mmpos;
pos = zeros(1,4);
pos.r(1)=0;pos.r(2)=0;
pos.r(3)=cx;pos.r(4)=cy/2;
set(m_h,TM("RealPosition"),pos);
}
if(IsMatcomHanleValid(m_h1))
{
Mmpos;
pos = zeros(1,4);
pos.r(1)=0;pos.r(2)=cy/2;
pos.r(3)=cx;pos.r(4)=cy/2;
set(m_h1,TM("RealPosition"),pos);
}
//axesposition(0,0,cx,cy);
}
}
(e)在工具栏上添加一个新的按钮,其ID为ID_DRAWNOISE,然后通过classwizard为view类添加新的消息响应函数OnDrawnoise()。中国函数实现的功能为:在第一个图上绘制新的曲线。其实现代码如下:
voidCMatcomGraphyView::OnDrawnoise()
{
// TODO: Add your command handler code here
if(this->isInitMatcom)
{
MmisHold;
m_data = randn(1,200);
axes(CL(m_h));
hold(TM("off"));
isHold = ishold();
if((int)(isHold.r(1)))
{
}
else
{
clf();
m_h = winaxes(m_hWnd);
m_h1 = winaxes(m_hWnd);
CRectrect;
GetClientRect(&rect);
intcx=rect.Width();
intcy=rect.Height();
if(IsMatcomHanleValid(m_h))
{
Mmpos;
pos = zeros(1,4);
pos.r(1)=0;pos.r(2)=0;
pos.r(3)=cx;pos.r(4)=cy/2;
set(m_h,TM("RealPosition"),pos);
}
axes(CL(m_h));
plot((CL(m_data),TM("y")));
title((CL(TM("随机数图形"))));
xlabel((CL(TM("x"))));
ylabel((CL(TM("y"))));
set(m_h,(CL(TM("Color")),TM("black")));
set(m_h,(CL(TM("Box")),TM("on")));
if(IsMatcomHanleValid(m_h1))
{
Mmpos;
pos = zeros(1,4);
pos.r(1)=0;pos.r(2)=cy/2;
pos.r(3)=cx;pos.r(4)=cy/2;
set(m_h1,TM("RealPosition"),pos);
}
axes(CL(m_h1));
plot(CL(m_data1));
}
}
}
(f)关于IsMatcomHanleValid函数的实现。IsMatcomHanleValid函数用于判断Matcom绘图句柄是否有效,由于Matcom绘图句柄在失效的时候不能自动告诉开发者,但是可以通过Matcom提供的ishandle()函数实现IsMatcomHanleValid函数的功能。其实现代码如下。
boolCMatcomGraphyView::IsMatcomHanleValid(Mmhandle)
{
if(this->isInitMatcom)
{
Mmlen,isH;
isH = ishandle(handle);
len = length(isH);
if(((int)(len.r(1)))&&((int)(isH.r(1))))
{
returntrue;
}
else
{
returnfalse;
}
}
else
{
returnfalse;
}
}
1、 实例的最终界面
实例说明
当读者运行实例3时,会发现每次在Matcom图形窗口上绘制曲线的时候,窗口内容闪烁的很厉害。本实例利用set函数设置Matcom图形窗口数据缓冲区数据以及用drawnow函数强制Matcom图形窗口绘制数据缓冲区的数据可以达到避免图形绘制的时候窗口闪烁的效果。
(a) 用Visual C++60创建一个单文档的MFC工程MatcomGraphy,或者将实例3的 应用程序拷贝一份,直接修改代码。
(b) 在View类中相关定义
Mmm_h; // 第一个图的MATCOM句柄
Mmm_data; //第一图的数据
Mmm_h1; //第二个图的MATCOM句柄
Mmm_data1; //第二个图的数据
Mmm_hline1,m_hline;
intm_nScopeTimerID;/*定时器ID*/
intm_scopeflag; /*第一个图是否继续显示标志*/
intm_scopeflag1;/*第二个图是否继续显示标志*/
intm_hFlag;/*图形窗口是否创建*/
BOOLisInitMatcom; //MATCOM的初始化标志
(c) 在工具栏上添加4个按钮,并创建其消息响应函数。
(d) 完整代码如下:
----------------------------MatcomGraphyView.h -----------------------------
// MatcomGraphyView.h : interface of the CMatcomGraphyView class
//
/////////////////////////////////////////////////////////////////////////////
#if !defined(AFX_MATCOMGRAPHYVIEW_H__6778F822_4B3C_4AD2_8F1D_83AE249F6134__INCLUDED_)
#define AFX_MATCOMGRAPHYVIEW_H__6778F822_4B3C_4AD2_8F1D_83AE249F6134__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "matlib.h"
classCMatcomGraphyView : publicCView
{
protected: // create from serialization only
CMatcomGraphyView();
DECLARE_DYNCREATE(CMatcomGraphyView)
// Attributes
public:
CMatcomGraphyDoc* GetDocument();
// Operations
public:
Mmm_h; // 第一个图的MATCOM句柄
Mmm_data; //第一图的数据
Mmm_h1; //第二个图的MATCOM句柄
Mmm_data1; //第二个图的数据
Mmm_hline1,m_hline;
intm_nScopeTimerID;/*定时器ID*/
intm_scopeflag; /*第一个图是否继续显示标志*/
intm_scopeflag1;/*第二个图是否继续显示标志*/
intm_hFlag;/*图形窗口是否创建*/
BOOLisInitMatcom; //MATCOM的初始化标志
boolIsMatcomHanleValid(Mmhandle);
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMatcomGraphyView)
public:
virtualvoidOnDraw(CDC* pDC); // overridden to draw this view
virtualBOOLPreCreateWindow(CREATESTRUCT& cs);
virtualvoidOnInitialUpdate();
protected:
virtualBOOLOnPreparePrinting(CPrintInfo* pInfo);
virtualvoidOnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
virtualvoidOnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
//}}AFX_VIRTUAL
// Implementation
public:
virtual ~CMatcomGraphyView();
#ifdef _DEBUG
virtualvoidAssertValid() const;
virtualvoidDump(CDumpContext& dc) const;
#endif
protected:
// Generated message map functions
protected:
//{{AFX_MSG(CMatcomGraphyView)
afx_msgvoidOnSize(UINTnType, intcx, intcy);
afx_msgvoidOnDrawnoise();
afx_msgvoidOnTimer(UINTnIDEvent);
afx_msgvoidOnScope1();
afx_msgvoidOnScope();
afx_msgvoidOnCosgraph();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
#ifndef _DEBUG // debug version in MatcomGraphyView.cpp
inlineCMatcomGraphyDoc* CMatcomGraphyView::GetDocument()
{ return (CMatcomGraphyDoc*)m_pDocument; }
#endif
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_MATCOMGRAPHYVIEW_H__6778F822_4B3C_4AD2_8F1D_83AE249F6134__INCLUDED_)
----------------------------MatcomGraphyView.cpp -----------------------------
// MatcomGraphyView.cpp : implementation of the CMatcomGraphyView class
//
#include "stdafx.h"
#include "MatcomGraphy.h"
#include "MatcomGraphyDoc.h"
#include "MatcomGraphyView.h"
#ifdef _DEBUG
#define newDEBUG_NEW
#undef THIS_FILE
staticcharTHIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CMatcomGraphyView
IMPLEMENT_DYNCREATE(CMatcomGraphyView, CView)
BEGIN_MESSAGE_MAP(CMatcomGraphyView, CView)
//{{AFX_MSG_MAP(CMatcomGraphyView)
ON_WM_SIZE()
ON_COMMAND(ID_DRAWNOISE, OnDrawnoise)
ON_WM_TIMER()
ON_COMMAND(ID_SCOPE1, OnScope1)
ON_COMMAND(ID_SCOPE, OnScope)
ON_COMMAND(ID_COSGRAPH, OnCosgraph)
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CMatcomGraphyView construction/destruction
CMatcomGraphyView::CMatcomGraphyView()
{
// TODO: add construction code here
isInitMatcom=0;
m_hFlag=0;
m_scopeflag = 0;
m_nScopeTimerID =0;
m_scopeflag1=0;
}
CMatcomGraphyView::~CMatcomGraphyView()
{
if(isInitMatcom)
{
exitM();
isInitMatcom=0;
}
}
BOOLCMatcomGraphyView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
returnCView::PreCreateWindow(cs);
}
/////////////////////////////////////////////////////////////////////////////
// CMatcomGraphyView drawing
voidCMatcomGraphyView::OnDraw(CDC* pDC)
{
CMatcomGraphyDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
}
/////////////////////////////////////////////////////////////////////////////
// CMatcomGraphyView printing
BOOLCMatcomGraphyView::OnPreparePrinting(CPrintInfo* pInfo)
{
// default preparation
returnDoPreparePrinting(pInfo);
}
voidCMatcomGraphyView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: add extra initialization before printing
}
voidCMatcomGraphyView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: add cleanup after printing
}
/////////////////////////////////////////////////////////////////////////////
// CMatcomGraphyView diagnostics
#ifdef _DEBUG
voidCMatcomGraphyView::AssertValid() const
{
CView::AssertValid();
}
voidCMatcomGraphyView::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}
CMatcomGraphyDoc* CMatcomGraphyView::GetDocument() // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMatcomGraphyDoc)));
return (CMatcomGraphyDoc*)m_pDocument;
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CMatcomGraphyView message handlers
voidCMatcomGraphyView::OnInitialUpdate()
{
CView::OnInitialUpdate();
// TODO: Add your specialized code here and/or call the base class
if(!isInitMatcom)
{
initM(MATCOM_VERSION);
isInitMatcom=1;
m_h = winaxes(m_hWnd);
this->m_hFlag=1;
axesposition(10,10,100,100);
doublebounddata[4]={0,2*3.1415926,-1,1};
Mmmbound;
M_VECTOR(mbound,bounddata);
axis(mbound);
//axis
title((CL(TM("COS函数图形"))));
xlabel((CL(TM("x"))));
ylabel((CL(TM("y"))));
set(m_h,(CL(TM("Color")),TM("black")));
set(m_h,(CL(TM("Box")),TM("on")));
//axis(mbound);
Mmx,y;
x=linspace(0,2*pi,100);
y=mcos(x);
m_data = y;
m_hline=plot((CL(x),m_data,TM("y")));
m_h1 = winaxes(m_hWnd);
Mmpos;
pos=(BR(240),240,200,200);
set(m_h1,TM("RealPosition"),pos);
Mmcolor;
color = zeros(1,3);
color.r(1)=0;color.r(2)=0;color.r(3)=0;
set(m_h,TM("color"),color);
m_data1 = randn(1,150);
m_hline1=plot((CL(m_data1),TM("b")));
set(m_hline1,TM("Xdata"),linspace(1,150,150));
set(m_hline1,TM("Ydata"),m_data1);
drawnow();
double * phandle=NULL;
phandle = m_h1.addr();
intnrow,ncol;
nrow = m_h1.rows();
ncol = m_h1.cols();
title(CL(TM("当前的图形")));
}
}
voidCMatcomGraphyView::OnSize(UINTnType, intcx, intcy)
{
CView::OnSize(nType, cx, cy);
// TODO: Add your message handler code here
if(this->m_hFlag>0)
{
//axes(m_h);
if(IsMatcomHanleValid(m_h))
{
Mmpos;
pos = zeros(1,4);
pos.r(1)=0;pos.r(2)=0;
pos.r(3)=cx;pos.r(4)=cy/2;
set(m_h,TM("RealPosition"),pos);
}
if(IsMatcomHanleValid(m_h1))
{
Mmpos;
pos = zeros(1,4);
pos.r(1)=0;pos.r(2)=cy/2;
pos.r(3)=cx;pos.r(4)=cy/2;
set(m_h1,TM("RealPosition"),pos);
}
//axesposition(0,0,cx,cy);
}
}
voidCMatcomGraphyView::OnDrawnoise()
{
// TODO: Add your command handler code here
if(this->isInitMatcom)
{
m_data = randn(1,200);
if(IsMatcomHanleValid(m_h))
{
set(m_hline,TM("Xdata"),linspace(1,200,200));
set(m_hline,TM("Ydata"),m_data);
drawnow();
}
}
}
boolCMatcomGraphyView::IsMatcomHanleValid(Mmhandle)
{
if(this->isInitMatcom)
{
Mmlen,isH;
isH = ishandle(handle);
len = length(isH);
if(((int)(len.r(1)))&&((int)(isH.r(1))))
{
returntrue;
}
else
{
returnfalse;
}
}
else
{
returnfalse;
}
}
voidCMatcomGraphyView::OnTimer(UINTnIDEvent)
{
// TODO: Add your message handler code here and/or call default
if(this->isInitMatcom)
{
if(IsMatcomHanleValid(m_h))
{
if(this->m_scopeflag1)
{
Mmydata,tmp;
ydata = get(m_hline1,TM("Ydata"));
intnydatalen;
nydatalen = ydata.rows()*ydata.cols();
for(inti=1;i<nydatalen;i++)
{
ydata.r(i)=ydata.r(i+1);
}
tmp = randM(1,1);
ydata.r(nydatalen) = tmp.r(1);
set(m_hline1,TM("Ydata"),ydata);
drawnow();
}
if(this->m_scopeflag)
{
Mmydata;
ydata = get(m_hline,TM("Ydata"));
intnydatalen;
doublelastYdata;
nydatalen = ydata.rows()*ydata.cols();
lastYdata = ydata.r(nydatalen);
for(inti=nydatalen;i>=2;i--)
{
ydata.r(i)=ydata.r(i-1);
}
ydata.r(1) = lastYdata;
set(m_hline,TM("Ydata"),ydata);
drawnow();
}
}
}
CView::OnTimer(nIDEvent);
}
/*第二个实时曲线绘制窗口的启动和暂停函数*/
voidCMatcomGraphyView::OnScope1()
{
// TODO: Add your command handler code here
if(this->isInitMatcom)
{
if(IsMatcomHanleValid(m_h1))
{
if(m_scopeflag1==0)
{
Mmydata,tmp;
ydata = get(m_hline1,TM("Ydata"));
intnydatalen;
nydatalen = ydata.rows()*ydata.cols();
for(inti=1;i<nydatalen;i++)
{
ydata.r(i)=ydata.r(i+1);
}
tmp = randM(1,1);
ydata.r(nydatalen) = tmp.r(1);
set(m_hline1,TM("Ydata"),ydata);
drawnow();
if(m_nScopeTimerID<=0)
{
m_nScopeTimerID = SetTimer(1,50,NULL);
}
m_scopeflag1 = 1;
}
else
{
m_scopeflag1 =0 ;
}
}
}
}
/*第一个实时曲线绘制窗口的启动和暂停函数*/
voidCMatcomGraphyView::OnScope()
{
// TODO: Add your command handler code here
if(this->isInitMatcom)
{
if(IsMatcomHanleValid(m_h))
{
if(m_scopeflag==0)
{
Mmydata;
ydata = get(m_hline,TM("Ydata"));
intnydatalen;
doublelastYdata;
nydatalen = ydata.rows()*ydata.cols();
lastYdata = ydata.r(nydatalen);
for(inti=nydatalen;i>=2;i--)
{
ydata.r(i)=ydata.r(i-1);
}
ydata.r(1) = lastYdata;
set(m_hline,TM("Ydata"),ydata);
drawnow();
if(m_nScopeTimerID<=0)
{
m_nScopeTimerID = SetTimer(1,10,NULL);
}
m_scopeflag = 1;
}
else
{
m_scopeflag =0 ;
}
}
}
}
/*将第一个切换为绘制移动的余弦曲线*/
voidCMatcomGraphyView::OnCosgraph()
{
// TODO: Add your command handler code here
if(this->isInitMatcom)
{
if(IsMatcomHanleValid(m_h))
{
Mmx,y;
x=linspace(0,2*pi,100);
y=mcos(x);
set(m_hline,TM("Xdata"),x);
set(m_hline,TM("Ydata"),y);
drawnow();
}
}
}
1、实例最后结果:
2、实例说明
本实例用来说明采用Matcom C++矩阵库的图形函数进行各种类型的数据显示功能,其中包括极坐标、三维数据显示、三维数据等高线显示、二维数据显示、二维数据条形图及阴影显示等。
(a) 用Visual C++60创建一个单文档的MFC工程Matcom2D3D。
(b) 添加相应的菜单,如图所示:
(c) 重载WM_SIZE的消息响应函数OnSize及OnInitialUpdate函数。
添加下列辅助函数。
voidClearData(Mmm_h); //清除句柄为m_h的图形窗口的数据
voidResizePlot(); //重新布置当前的Matcom的图形窗口
boolIsMatcomHandleValid(Mmm_h); //判断Matcom图形句柄是否有效
3、源程序清单
--------------------------------Matcom2D3DView.h----------------------------------
// Matcom2D3DView.h : interface of the CMatcom2D3DView class
//
/////////////////////////////////////////////////////////////////////////////
#if !defined(AFX_MATCOM2D3DVIEW_H__4E184416_9F38_4ABE_96C1_3F3533AA95E8__INCLUDED_)
#define AFX_MATCOM2D3DVIEW_H__4E184416_9F38_4ABE_96C1_3F3533AA95E8__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "matlib.h"
classCMatcom2D3DView : publicCView
{
protected: // create from serialization only
CMatcom2D3DView();
DECLARE_DYNCREATE(CMatcom2D3DView)
// Attributes
public:
CMatcom2D3DDoc* GetDocument();
// Operations
public:
public:
Mmm_h,m_hplot;/*第一个MATCOM图形窗口的Figure句柄和Axes句柄*/
Mmm_h1,m_hplot1;/*第二个MATCOM图形窗口的Figure句柄和Axes句柄*/
boolisTwoPlot;/*当前View中是否有两个MATCOM图形绘制窗口*/
boolisInit;/*MATCOM C++矩阵库是否初始化*/
Mmm_2ddata;/*二维图形y轴数据*/
Mmm_2ddatax;/*二维图形x轴数据*/
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CMatcom2D3DView)
public:
virtualvoidOnDraw(CDC* pDC); // overridden to draw this view
virtualBOOLPreCreateWindow(CREATESTRUCT& cs);
virtualvoidOnInitialUpdate();
protected:
virtualBOOLOnPreparePrinting(CPrintInfo* pInfo);
virtualvoidOnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
virtualvoidOnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
//}}AFX_VIRTUAL
// Implementation
public:
voidClearData(Mmm_h);
voidResizePlot();
boolIsMatcomHandleValid(Mmm_h);
virtual ~CMatcom2D3DView();
#ifdef _DEBUG
virtualvoidAssertValid() const;
virtualvoidDump(CDumpContext& dc) const;
#endif
protected:
// Generated message map functions
protected:
//{{AFX_MSG(CMatcom2D3DView)
afx_msgvoidOnSize(UINTnType, intcx, intcy);
afx_msgvoidOn2DGraphy();
afx_msgvoidOn2DShadow();
afx_msgvoidOn3dcontour();
afx_msgvoidOn3DGraphy();
afx_msgvoidOn2dcompass();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
#ifndef _DEBUG // debug version in Matcom2D3DView.cpp
inlineCMatcom2D3DDoc* CMatcom2D3DView::GetDocument()
{ return (CMatcom2D3DDoc*)m_pDocument; }
#endif
/////////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif // !defined(AFX_MATCOM2D3DVIEW_H__4E184416_9F38_4ABE_96C1_3F3533AA95E8__INCLUDED_)
--------------------------------Matcom2D3DView.cpp----------------------------------
// Matcom2D3DView.cpp : implementation of the CMatcom2D3DView class
//
#include "stdafx.h"
#include "Matcom2D3D.h"
#include "Matcom2D3DDoc.h"
#include "Matcom2D3DView.h"
#ifdef _DEBUG
#define newDEBUG_NEW
#undef THIS_FILE
staticcharTHIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CMatcom2D3DView
IMPLEMENT_DYNCREATE(CMatcom2D3DView, CView)
BEGIN_MESSAGE_MAP(CMatcom2D3DView, CView)
//{{AFX_MSG_MAP(CMatcom2D3DView)
ON_WM_SIZE()
ON_COMMAND(ID_2DGraphy, On2DGraphy)
ON_COMMAND(ID_2DShadow, On2DShadow)
ON_COMMAND(ID_3DCONTOUR, On3dcontour)
ON_COMMAND(ID_3DGraphy, On3DGraphy)
ON_COMMAND(ID_2DCOMPASS, On2dcompass)
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT, CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CMatcom2D3DView construction/destruction
CMatcom2D3DView::CMatcom2D3DView()
{
// TODO: add construction code here
isInit = false;
isTwoPlot=false;
if(!isInit)
{
initM(MATCOM_VERSION);
isInit = true;
}
}
CMatcom2D3DView::~CMatcom2D3DView()
{
}
BOOLCMatcom2D3DView::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
returnCView::PreCreateWindow(cs);
}
/////////////////////////////////////////////////////////////////////////////
// CMatcom2D3DView drawing
voidCMatcom2D3DView::OnDraw(CDC* pDC)
{
CMatcom2D3DDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
}
/////////////////////////////////////////////////////////////////////////////
// CMatcom2D3DView printing
BOOLCMatcom2D3DView::OnPreparePrinting(CPrintInfo* pInfo)
{
// default preparation
returnDoPreparePrinting(pInfo);
}
voidCMatcom2D3DView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: add extra initialization before printing
}
voidCMatcom2D3DView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
// TODO: add cleanup after printing
}
/////////////////////////////////////////////////////////////////////////////
// CMatcom2D3DView diagnostics
#ifdef _DEBUG
voidCMatcom2D3DView::AssertValid() const
{
CView::AssertValid();
}
voidCMatcom2D3DView::Dump(CDumpContext& dc) const
{
CView::Dump(dc);
}
CMatcom2D3DDoc* CMatcom2D3DView::GetDocument() // non-debug version is inline
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CMatcom2D3DDoc)));
return (CMatcom2D3DDoc*)m_pDocument;
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CMatcom2D3DView message handlers
boolCMatcom2D3DView::IsMatcomHandleValid(Mmhandle)
{
if(this->isInit)
{
Mmlen,isH;
isH = ishandle(handle);
len = length(isH);
if(((int)(len.r(1)))&&((int)(isH.r(1))))
{
returntrue;
}
else
{
returnfalse;
}
}
else
{
returnfalse;
}
}
voidCMatcom2D3DView::OnInitialUpdate()
{
CView::OnInitialUpdate();
// TODO: Add your specialized code here and/or call the base class
m_h1 = winaxes(this ->m_hWnd);
Mmposition=zeros(1,4);
position.r(1) = 0;
position.r(2) = 0;
position.r(3) = 0;
position.r(4) = 0;
set(m_h1,TM("RealPosition"),position);
m_h = winaxes(this->m_hWnd);
m_2ddatax = linspace(0,2*pi,100);
m_2ddata = msin(m_2ddatax);
m_hplot = plot((CL(m_2ddatax),m_2ddata));
axes(CL(m_h1));
m_hplot1 = plot((CL(m_2ddatax),m_2ddata));
set(m_h,TM("NextPlot"),TM("replace"));
set(m_h1,TM("NextPlot"),TM("replace"));
}
voidCMatcom2D3DView::OnSize(UINTnType, intcx, intcy)
{
CView::OnSize(nType, cx, cy);
// TODO: Add your message handler code here
if(isInit)
{
if(!isTwoPlot)
{
if(this->IsMatcomHandleValid(m_h))
{
Mmposition=zeros(1,4);
position.r(1) = 0;
position.r(2) = 0;
position.r(3) = cx;
position.r(4) = cy;
set(m_h,TM("RealPosition"),position);
}
if(this->IsMatcomHandleValid(m_h1))
{
Mmposition=zeros(1,4);
position.r(1) = 0;
position.r(2) = 0;
position.r(3) = 0;
position.r(4) = 0;
set(m_h1,TM("RealPosition"),position);
}
}
else
{
if(this->IsMatcomHandleValid(m_h))
{
Mmposition=zeros(1,4);
position.r(1) = 0;
position.r(2) = 0;
position.r(3) = cx;
position.r(4) = cy/2;
set(m_h,TM("RealPosition"),position);
}
if(this->IsMatcomHandleValid(m_h1))
{
Mmposition=zeros(1,4);
position.r(1) = 0;
position.r(2) = cy/2;
position.r(3) = cx;
position.r(4) = cy/2;
set(m_h1,TM("RealPosition"),position);
}
}
}
}
/*测试二维数据条形图显示*/
voidCMatcom2D3DView::On2DGraphy()
{
// TODO: Add your command handler code here
if(isInit)
{
if(this->IsMatcomHandleValid(m_h))
{
axes(CL(m_h));
Mmcolor = zeros(1,3);
color.r(1)=0;
color.r(2)=1;
color.r(2)=1;
ClearData(m_hplot);
m_hplot = bar(m_2ddatax,m_2ddata);
set(m_hplot,TM("color"),color);
axes(CL(m_h1));
ClearData(m_hplot1);
//m_hplot1 = barh(m_2ddatax,m_2ddata,TM("b"));
m_2ddatax = linspace(0,2*pi,50);
m_2ddata = msin(m_2ddatax);
m_hplot1 = barh(m_2ddatax,m_2ddata,TM("b"));
//set(m_hplot1,(CL(TM("FaceColor")),color));
this->isTwoPlot = true;
ResizePlot();
}
}
}
/*二维数据阴影显示*/
voidCMatcom2D3DView::On2DShadow()
{
// TODO: Add your command handler code here
if(isInit)
{
if(this->IsMatcomHandleValid(m_h))
{
axes(CL(m_h));
Mmcolor = zeros(1,3);
color.r(1)=0;
color.r(2)=1;
color.r(2)=1;
ClearData(m_hplot);
m_hplot = area((CL(m_2ddatax),m_2ddata));
set(m_hplot,TM("color"),color);
this->isTwoPlot = false;
ResizePlot();
}
}
}
/*三维数据网格及等高线显示*/
voidCMatcom2D3DView::On3dcontour()
{
// TODO: Add your command handler code here
if(isInit)
{
if(this->IsMatcomHandleValid(m_h))
{
axes(CL(m_h));
ClearData(m_hplot);
Mmdata=peaks();
m_hplot = mesh(CL(data));
view(10, 0);
this->isTwoPlot = true;
Mmcolor;
color = zeros(1,3);
color.r(1) = 0;
color.r(2) = 0;
color.r(3) = 1;
set(m_hplot,TM("color"),color);
axes(CL(m_h1));
ClearData(m_hplot1);
m_hplot1 = contour(CL(data));
ResizePlot();
}
}
}
/*测试三维数据条形图显示*/
voidCMatcom2D3DView::On3DGraphy()
{
// TODO: Add your command handler code here
if(isInit)
{
if(this->IsMatcomHandleValid(m_h))
{
axes(CL(m_h));
Mmcolor = zeros(1,3);
color.r(1)=0;
color.r(2)=1;
color.r(2)=1;
ClearData(m_hplot);
m_hplot = bar3(m_2ddatax,m_2ddata);
set(m_hplot,TM("color"),color);
axes(CL(m_h1));
ClearData(m_hplot1);
set(m_hplot1,TM("color"),color);
m_2ddatax = linspace(0,2*pi,50);
m_2ddata = msin(m_2ddatax);
m_hplot1 = bar3h(m_2ddatax,m_2ddata,TM("b"));
this->isTwoPlot = true;
ResizePlot();
}
}
}
/*二维数据极坐标显示*/
voidCMatcom2D3DView::On2dcompass()
{
// TODO: Add your command handler code here
if(isInit)
{
if(this->IsMatcomHandleValid(m_h))
{
axes(CL(m_h));
Mmcolor = zeros(1,3);
color.r(1)=1;
color.r(2)=0;
color.r(3)=0;
ClearData(m_hplot);
Mmxdata,ydata;
xdata = zeros(1,4);
ydata = zeros(1,4);
xdata.r(1) = 1;
xdata.r(2) = -2;
xdata.r(3) = 3;
xdata.r(4) = 4;
ydata.r(1) = 4;
ydata.r(2) = -3;
ydata.r(3) = 2;
ydata.r(4) = -1;
//ydata.
m_hplot = compass(xdata,ydata);
set(m_hplot,TM("color"),color);
this->isTwoPlot = false;
ResizePlot();
}
}
}
/*重新布置当前MATCOM的图形窗口*/
voidCMatcom2D3DView::ResizePlot()
{
if(isInit)
{
CRectrect;
GetClientRect(&rect);
intcx=rect.Width();
intcy=rect.Height();
if(!isTwoPlot)
{
if(this->IsMatcomHandleValid(m_h))
{
Mmposition=zeros(1,4);
position.r(1) = 0;
position.r(2) = 0;
position.r(3) = cx;
position.r(4) = cy;
set(m_h,TM("RealPosition"),position);
}
if(this->IsMatcomHandleValid(m_h1))
{
Mmposition=zeros(1,4);
position.r(1) = 0;
position.r(2) = 0;
position.r(3) = 0;
position.r(4) = 0;
set(m_h1,TM("RealPosition"),position);
}
}
else
{
if(this->IsMatcomHandleValid(m_h))
{
Mmposition=zeros(1,4);
position.r(1) = 0;
position.r(2) = 0;
position.r(3) = cx;
position.r(4) = cy/2;
set(m_h,TM("RealPosition"),position);
}
if(this->IsMatcomHandleValid(m_h1))
{
Mmposition=zeros(1,4);
position.r(1) = 0;
position.r(2) = cy/2;
position.r(3) = cx;
position.r(4) = cy/2;
set(m_h1,TM("RealPosition"),position);
}
}
}
}
/*清空当前图形窗口缓冲区的数据*/
voidCMatcom2D3DView::ClearData(Mmhandle)
{
if(isInit)
{
if(this->IsMatcomHandleValid(handle))
{
Mmtemp;
set(handle,TM("Xdata"),temp);
set(handle,TM("Ydata"),temp);
set(handle,TM("Zdata"),temp);
}
}
}