明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 7807|回复: 16

【原创】介绍一种新的CAD二次开发程序语言

[复制链接]
发表于 2004-3-7 14:59:00 | 显示全部楼层 |阅读模式
本帖最后由 作者 于 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! ///////////////////////////////
发表于 2023-6-12 08:56:48 | 显示全部楼层
太及时的帖子了,我最近被困惑了2个月了,莫名其妙的出错,原来是我从while里面用esc中断后导致的问题。
发表于 2024-11-8 19:30:00 来自手机 | 显示全部楼层
纯属扯淡,吹牛逼成分太大
发表于 2004-3-8 08:52:00 | 显示全部楼层
祝成功!
发表于 2004-3-31 22:36:00 | 显示全部楼层
该语言正式版出来了吗?
 楼主| 发表于 2004-4-4 22:10:00 | 显示全部楼层
本帖最后由 作者 于 2004-4-11 16:07:44 编辑

xiequanfu: 该语言正式版出来了吗? 还没有。 迄今,Dx只在几个朋友中试用。目前还在改进、完善中。Dx的设计者有打算将来扩大到让大家试用。我在第一贴中只是粗糙地介绍Dx中的一部分功能,实际上Dx还有不少其他很好的功能,Dx牵涉面比较广,很难用几句话介绍清楚,如果时间允许的话,我打算将来比较全面地介绍Dx给大家,只是深恐词不达意,倒了大家的胃口。 从论坛上看,人们对LISP的热情,远远超过AutoCAD的其他开发语言。用几行、甚至一行的AutoLISP程序码就可以完成一件事。不过,大家是否注意到,为了解决一些简单的问题,用AutoLISP编程起来并不是很简单。VisualLISP的出现,显著扩展了AutoLISP的功能,但是LISP原有的程序架构并没有什么改变,也不可能改变,将来也会如此。
人们不太爱用ARX、VBA之类的开发语言,并不完全是因为ARX、VBA等难学、难用。这里一个重要的原因是ARX、VBA等语言的程序要求编译,"麻雀虽小、五脏具全",不管程序有多小,编译是一件很烦人的事。
有不少人对非编译型语言----也就是解释型语言的运行效率有偏见,认为编译型程序比非编译型程序运行得快很多而看不起非编译型语言,事实上,很多时候程序运行快慢并不是很重要(不可否认,实时操作时,对程序运行速度很敏感!不过,这种情况并不多见),启动一条命令后,让AutoCAD完成某个操作,用0.001秒完成与0.01秒完成,对操纵者来说并没有任何差别。
一般上,程序的运行时间消耗,主要由两个因素构成:程序解释、"基本功能块"调用。对于C/ C++之类的编译型语言,程序的解释所花费的时间几乎为0,而解释型语言在程序解释部分所花费的时间,则因程序段的长短、复杂程度而会有很明显的不同,但是在"基本功能块"调用方面,两类语言所花费的时间基本上是一样的,因为"基本功能块"都是编译成型的。因此,解释型语言的运行效率在很大程度上取决于"基本功能块"的调用时间与程序解释时间的比率。如果能够非常有效地调用"基本功能块"(比如内建的函数、方法等),解释型语言的运行效率可以很高,甚至可以比美C/C++。比如,对于数据链节点数据的排序过程,如果完全使用解释型程序实现,运行效率肯定非常低,但是,如果通过调用内建排序功能块来排序(MyList.Sort()),程序的运行效率与C/C++程序无异,可以在瞬间完成。因此,解释型语言的运行效率贵在有效调用内建的功能块,要避免书写庞大的解释型程序来完成比较简单的功能。
同是解释型语言,由于采用不同的数据处理架构,处理数据的效率有时会差异很大,下面给出一个AutoLISP程序与Dx等同程序的运行耗时比较特例(用于构造num个元素的LIST数据链):
;;AutoLISP程序:
(defun time_LSP (num / Lx k)
(setq Lx nil k 0)
(while (< k num)
(setq Lx (append Lx (list k)))
(setq k (1+ k))
)
)
//Dx 程序:
defun time_DX (num)
{
Lx = new LIST;
for (k=0; k<num; k++) Lx.Add(k);
}
比较结果(时间:毫秒) num | AutoLISP | Dx
--------------------------------------
100 | 5 | 5
1000 | 340 | 25
10000 | 44386 | 190
---------------------------------------

num值越大,两者的时间差异越大。这里的差异的根源在于:Dx每增加一个数据节点,仅仅是生成一个节点接到数据链尾端;而AutoLISP每增加一个节点时,需要重构整条数据链,获得较长的LIST数据链,AutoLISP需要生成、消毁很多临时数据节点,因此非常耗时,这里还会引发更为严重的问题,就是内存粹化问题。
========================
续第一贴,下面给大家介绍Dx的另外三个功能:
13) 通过COM技术,Dx可以以ActiveX/VBA方式调用AutoCAD自身的功能:

Xapp = GetDispatch(); // 取得AutoCAD本身的IDispatch接口。
ThisDrawing = Xapp.ActiveDocument;
MyLine = ThisDrawing.ModelSpace.AddLine([], [111,222]);
MyLine.Color = 3;
懂得ActiveX /VBA的人,应该可以看懂这里的程序吧。这里需要说明的是Dx把矢量/点坐标看成简单、基本数据类型,并可以用[…]格式构造:
pt1 = [1, 234.5, 6.78];
pt2 = pt1; // pt2的结果值是 (1.0, 234.5, 6.78)
pt2 *= [2, 2, 0]; // pt2的结果值是 (2.0, 469.0, 0.0)
Y = pt2[1]; // Y的值是 469.0

14) Dx可以象AutoLISP一样,在Command:命令行逐条输入程序码,并运行程序。很直观地看到程序运行过程。Dx还可以用内建函数vLoad(fileName),逐行显式装入程序过程。
15) Dx支持Lambda(),实现动态构造、扩充程序功能。用Lambda()建立起来的函数存入变量中,可以两种方式调用,其运行形态不同:
MyLx = Lambda(x, y) {
… …;
}

(a) MyL(2, 3); // MyL以独立的函数调用运行。
(b) MyL.Eval(2, 3);
// 如果该行程序码在一个用户定义的函数/方法Fx()中,
// 那么MyL中的程序码会作为Fx()中的一部分来运行,类似
// C/C++中的 {…}结构,MyL()中的 break, return等关键
// 字的功能显然会作用于Fx(),另外,MyL()的程序码同时
// 也取得Fx()所拥有的存取权限。
// 如果该行程序码不在一个用户定义的函数/方法中,那么
// 其运行形态与 (a) 相同。

MyL()定义后,还可以对其进行编辑,比如:
MyL.Add ("return x + sin(y);");
另外, Dx还可以把字符串作为程序码来运行:
MyStr = "sin(x)";
MyStr.Eval();

========== OK ===============
发表于 2004-4-10 23:13:00 | 显示全部楼层
什么地方有下载???
发表于 2004-4-11 10:18:00 | 显示全部楼层
不能试用好像。。。
发表于 2011-10-14 15:53:46 | 显示全部楼层
DX  合称是什么?
发表于 2011-10-15 08:33:24 | 显示全部楼层
去学习太多的语言没有时间,个人看法是熟练掌握一两种语言就够用了...
发表于 2013-5-2 15:23:58 | 显示全部楼层
求C#二次开发CAD的实例教程。。
发表于 2013-5-13 09:02:44 | 显示全部楼层
这种简便的语言已经实现了
http://www.easylou.com/download.php?key=2
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-11-25 04:52 , Processed in 0.170113 second(s), 25 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表