- 积分
- 1057
- 明经币
- 个
- 注册时间
- 2004-3-5
- 在线时间
- 小时
- 威望
-
- 金钱
- 个
- 贡献
-
- 激情
-
|
本帖最后由 作者 于 2004-4-1 20:22:18 编辑
【原创】 ishou, ishou@sina.com
初学、初用AutoCAD提供的二次开发程序语言者,一般会觉得这些语言不错,一但熟悉这些语言、尤其是深究之,会逐渐发现这些语言的欠缺,甚至语言架构有问题。 CAD的操作过程很随意,按下Esc键是非常平常的事,不少AutoLISP程序的运行,就是因为按下这个键后出现紊乱,以致不能正常再运行,如果程序中有开启的文件句柄,情况会更糟。。。
迄今Autodesk公司为AutoCAD提供的众多二次开发语言中没有一个理想的。把Java引入AutoCAD中也不是好事,Java是一个极端性编译型语言,凡事用Class表达,且必须在Class定义体内表述,当Class中内容较多时,程序的可读性很差!另外,其垃圾回收机制的不可预测性和对内存巨大需求,使该语言所谓的优势大打折扣。学习并用好Java,其难度和复杂度并不会比C++小。
Python语言具有很好功能和扩展性,可惜该语言有它的致命点,它对程序对齐格式有非常严格要求,多一个空格、多一个空行都会导致程序错误,这初看起来是一个很简单要求,但是,有点编程经验的人都会发觉这是非常难以避免的、并会频繁发生的错误,其情形让我想起当年FORTRAN 77程序语言对格式对齐的要求----太原始了。
虽然AutoLISP语言功能欠佳,但是不可否认,早期AutoCAD的成功,在很大程度上得益于AutoLISP !
我在国外工作的一位朋友,花费约七年的业余时间研究、设计成功一种程序语言,这里姑且称为Dx语言,该语言以ADS/ARX形式,与AuoCAD R14 ---- AutoCAD2004 很好地结合,很可能引发AutoCAD一次变革。如果能与其他CAD软件结合,譬如国产CAD,在二次开发领域,足以与AutoCAD抗衡,甚至超越后者,对CAD本身的进一步发展及其发展模式都可能发生深刻变革。不多说了,下面简要介绍一下:
Dx是一个集面向对象、面向过程、面向泛型于一体的解释性程序语言,不需要任何编译,电脑直接运行源程序码,Dx在AutoCAD里使用时,与AutoLISP一样方便,而其程序的架构、简洁性、可读性、尤其是功能等方面比AutoLISP好很多。Dx程序格式与C /C++相同或相似,其他语言的一些优秀架构在Dx中也可以见到身影。Dx并不是在简单模仿其他语言架构,除了简化、提高外,Dx也拥有本身特色的一些结构。
1) Dx的Class定义,支持"简化型"的多重继承。实际上,是在C++的普通公共单继承的基础上,同时继承任意个数的"简单型"基类:该基类上没有任何继承关系。拿Java相比,就是把Java中的接口(interface)换成"简单型"基类。这里既可以避免普通多重继承所产生的网状复杂继承关系,又可以弥补单一继承所存在的一些缺憾,另外,还省了"接口"这个概念!
class A1 { ...略... }; class A2 { ........ }; class A3 { ........ }; class B { ........ }; class C : B { ........ };
class MyDemo (A1, A2, A3) : C { static int count = 0;
public: int age = 24; str (姓名) = "赵明";
sub MyDemo() { } sub ~MyDemo() { } sub Run( ) {...} };
懂得C++的人,对上面大部分class的定义体,应该不会陌生,对于MyDemo的定义有必要说明一下,(A1, A2, A3) 就是同时继承这些"简单型"的class,而 : C是普通继承基类C。成员函数的定义以关键字sub开头 (在Class 体外定义函数时,以关键字defun开头)。这里有一个有趣功能:类的成员变量名可以为非英文,只要用括号括起来就可以了,如何存取?如下:
X = new MyDemo; name = X.[姓名];
2) Dx提供名字空间功能,通过名字空间概念,可以显著降低程序命名冲突,这对于解释型语言程序来说尤其重要。
3) Dx支持多线程(Thread)和同步机制。
4) Dx中,类的非静态成员函数也可以成为回调函数,因此,设计窗口之类的程序很方便。
5) Dx的函数定义,支持关键字参数格式: defun MyFunc (Name, age=1) { .........; }
MyFunc("John", 24); 或 MyFunc(age=24, Name="Jhon");
6) Dx的函数定义,支持参数个数可变模式: defun MyFunc (A, B, ...) { ............; }
顺便说一下,Dx中的线程主函数也可以是参数个数可变模式的函数。
7) Dx支持子函数、甚至孙函数定义,这些子孙函数拥有其长辈函数的存取权限。这个支持,进一步提高Dx程序的简洁性、可读性。
defun MyDemo :: Run . SubFun() { .....; }
8) Dx内部的内存回收机制优于AutoLISP、Java、C#等,Dx不仅可以回收直接产生的动态内存,而且可以自动关闭相关的各类句柄,比如文件句柄,因此,在Dx中 fclose(), EndPaint()之类的函数是多余的。程序员可以控制、清楚知道内存回收过程。程序是正常结束、还是异常中断,所有该回收的内存,程序会自动回收,所有该关闭的句柄,程序会自动关闭。
9) Dx支持C /C++中的大部分数据类型,包括64-bit整数、指针和结构(struct)位域,也支持简单型宏替换,甚至在结构定义中可以支持不同位对齐,因此与C/C++的沟通能力很强,这为Dx的扩展性提供一个很好的基础。指针的支持,可能会引起一些人的恐惧,其实大可不必,在很多情况下Dx根本用不着指针,但是指针的支持,既为一些程序高手提供便利,也为Dx与C/C++的高度兼容提供架构,Dx程序极易仿真到C/C++,反之亦然。Dx还支持货币 (Currency)、日期 (Date)、日期时间(DateTime)、复数等数据类型,此外,Dx还提供超高精度整数(Number)、超高精度实数(Decimal),两者的十进制有效数字位高达78位:
Number: -5.7896 E+77 到 5.7896+77 Decimal: -5.7896 E+39 到 5.7896+39, 小数点后精密(无失真)记录到1.0E-38
10) Dx内部定义多种数据链,并提供大量操作方法,包括迭代操作方法,处理数据非常方便。当然,其中包括 AutoLISP 的LIST 数据链,下面看看如何方便操作LIST数据链:
Lx = new LIST ( 11, 22, <33, <"$$$", PI>>, "Hello!"); Lx的当前值(实际上是一种高级指针): (11 22 (33 ("$$$" 3.14159)) "Hello")
Lx.len(); 得到LIST链主结点个数: 4
Lx++; Lx的当前值: (22 (33 ("$$$" 3.14159)) "Hello")
Lx -> Value; 得到结果: 22
Lx++; Lx的当前值: ((33 ("$$$" 3.14159)) "Hello")
Lx->Value; 与 Lx[0].Value; 得到相同结果: 33
Lx[1].Value; 得到结果: "$$$"
Lx[1].Value = "NewX"; Lx的当前值是: ((33 ("newX" 3.14159)) "Hello")
Lx--; Lx的当前值: (22 (33 ("newX" 3.14159)) "Hello")
Lx.ToLast(); Lx的担前值: ("Hello")
Lx.ToFirst(); Lx的当前值: (11 22 (33 ("newX" 3.14159)) "Hello")
Dx对LIST数据链的数据存取操作,是基于高级指针,它可以对数据链各结点局部操作,不会影响其它结点的数据,因此很有效率。下面是用函数entget获得的轻型LWPOLYLINE实体(Entity)的LIST数据链MyList: ((-1 . <Entity name: 33e0528>) (0 . "LWPOLYLINE") (5 . "4D") (100 . "AcDbEntity") (67 . 0) (8 . "0")(100 . "AcDbPolyline") (90 . 3) (70 . 0) (43 . 0.0) (38 . 0.0)(39 . 0.0) (10 5.39471 4.932 0.0) (40 . 0.0)(41 . 0.0)(42 . 0.0) (10 7.10083 6.636 1.21312e-126) (40 . 0.0)(41 . 0.0)(42 . -0.792233) (10 9.38367 5.22 7.74682e-304) (40 . 0.0)(41 . 0.0)(42 . 1.39127) (210 0.0 0.0 1.0) )
该LIST数据链含3个顶点(对应代码 10)。在AutoLISP程序里要修改该3个顶点的值(比如Scale),是比较复杂的,如果不重构整条数据链,好象做不到。在Dx里进行该操作非常简单,且用不着重构:
把顶点的X和Y值放大到2倍,而Z值置0: scale = 2.0;
for (p=MyList; p && (p=p.SeekType(10)); p++) { p->Value[0] *= scale; p->Value[1] *= scale; p->Value[2] = 0.0; }
或用迭代法: MyList.do() { if (Just->Type != 10) continue; Just->Value[0] *= scale; Just->Value[1] *= scale; Just->Value[2] = 0.0; }
事实上,这里的关键字Just还可以用符号 ^ 代替: MyList.do() { if (^Type != 10) continue; ^Value[0] *= scale; ^Value[1] *= scale; ^Value[2] = 0.0; }
11) Dx可以直接调用电脑操作系统中数以千计的WIN32 的API库函数: api.gdi32.Ellipse( hdc, x1, y1, x2, y2); 或 My = api.gdi32; My.Ellipse(hdc, x1, y1, x2, y2);
12) Dx支持COM构件操作,比如操作MS Excel: xApp = CreateDispatch("Excel.Application"); xApp.Visible = TRUE; xApp.Workbooks.Add(); xSheet = xApp.Sheets[1]; xSheet.Cells[1, 1] = "Hello, my friends!"; ... ...;
今天就粗粗讲这些。最后,给出Dx在AutoCAD中的一个应用实例:
// 从客户提供的DXF文件读得的AutoCAD图中,存在数以千计的"圆":由半 // 圆型的、同圆心、同半径的两个圆弧构成。现在需要把这些"圆"的圆心 // 坐标(X, Y)按半径从小到大输出到指定文件 "A2C.NC"中,同时在屏幕显示。 // 下面的Dx程序就是执行该功能,该程序装入内存后,会自动在AutoCAD // 的命令序列中 插入一个命令"A2C", 用于启动下面程序。
defun C_A2C() { SysVar = SaveSysVar(); $luprec= 3; $D2 = TRUE; $Xstr = "X"; $Ystr = "Y"; acc = 0.0005;
filter = buildlist(0, "ARC"); ss = ssget(filter); if (!ss) return; len = ss.len();
T = new TREE.Real.Lvect(acc);
for (i=0; i<len; i++) { eg = entget(ss); Ra = eg.SeekType(40)->Value; Pt = eg.SeekType(10)->Value; dA = eg.SeekType(50)->Value - eg.SeekType(51)->Value; if (Ra>acc && equal(abs(dA), PI, acc) && !T[Ra].Seek(Pt, acc)) T[Ra].Add(Pt); }
fw = fopen("A2C.NC", "w");
T.Sort();
Xout(fw, "%\n"); T.Queue.Do() { Xprintf(fw, "T%02dD%0.3lf\n", $i+1, Just->Key *2); } T.Queue.Do() { Xprintf(fw, "\nT%02d\n", $i+1); Just->Value.Xout(fw); Xout(fw, "\nM99\n"); } Xout(fw, "%\n"); }
下面是输出的部分结果: % T01D0.701 T02D0.800 T03D0.899 ... ... T11D3.200 T12D4.000
T01 X58.499Y98.501 X48.339Y98.501 X48.339Y103.624 ... ... X43.0Y165.039 M99
T02 X26.5Y48.501 X26.5Y57.501 ... ... X80.249Y158.75 X85.011Y160.637 M99
.....
T12 X75.001Y92.25 X0.0Y0.0 X119.499Y0.0 X120.5Y0.0 X119.501Y0.0 X120.499Y0.0 M99 %
// OK! ///////////////////////////////
|
|