【前言】
最近发现,做CAD二次开发的,要么是计算机相关专业的,没有CAD基础;要么是工程制图出身,不会编程。所以写了这篇博客,AutoLisp学习笔记,根据我的学习经验,旨在给没有基础的你指明学习的方向,让读者少走弯路。
AutoLISP语言是开发AutoCAD的主要工具,是LISP语言和AutoCAD有机结合的产物,在AutoCAD的发展及壮大过程中起到了重要的作用。CAD二次开发可以使用VB、LISP和C#语言,CAD均为各种方式开发了平台。之所以使用LISP,是因为经过多年的累积,已经有很多的基础代码,方便程序的开发。
掌握AutoLISP开发,需要准备如下知识:1、LISP语言 2.AutoCAD基础 3.AutoLISP函数 4.AutoLisp进阶。
一. LISP语言相关知识
跟我们学过的C++ Java语言有很大的不同,LISP语言是解释性语言,只有两个基本单位:原子(atom)和列表(List)。这里推荐如下两个博客,前者是一个常用的LISP语言的开发环境,后者是语法学习,配合使用快速学习。这样,半天的时间可以对LISP语言有初步的概念。递归是LISP的精髓,如果看懂链接博客中的所有示例,那么可以进行下一步了。
【Lisp语言: 在Windows下搭建CLisp环境】
http://blog.csdn.net/keyboardota/article/details/8237185
【Lisp入门】
http://www.cnblogs.com/suiqirui19872005/archive/2007/12/05/984517.html
二.AutoCAD基础
AutoCAD博大精深,对于我们这些开发人员,只需了解些基本操作和概念就足够了。个人认为如下几个概念比较重要,部分可以在AutoCAD术语帮助中找到。
1、命令。
CAD的操作几乎都可以用命令描述,如创建一个以原点为圆心,半径为100的圆,可用如下几种命令形式完成。CAD中不区分大小写。
(1)CAD命令
命令:circle/c
在命令栏里键入c,然后就可以用鼠标在绘图窗口中操作指定圆心和半径。
(2) AutoLisp命令
$ (setq center ‘(0 0 0))
-
$ (command “circle” center 100)
1
(注:_$ 是Visual Lisp控制台的前置符,启动Visual Lisp的CAD命令是VLIDE,在控制台和CAD命令栏均可使用AutoLisp命令及ActiveX方法。在CAD命令栏查看变量前面加感叹号!) -
学习过前面LISP语言后,应该可以理解setq,command命令其实是AutoLisp函数,即AutoCAD提供给开发人员的Lisp函数。前者为变量设置值,后者是AutoLisp与AutoCAD的接口,可以向AutoCAD命令行直接发送命令和参数。其中列表 ‘(0 0 0)和原子 100分别为圆心和半径的参数。如上命令括号不能省略,表示其为函数。
(3)Active X方法
$ (vl-load-com) -
$ (setq acad_obj (vlax-get-acad-object))
$ (setq acad_document (vla-get-activedocument acad_obj)) -
$ (setq m_space (vla-get-modelspace acad_document))
_$ (vla-addcircle m_space (vlax-3d-point ‘(0 0 0)) 100)
1
Active X技术通过将AutoCAD的对象显示在外部使用户以各种编程的方式访问AutoCAD对象。Active X函数 (vla-) (vlax-)提供了访问对象的各种方法。这里只是引入,说明了CAD命令,AutoLisp命令和ActiveX方法均能对CAD进行操作。 -
2、图层。
一组具有一定逻辑关系的数据,类似于覆盖在图形上的透明硫酸纸。可以单独查看每个图层,也可以同时查看多个图层。与PS中图层的概念一样,一个绘图可由多个叠加的图层组成,不同的图元可以选择绘制在哪个图层上。通过layer命令(CAD命令)可以生成或者选择图层。3、选择集。
顾名思义,选择集就是选择的集合,在绘图窗口中,点击鼠标左键拖动鼠标,所选择的图元构成选择集。在AutoLisp 中可以用 ssget 函数对选择进行处理和过滤,得到自己想要到集合。- 图元对象属性及DXF组代码
图元,顾名思义,就是CAD中最小的操作单位,如上面画的一个圆,还可以是插入的块。在CAD中,每个图元其实就是一个对象,它们具有各种属性,比如前面画的circle属性包括实体名,实体类型,辅助实体名,所在图层名,圆心坐标,半径等。这些属性都是用DXF组代码的形式写入的。组代码使用LISP语言的点对结构表示图元对象的属性,类似于C++中map结构。可用entget函数查看。
$ (command “circle” ‘(10 286.611 50.8336 0.0) 100)
- 图元对象属性及DXF组代码
-
$ (setq ent1 (entlast))
_$ (entget ent1)
1
其中,entlast函数返回最后的图元,上述返回结果如下:
((-1 . <图元名: 7ffff705c20>) ;实体名
(0 . “CIRCLE”) ;实体类型
(330 . <图元名: 7ffff7039f0>) ;辅助实体名
(5 . “23A”) ;实体标号
(100 . “AcDbEntity”) ;实体子类说明
(67 . 0) ;空间类型说明
(410 . “Model”) ;空间名
(8 . “0”) ;图层名
(100 . “AcDbCircle”) ;
(10 286.611 50.8336 0.0) ;圆心坐标
(100.000) ;半径
(210 0.0 0.0 1.0)) ;3D延伸方向
1 -
更多关于DXF可参考AutoCAD的帮助。如果想得到该图元的圆心坐标,使用如下代码。
_$ (cdr (assoc 10 (entget ent1)))
(286.611 50.8336 0.0)
1
※拓展:也可以用ActiveX的方法进行访问。
$ (setq myobject (vlax-ename->vla-object (entlast))) -
#<VLA- OBJECT IAcadCircle 0000000037be66f8>
-
$ (vlax-safearray->list (vlax-variant-value (vlax-get-property myobject 'center)))
(286.611 50.8336 0.0)
1
这里涉及到ActiveX方法和函数,看不懂可先跳过,有概念即可。
5. 块。
表示结合起来以创建单一对象的一个或多个对象。常用于块定义或块参照。在块编辑窗口中可增加其属性,在AutoLisp中使用entnext遍历属性。 -
6.AutoCAD的自定义配置
AutoCAD强大的平台可以使用户自定义工作区间,包括菜单,面板,双击动作等等,还能定义新的命令,可以说,AutoCAD的灵活和个性化是软件的精髓,是AutoDesk公司屹立不倒的根基。敲击CAD命令:cui,或者【工具】->【自定义】->【界面】
这里可以自定义用户界面,比如想添加一个查看图元属性的菜单选项,可以按如下步骤:
(1)创建自定义命令 点击命令列表中的五角星,这里使用默认名称“命令1”
(2)编辑自定义命令的宏,其中CC表示连按两下Esc,保证退出其他命令。键入命令:
CC(entget (SSNAME (SSGET) 0))
1
(3)打开菜单栏,在文件菜单中,将命令列表的“命令1”拖动进去。点击确定退出。图表 1 CAD自定义界面
(4)单击选中窗口中任一图元,然后再文件菜单中点击“命令1”,发现命令栏里返回图元的属性信息。
7.AutoCAD支持文件配置
类似于Java的环境配置,是程序寻找文件的目录列表。三.AutoLisp函数
参考任一一本关于AutoLisp的书均可。推荐下《AutoCAD 2008 Visual LISP二次开发入门到精通》,现已绝版(⊙﹏⊙b汗)。AutoLisp函数是二次开发的基础,基本函数不多,是Lisp语言在AutoCAD环境下的扩展,容易掌握。四.Visual Lisp集成开发环境
CAD命令栏键入 VLIDE 命令弹出VisualLisp开发环境,包括编译器,调试器和其他工具,提高编程效率。控制台窗口可以像AutoCAD命令行那样输入AutoLisp命令,还可以输入Visual LISP命令。五.AutoLisp进阶
1.使用ActiveX对象
我们可以把整个AutoCAD理解为一个对象的模型,包括样式设置对象,组织结构对象,图形显示对象,图元对象,AutoCAD本身也是一个对象。这些对象根据包含关系组成了层次结构,称为对象模型。既然为对象,那么就有属性值和方法。下面我们根据一个例子讲下ActiveX对象的使用方法。我们将使用三种方法完成。例:画直线,根据图元名称获得其对象,并得到直线的两个端点。并将直线向x轴负方向挪动1000。
1.1 ActiveX对象属性的操作
(1) 选择直线工具,在cad绘图窗口中绘制一条直线(2)打开Visual Lisp,在控制台窗口键入如下代码:
$ (vl-load-com)
-
$ (setq line-object (vlax-ename->vla-object (entlast)))
#<VLA-OBJECT IAcadLine 0000000030624d58>
1
若要用ActiveX访问指定的图形对象,则可以先找到对象的图元名,再将这个名称转化为VLA对象,之后就可以按照访问VLA对象的方法访问了。通过上面两行代码,我们就得到了刚刚画的直线的对象,并保存在变量line-object中。 -
右键变量line-object,选择【检验】。弹出【检验】窗口,这里我们可以看到这个变量的多个属性。其中<StartPoint>和<EndPoint>便是线段的起点和终点。这里我们看到<EndPoint>值为#<variant 8197…>,这是一种ActiveX型的数据类型,术语“变体”。变体是应用在ActiveX对象操作中使用的数据类型,可以通过内置的函数与AutoCAD中的数据相互转化,下面我们将看到应用。双击<EndPoint>,弹出子窗口,我们可以看到该变体的值是一个元素类型为Double的#<safearray…>,Safearray(安全数组)也是一种ActiveX数据类型。双击safearray,弹出子窗口我们终于看到终点坐标(1007.6 3440.77 0.0)。
图表 2 ActiveX对象检验
接下来我们使用Lisp代码完成读取工作
$ (setq endp (vla-get-endpoint line-object))
-
#<variant 8197 …>
-
$ (setq endp2 (vlax-variant-value endp))
#<safearray…>
_$ (setq endp3 (vlax-safearray->list endp2))
(1007.6 3440.77 0.0)
1
其中vla-get-endpoint,vlax-variant-value,vlax-safearray->list是AutoLisp函数。同样如法炮制得到起点的坐标值: -
_$(setq startp3 (vlax-safearray->list (vlax-variant-value (vla-get-startpoint (vlax-ename->vla-object (entlast))))))
(234.156 3400.91 0.0)
1
接下来设置新的坐标$ (setq newEndP (list (- (nth 0 endp3) 1000) (nth 1 endp3) (nth 2 endp3)))
-
( 7.6 3440.77 0.0)
-
$ (setq newStartP (list (- (nth 0 startp3) 1000) (nth 1 startp3) (nth 2 startp3)))
(-765.844 3400.91 0.0)
1
接下来将新的坐标写入该对象,写入函数vla-put- 要求写入参数应当为变体类型,所以我们创建了两个安全数组,NewEndP2&NewStartP2。 -
$ (setq NewEndP2 (vlax-make-safearray vlax-vbDouble '(0 . 2)))
-
#<safearray…>
-
$ (vlax-safearray-fill NewEndP2 NewEndP)
#<safearray…>
$ (setq NewStartP2 (vlax-make-safearray vlax-vbDouble '( 0 . 2))) -
#<safearray…>
-
$ (vlax-safearray-fill NewStartP2 NewStartP)
#<safearray…>
$ (vla-put-startpoint line-object NewStartP2) -
nil
-
$ (vla-put-endpoint line-object NewEndP2)
Nil
1
同样地,我们可以不用ActiveX,而是采用AutoCAD 内部命令来完成上述工作。 -
_ ( s e t q e n t 1 ( e n t g e t ( e n t l a s t ) ) ) < / d i v > < / d i v > < / l i > < l i > < d i v c l a s s = " h l j s − l n − n u m b e r s " > < d i v c l a s s = " h l j s − l n − l i n e h l j s − l n − n " d a t a − l i n e − n u m b e r = " 2 " > < / d i v > < / d i v > < d i v c l a s s = " h l j s − l n − c o d e " > < d i v c l a s s = " h l j s − l n − l i n e " > < s p a n c l a s s = " h l j s − n u m b e r " > < s p a n c l a s s = " h l j s − n u m b e r " > < / s p a n > < / s p a n > (setq ent1 (entget (entlast)))</div></div></li><li><div class="hljs-ln-numbers"><div class="hljs-ln-line hljs-ln-n" data-line-number="2"></div></div><div class="hljs-ln-code"><div class="hljs-ln-line"><span class="hljs-number"><span class="hljs-number">_</span></span> (setqent1(entget(entlast)))</div></div></li><li><divclass="hljs−ln−numbers"><divclass="hljs−ln−linehljs−ln−n"data−line−number="2"></div></div><divclass="hljs−ln−code"><divclass="hljs−ln−line"><spanclass="hljs−number"><spanclass="hljs−number"></span></span>(setq oldEndp (assoc 11 ent1)) ;终点坐标DXF组码值为11
$ (setq oldStartp (assoc 10 ent1)) ;起点坐标DXF组码值为10 -
$ (setq newEndp (list 10 (- (nth 1 oldEndp) 1000) (nth 2 oldEndp) (nth 3 oldEndp)))
$ (setq newStartp (list 10 (- (nth 1 oldStartp) 1000) (nth 2 oldStartp) (nth 3 oldStartp))) -
$ (setq ENT1 (subst newEndp oldEndp ENT1))
$ (setq ENT1 (subst newStartp oldStartp ENT1)) -
$ (entmod ENT1)
1
可能这时你就有疑问了,既然两种办法都可改变图元的属性,那还用ActiveX干嘛。其实相比于其他AutoCAD API环境,ActiveX接口技术速度快,效率高,随CAD安装,便于使用。另外ActiveX的对象交互技术使得CAD可与外界应用程序交互,拓宽了CAD开发的应用范围。 -
1.2 ActiveX对象属性的操作
Visual Lisp提供了Vlax-invoke-method函数来调用ActiveX的方法。使用的时候要注意将参数转化为ActiveX型,除非参数的默认分配类型(见将数据类型转换为ActiveX型)正好为所需的类型。对于上面的line-object,我们如何知道该对象所支持的ActiveX方法呢,下面提供个人经验。首先使用vlax-dump-object函数列出line-object所支持的方法。_$ (vlax-dump-object line-object t)
; IAcadLine: AutoCAD Line 接口
;特性值:
; Angle (RO) = 0.620333
; Application (RO) = #<VLA-OBJECT IAcadApplication 000000013f993318>
; Delta (RO) = (1361.03 972.338 0.0)
; Document (RO) = #<VLA-OBJECT IAcadDocument 000000002fb96bc8>
; EndPoint = (1458.44 1051.22 0.0)
; EntityTransparency = “ByLayer”
; Handle (RO) = “236”
; HasExtensionDictionary (RO) = 0
; Hyperlinks (RO) = #<VLA-OBJECT IAcadHyperlinks 0000000039a12ad8>
; Layer = “0”
; Length (RO) = 1672.68
; Linetype = “ByLayer”
; LinetypeScale = 1.0
; Lineweight = -1
; Material = “ByLayer”
; Normal = (0.0 0.0 1.0)
; ObjectID (RO) = 42
; ObjectID32 (RO) = 42
; ObjectName (RO) = “AcDbLine”
; OwnerID (RO) = 43
; OwnerID32 (RO) = 43
; PlotStyleName = “ByLayer”
; StartPoint = (97.4054 78.8798 0.0)
; Thickness = 0.0
; TrueColor = #<VLA-OBJECT IAcadAcCmColor 0000000039a12890>
; Visible = -1
;支持的方法:
; ArrayPolar (3)
; ArrayRectangular (6)
; Copy ()
; Delete ()
; GetBoundingBox (2)
; GetExtensionDictionary ()
; GetXData (3)
; Highlight (1)
; IntersectWith (2)
; Mirror (2)
; Mirror3D (3)
; Move (2)
; Offset (1)
; Rotate (2)
; Rotate3D (3)
; ScaleEntity (2)
; SetXData (2)
; TransformBy (1)
; Update ()
1
这里我们看到有个名字为Move的方法,猜想其可以完成图元的移动,是否如此呢,查看帮助中的“AutoCAD ActiveX and VBA references”部分,查找Move Method,确实是我们预想的那样。接下来我们使用这个函数同样完成将直线向x轴负方向挪动1000的工作。Move方法接受两个参数,Point1&Point2,分别表示移动向量的起点和终点。显然这里Point1&Point2的坐标分别为(0 , 0 , 0)和 (-1000 , 0 , 0)。参数类型要求为Variant (three-element array of doubles),我们先构造两个double类型的safearray,然后转化为变体(Variant )类型。(转化为变体类型这一步并非必须,因为safearray会被强制转化为Variant)最后作为实参代入函数,看能否同样完成前面的工作。图表 3 Move方法帮助
$ (setq Point1 (vlax-make-safearray vlax-vbDouble '(0 . 2)))
-
#<safearray…>
-
$ (setq Point2 (vlax-make-safearray vlax-vbDouble '(0 . 2)))
#<safearray…>
$ (vlax-safearray-fill Point1 (list 0 0 0)) -
#<safearray…>
-
$ (vlax-safearray-fill Point2 (list -1000 0 0))
#<safearray…>
$ (setq Point1 (vlax-make-variant Point1)) ;;转换为变体类型,非必须 -
#<variant 8197 …>
-
$ (setq Point2 (vlax-make-variant Point2)) ;;非必须
#<variant 8197 …>
_$ (vlax-invoke-method line-object “Move” Point1 Point2)
Nil
1
然后将窗口切换到AutoCAD绘图窗口,直线确实按照我们预想的一样,又往x轴负方向移动了1000单位。 -
总结:对于某一ActiveX对象,使用vlax-dump-object函数可以列出该对象的属性值和适用的ActiveX方法。对属性值的操作,可以采用vla-get和vla-put为前缀的函数。ActiveX方法就比较复杂,若要查找相应的操作函数的使用方法,可以参考AutoCAD帮助中的“AutoCAD ActiveX and VBA references”部分。并应用vlax-invoke-method方法调用ActiveX对象的方法。CAD2013版本以后,VBA开发不再随软件安装,所以帮助部分也不容易找到了。http://entercad.ru/acadauto.en/ 网站可供参考使用。
2.ActiveX对象交互
在Visual Lisp环境中,也可以使用其他应用程序的ActiveX对象。笔者在开发的时候,遇到XML和CAD信息交互的问题,上网搜索,发现没有解析XML的LISP库,如果自己写XML解析,势必大大影响项目进展。终于在ActiveX对象中找到灵感,下载了一个XML的ActiveX控件,加载到自己的程序里完成项目开发。(见我的另一篇博客,AutoLisp中XML解析方案)转载【坠入海浪】的博客