|
|
用户名:eachy 笔名:eachy 地区: 行业:其他 |
| 日 | 一 | 二 | 三 | 四 | 五 | 六 |
Lisp 收藏,开发,CAD应用
Eachy Lisp/Vlisp Library 使用说明
(作者置顶)
Adodb.Stream组件详解
Adodb.Stream是ADO的Stream对象,提供存取二进制数据或者文本流,从而实现对流的读、写和管理等操作.
有下列方法:
Cancel 方法
使用方法如下
Object.Cancel
说明:取消执行挂起的异步 Execute 或 Open 方法的调用。
Close 方法
使用方法如下
Object.Close
:关闭对像
CopyTo 方法
使用方法如下
Object.CopyTo(destStream,[CharNumber])
说明:将对像的数据复制,destStream指向要复制的对像,CharNumber为可选参数,指要复制的字节数,不选为全部复制。
Flush 方法
使用方法如下
Object.Flush
说明:
LoadFromFile 方法
使用方法如下
Object.LoadFromFile(FileName)
说明:将FileName指定的文件装入对像中,参数FileName为指定的用户名。
Open 方法
使用方法如下
Object.Open(Source,[Mode],[Options],[UserName],[Password])
说明:打开对像,
参数说明:Sourece 对像源,可不指定
Mode 指定打开模式,可不指定,可选参数如下:
adModeRead =1
adModeReadWrite =3
adModeRecursive =4194304
adModeShareDenyNone =16
adModeShareDenyRead =4
adModeShareDenyWrite =8
adModeShareExclusive =12
adModeUnknown =0
adModeWrite =2
adModeUnknown:缺省。当前的许可权未设置
adModeRead:只读
adModeWrite:只写
adModeReadWrite:可以读写
adModeShareDenyRead:阻止其它Connection对象以读权限打开连接
adModeShareDenyWrite:阻止其它Connection对象以写权限打开连接
adModeShareExclusive:阻止其它Connection对象以读写权限打开连接
adModeShareDenyNone:阻止其它Connection对象以任何权限打开连接
Options 指定打开的选项,可不指定,可选参数如下:
adOpenStreamAsync =1
adOpenStreamFromRecord =4
adOpenStreamUnspecified=-1
UserName 指定用户名,可不指定。
Password 指定用户名的密码
Read 方法
使用方法如下:
Object.Read(Numbytes)
说明:读取指定长度的二进制内容。
参数说明:Numbytes指定的要读取的找度,不指定则读取全部。
ReadText 方法
使用方法如下:
Object.ReadText(NumChars)
说明:读取指定长度的文本
参数说明:NumChars指定的要读取的找度,不指定则读取全部。
SaveToFile 方法
使用方法如下:
Object.SaveToFile(FileName,[Options])
说明:将对像的内容写到FileName指定的文件中
参数说明:FileName指定的文件
Options 存取的选项,可不指定,可选参数如下:
adSaveCreateNotExist =1 指定文件不存在则创建
adSaveCreateOverWrite =2 覆盖
SetEOS 方法
使用方法如下:
Object.setEOS()
说明:返回对像内数据是否为空
SkipLine 方法
使用方法如下:
Object.SkipLine()
说明:在读取textstream文件时跳过下一行
Write 方法
使用方法如下:
Object.Write(Buffer)
说明:将指定的数据装入对像中。
参数说明:Buffer 为指定的要写入的内容。
WriteText 方法
使用方法如下:
Object.Write(Data,[Options])
说明:将指定的文本数据装入对像中。
参数说明:Data 为指定的要写入的内容。
Options 写入的选项
,可不指定,可选参数如下:
adWriteChar =0
adWriteLine =1
有下列属性:
Charset
EOS 返回对像内数据是否为空。
LineSeparator 指定换行格式,可选参数有
adCR =13
adCRLF =-1
adLF =10
Mode 指定或返加模式。
Position 指定或返加对像内数据的当前指针。
Size 返回对像内数据的大小。
State 返加对像状态是否打开。
Type 指定或返回的数据类型,可选参数为:
adTypeBinary =1
adTypeText =2
例子:
adodb.stream 的使用地方很多 这里ME只讲一讲使用 adodb.stream 组 替代 FSO 实现读取文件的方法 --- 网上流行的一些无组件上传程序全部基于这个写成的 哈哈
因为直接讲可能会使一大半人头脑发晕 所以ME们在这里先写一个例子 看不明白不要紧 后面后一条一条的进行解释
1 On Error Resume Next
2 Set stream=Server.CreateObject("Adodb.Stream")
3 If Err.Number=-2147221005 Then
4 Response.Write "<div align='center'>非常遗憾,您的主机不支持ADODB.Stream,不能使用本程序</div>"
5 Err.Clear
6 Response.End
7 End If
8 stream.type=2
9 stream.mode=3
10 stream.open
11 dim filepath
12 filepath="a.txt"
13 stream.LoadFromFile Server.Mappath(filepath)
14 If Err.Number<>0 Then
15 Response.Write "<div align='center'>文件<font color='#ff0000'>"&Filepath&"</font>无法被打开,请检查是否存在!< /font></div>"
16 Err.Clear
17 Response.End
18 End If
19 stream.Charset="gb_2312"
20 stream.position=2
21 dim loadfile
22 loadfile=steam.readtext
23 stream.close
24 set stream=nothing
到这里读出的工作就完成了
下面来一句一句的解释
第一句不要解释了吧 会点英语的光看意思就知道了 就是 在遇到错误的时候假定到下一行 也就是如果错误不中断程序从下一句继续执行了
第二句 为 创建 stream 对象 3-7为进行错误判断 可以不写
8,9分别设置stream对象 返回 数据的类型和打开的模式
type属性 设置 stream 对象返回的数据类型 有两个值 1 返回二进制 2 返回文本
mode属性 为打开模式 1.只读 2.只写 3.读写 还有一些其它的 这里就不写了
10 句 是打开对象 只有打开后 以后的方法如 loadFromFile才能使用
11,12,13是使用loadFromFile 将文件 a.txt 里的内容 读入 stream对象里(注意是读入这个对象中,并没有另开一个变量空间)
14-18 一样是错误判断啊
19 Charset设置返回数据类型的编码格式 这里设的是 简体中文
20 Position 设置文件读取的当前指针位 也就是当前指向的是第几个字符了
21,22使用readtext的方法将对象里的内容(就是先前用LOADFROMFILE读进来的内容)写入loadfile变量
后面两句是 关闭 和 销毁 对象
当然这只是读取
要想将其内容写入一文件 可以使用 savetofile(filename,[adsavecreatenotexist|adsavecreateoverwrite])
要想往其内容里写入内容 可以使用 write(buffer) 和writetext(data,[adwritechar|adwriteline])
将一个对象的内容复制到另一个对象里
Object.CopyTo(destStream,[CharNumber])
其中destStream为另一对象 charnumber 为要复制文件的大小 不设为全部 这个在上传里必须用到 啊 哈哈
好了 就写到这里了 希望给看到的人 并需要使用他的人 有点 帮助
///////////////////////////////////////////////////////////////////////////
目前有很多无组件上传类,我大概看了一下,大多写的相当复杂,有的居然还只能传文本
最关键的是没有10行代码以下的 :)
我花了一个晚上时间研究了一下ADODB.Stream,并且用了6行代码实现了无组件上传:
strFileName = Request.QueryString("file1")
Set objStream = Server.CreateObject("ADODB.Stream")
objStream.Type = 1 ' adTypeBinary
objStream.Open
objStream.LoadFromFile strFileName
objStream.SaveToFile Server."123_onweb.gif",2
使用方法:
把上面的代码写成upload.asp
在浏览器里面输入:
http://XXX/upload.asp?file1=c:\上传文件\123.gif
XXX为你的主机地址
执行完后你会看到你的目录下面多了一个123_onweb.gif
他就是你要文件拉!!!!
根据原理我们可以扩展以下代码:
upload.asp文件
<%
Function GetFileName(ByVal strFile)
If strFile <> "" Then
GetFileName = mid(strFile,InStrRev(strFile, "\")+1)
Else
GetFileName = ""
End If
End function
strFileName = Request.Form("file1")
Set objStream = Server.CreateObject("ADODB.Stream")
objStream.Type = 1 ' adTypeBinary
objStream.Open
objStream.LoadFromFile strFileName
objStream.SaveToFile Server.MapPath(GetFileName(strFileName)),2
objStream.Close
%>
upload.htm文件
<form name="FORM" action="upload.asp" method="post">
<input type="submit" name="submit" value="OK">
<input type="file" name="file1" style="width:400" value="">
</form>
前几天要写一个东西里面有用到读文件的。
可是我不想用FSO,我怕有的空间不支持。
可是网络上找了半天没有找到一个不是用FSO写的。
还是自己做了一个。
我记得以前在做无组件上传,并保存文件的时候有用到stream
我发现有一个LoadFromFile的方法。可以读取文件。
下面是我的代码。
function readfile(URL,chartype)
set srmObj = server.CreateObject("adodb.stream")
srmObj.type=1
srmObj.mode=3
srmObj.open
srmObj.Position=0
srmObj.LoadFromFile URL
srmObj.Position = 0
srmObj.type=2
srmObj.charset=chartype
readfile=srmObj.readtext()
end function
两个参数。URL是文件的路径,好像只能绝对路径。chartype是文件以什么编码存放的。
返回一个字符串,存放文件的内容。
这个函数只能读文本文件。读二进制文件也差不多。要用的人可以自己改
总结:Autocad 与 Excel 通讯的一般Autolisp写法步骤
;;总结:Autocad 与 Excel 通讯的一般步骤
;;1 建立连接
(if
(not
(setq excel (vlax-get-or-create-object "Excel.Application")) ;_ Excel ActiveX
)
(progn
(princ "\nExcel not Install!")
(exit) ;_无法建立时程序退出
)
)
;;2 获得 Workbooks
(setq wks (vlax-get excel 'workbooks))
;;3 用 Workbooks 的 Open 或者 Add 方法打开或者建立新的Workbook
(setq shts (vlax-invoke wks 'open fl))
;;4 获取 excel 的 Sheets (工作簿)
(setq sht (vlax-get excel 'sheets))
;;5 依次处理 Sheet
(vlax-for sh sht ...)
;;6 获取 Sheet 中的 Cells (单元格集合)
(vlax-get sh 'Cells)
;;7 用 item 属性就可以得到指定的单元格了
(vlax-get-property cells 'item 1 2)
;;8 后面就是根据需要对 Cell 进行各种操作
一个简单的将 Excel 中每个 Sheet 特定的区域数据导出程序
;;Author: eachy 2010.06.25
(defun c:tt (/ fl excel fn fp wks shts sht na uc nfl cells i j lst
maxrows nfp)
(if (setq fl (getfiled "Please Select Excel files" "" "xlsx;xls" 8)) ;_excel Filename
(progn
(if
(not
(setq excel (vlax-get-or-create-object "Excel.Application")) ;_ Excel ActiveX
)
(progn
(princ "\nExcel not Install!")
(exit)
)
)
(setq fn (vl-filename-base fl) ;_ Excel FileName
fp (vl-filename-directory fl) ;_ Excel File Directory
)
(setq wks (vlax-get excel 'workbooks) ;_ Workbooks
shts (vlax-invoke wks 'open fl) ;_ Open Excel file , also can use Excel Findfile property
)
(setq sht (vlax-get excel 'sheets)) ;_Get Sheets
(vlax-for sh sht ;_ Foreach all sheet
(setq na (vlax-get sh 'name) ;_ Sheet Name
uc (vlax-get sh 'usedrange) ;_UsedRange
maxRows (vlax-get (vlax-get uc 'Rows) 'Count) ;_Number of Bottom Row in Used
cells (vlax-get sh 'Cells) ;_ActiveSheet Cells
i 7
)
(while (<= i maxrows) ;_Get Needed Rangs
(setq lst (cons (list
(vlax-get (variant-value
(vlax-get-property cells 'item i 2)
)
'text
)
(vlax-get (variant-value
(vlax-get-property cells 'item i 3)
)
'text
)
(vlax-get (variant-value
(vlax-get-property cells 'item i 4)
)
'text
)
)
lst
)
i (1+ i)
)
(setq lst nil)
)
(setq lst
(vl-remove
nil
(reverse
(mapcar
'(lambda (x)
(setq
x
(mapcar '(lambda (a) (vl-string-trim " " a))
x
)
)
(if (vl-every '(lambda (x) (= x "")) x)
nil
(if (= (last x) "")
(list (car x) (cadr x))
x
)
)
)
lst
)
)
)
) ;_Prosss Space
;;Follow to write hdm files
(setq nfl (strcat fp "\\" fn "_" na ".hdm"))
(setq nfp (open nfl "w"))
(write-line "zgh" nfp)
(write-line "BEGIN,坝中1.00:1" nfp)
(foreach x lst
(if (= 3 (length x))
(write-line
(strcat (car x) "," (cadr x) "(" (last x) ")")
nfp
)
(write-line (strcat (car x) "," (cadr x)) nfp)
)
)
(close nfp)
)
(vlax-invoke-method excel "quit") ;_closed excel
(if (vlax-object-released-p excel)
(vlax-release-object excel) ;_this can not close excel
)
)
)
(princ)
)
【转载】齐次坐标概念&&透视投影变换推导
原文地址:http://www.alchemy3d.cn/blog/post/4.html
透视投影是3D固定流水线的重要组成部分,是将相机空间中的点从视锥体(frustum)变换到规则观察体(Canonical View Volume)中,待裁剪完毕后进行透视除法的行为。在算法中它是通过透视矩阵乘法和透视除法两步完成的。
透视投影变换是令很多刚刚进入3D图形领域的开发人员感到迷惑乃至神秘的一个图形技术。其中的理解困难在于步骤繁琐,对一些基础知识过分依赖,一旦对它们中的任何地方感到陌生,立刻导致理解停止不前。
没错,主流的3D APIs如OpenGL、D3D的确把具体的透视投影细节封装起来,比如
gluPerspective(…)就可以根据输入生成一个透视投影矩阵。而且在大多数情况下不需要了解具体的内幕算法也可以完成任务。但是你不觉得,如果想要成为一个职业的图形程序员或游戏开发者,就应该真正降伏透视投影这个家伙么?我们先从必需的基础知识着手,一步一步深入下去(这些知识在很多地方可以单独找到,但我从来没有在同一个地方全部找到,但是你现在找到了)。
我们首先介绍两个必须掌握的知识。有了它们,我们才不至于在理解透视投影变换的过程中迷失方向(这里会使用到向量几何、矩阵的部分知识,如果你对此不是很熟悉,可以参考
可以找到一组坐标(v1,v2,v3),使得
v = v1 a + v2 b + v3 c (1)
而对于一个点p,则可以找到一组坐标(p1,p2,p3),使得
p - o = p1 a + p2 b + p3 c (2)
从上面对向量和点的表达,我们可以看出为了在坐标系中表示一个点(如p),我们把点的位置看作是对这个基的原点o所进行的一个位移,即一个向量 --p - o(有的书中把这样的向量叫做位置向量--起始于坐标原点的特殊向量),我们在表达这个向量的同时用等价的方式表达出了点p:
p = o + p1 a + p2 b + p3 c (3)
(1)(3)是坐标系下表达一个向量和点的不同表达方式。这里可以看出,虽然都是用代数分量的形式表达向量和点,但表达一个点比一个向量需要额外的信息。如果我写出一个代数分量表达(1, 4, 7),谁知道它是个向量还是个点!
我们现在把(1)(3)写成矩阵的形式:
这里(a,b,c,o)是坐标基矩阵,右边的列向量分别是向量v和点p在基下的坐标。这样,向量和点在同一个基下就有了不同的表达:3D向量的第4个代数分量是0,而3D点的第4个代数分量是1。像这种这种用4个代数分量表示3D几何概念的方式是一种齐次坐标表示。
"齐次坐标表示是计算机图形学的重要手段之一,它既能够用来明确区分向量和点,同时也更易用于进行仿射(线性)几何变换。"-- F.S. Hill, JR
这样,上面的(1, 4, 7)如果写成(1,4,7,0),它就是个向量;如果是(1,4,7,1),它就是个点。
下面是如何在普通坐标(Ordinary Coordinate)和齐次坐标(Homogeneous Coordinate)之间进行转换:
从普通坐标转换成齐次坐标时,
如果(x,y,z)是个点,则变为(x,y,z,1);
如果(x,y,z)是个向量,则变为(x,y,z,0)
从齐次坐标转换成普通坐标时,
如果是(x,y,z,1),则知道它是个点,变成(x,y,z);
如果是(x,y,z,0),则知道它是个向量,仍然变成(x,y,z)
以上是通过齐次坐标来区分向量和点的方式。从中可以思考得知,对于平移T、旋转R、缩放S这3个最常见的仿射变换,平移变换只对于点才有意义,因为普通向量没有位置概念,只有大小和方向,这可以通过下面的式子清楚地看出:
而旋转和缩放对于向量和点都有意义,你可以用类似上面齐次表示来检测。从中可以看出,齐次坐标用于仿射变换非常方便。
此外,对于一个普通坐标的点P=(Px, Py, Pz),有对应的一族齐次坐标(wPx, wPy, wPz, w),其中w不等于零。比如,P(1, 4, 7)的齐次坐标有(1, 4, 7, 1)、(2, 8, 14, 2)、(-0.1, -0.4, -0.7, -0.1)等等。因此,如果把一个点从普通坐标变成齐次坐标,给x,y,z乘上同一个非零数w,然后增加第4个分量w;如果把一个齐次坐标转换成普通坐标,把前三个坐标同时除以第4个坐标,然后去掉第4个分量。
由于齐次坐标使用了4个分量来表达3D概念,使得平移变换可以使用矩阵进行,从而如F.S. Hill, JR所说,仿射(线性)变换的进行更加方便。由于图形硬件已经普遍地支持齐次坐标与矩阵乘法,因此更加促进了齐次坐标使用,使得它似乎成为图形学中的一个标准。
简单的线性插值
这是在图形学中普遍使用的基本技巧,我们在很多地方都会用到,比如2D位图的放大、缩小,Tweening变换,以及我们即将看到的透视投影变换等等。基本思想是:给一个x属于[a, b],找到y属于[c, d],使得x与a的距离比上ab长度所得到的比例,等于y与c的距离比上cd长度所得到的比例,用数学表达式描述很容易理解:
这样,从a到b的每一个点都与c到d上的唯一一个点对应。有一个x,就可以求得一个y。
此外,如果x不在[a, b]内,比如x < a或者x > b,则得到的y也是符合y < c或者y > d,比例仍然不变,插值同样适用。
透视投影变换
好,有了上面两个理论知识,我们开始分析这次的主角--透视投影变换。这里我们选择OpenGL的透视投影变换进行分析,其他的APIs会存在一些差异,但主体思想是相似的,可以类似地推导。经过相机矩阵的变换,顶点被变换到了相机空间。这个时候的多边形也许会被视锥体裁剪,但在这个不规则的体中进行裁剪并非那么容易的事情,所以经过图形学前辈们的精心分析,裁剪被安排到规则观察体(Canonical View Volume, CVV)中进行,CVV是一个正方体,x, y, z的范围都是[-1,1],多边形裁剪就是用这个规则体完成的。所以,事实上是
透视投影变换由两步组成:
1) 用透视变换矩阵把顶点从视锥体中变换到裁剪空间的CVV中。
2) CVV裁剪完成后进行透视除法(一会进行解释)。
我们一步一步来,我们先从一个方向考察投影关系。
上图是右手坐标系中顶点在相机空间中的情形。设P(x,z)是经过相机变换之后的点,视锥体由eye--眼睛位置,np--近裁剪平面,fp--远裁剪平面组成。N是眼睛到近裁剪平面的距离,F是眼睛到远裁剪平面的距离。投影面可以选择任何平行于近裁剪平面的平面,这里我们选择近裁剪平面作为投影平面。设P'(x',z')是投影之后的点,则有z' = -N。通过相似三角形性质,我们有关系:
同理,有
这样,我们便得到了P投影后的点P'
从上面可以看出,投影的结果z'始终等于-N,在投影面上。实际上,z'对于投影后的P'已经没有意义了,这个信息点已经没用了。但对于3D图形管线来说,为了便于进行后面的片元操作,例如z缓冲消隐算法,有必要把投影之前的z保存下来,方便后面使用。因此,我们利用这个没用的信息点存储z,处理成:
这个形式最大化地使用了3个信息点,达到了最原始的投影变换的目的,但是它太直白了,有一点蛮干的意味,我感觉我们最终的结果不应该是它,你说呢?我们开始结合CVV进行思考,把它写得在数学上更优雅一致,更易于程序处理。假设能够把上面写成这个形式:
那么我们就可以非常方便的用矩阵以及齐次坐标理论来表达投影变换:
其中
哈,看到了齐次坐标的使用,这对于你来说已经不陌生了吧?这个新的形式不仅达到了上面原始投影变换的目的,而且使用了齐次坐标理论,使得处理更加规范化。注意在把变成
的一步我们是使用齐次坐标变普通坐标的规则完成的。这一步在透视投影过程中称为透视除法(Perspective Division),这是透视投影变换的第2步,经过这一步,就丢弃了原始的z值(得到了CVV中对应的z值,后面解释),顶点才算完成了投影。而在这两步之间的就是CVV裁剪过程,所以裁剪空间使用的是齐次坐标
,主要原因在于透视除法会损失一些必要的信息(如原始z,第4个-z保留的)从而使裁剪变得更加难以处理,这里我们不讨论CVV裁剪的细节,只关注透视投影变换的两步。
矩阵
就是我们投影矩阵的第一个版本。你一定会问为什么要把z写成
有两个原因:
1) P'的3个代数分量统一地除以分母-z,易于使用齐次坐标变为普通坐标来完成,使得处理更加一致、高效。
2) 后面的CVV是一个x,y,z的范围都为[-1,1]的规则体,便于进行多边形裁剪。而我们可以适当的选择系数a和b,使得这个式子在z = -N的时候值为-1,而在z = -F的时候值为1,从而在z方向上构建CVV。
接下来我们就求出a和b:
这样我们就得到了透视投影矩阵的第一个版本:
使用这个版本的透视投影矩阵可以从z方向上构建CVV,但是x和y方向仍然没有限制在[-1,1]中,我们的透视投影矩阵的下一个版本就要解决这个问题。
为了能在x和y方向把顶点从Frustum情形变成CVV情形,我们开始对x和y进行处理。先来观察我们目前得到的最终变换结果:
我们知道-Nx / z的有效范围是投影平面的左边界值(记为left)和右边界值(记为right),即[left, right],-Ny / z则为[bottom, top]。而现在我们想把-Nx / z属于[left, right]映射到x属于[-1, 1]中,-Ny / z属于[bottom, top]映射到y属于[-1, 1]中。你想到了什么?哈,就是我们简单的线性插值,你都已经掌握了!我们解决掉它:
则我们得到了最终的投影点:
下面要做的就是从这个新形式出发反推出下一个版本的透视投影矩阵。注意到是
经过透视除法的形式,而P'只变化了x和y分量的形式,az+b和-z是不变的,则我们做透视除法的逆处理--给P'每个分量乘上-z,得到
而这个结果又是这么来的:
则我们最终得到:
M就是最终的透视变换矩阵。相机空间中的顶点,如果在视锥体中,则变换后就在CVV中。如果在视锥体外,变换后就在CVV外。而CVV本身的规则性对于多边形的裁剪很有利。OpenGL在构建透视投影矩阵的时候就使用了M 的形式。注意到M的最后一行不是(0 0 0 1)而是(0 0 -1 0),因此可以看出透视变换不是一种仿射变换,它是非线性的。另外一点你可能已经想到,对于投影面来说,它的宽和高大多数情况下不同,即宽高比不为1,比如640/480。而CVV的宽高是相同的,即宽高比永远是1。这就造成了多边形的失真现象,比如一个投影面上的正方形在CVV的面上可能变成了一个长方形。解决这个问题的方法就是在对多变形进行透视变换、裁剪、透视除法之后,在归一化的设备坐标(Normalized Device Coordinates)上进行的视口(viewport)变换中进行校正,它会把归一化的顶点之间按照和投影面上相同的比例变换到视口中,从而解除透视投影变换带来的失真现象。进行校正前提就是要使投影平面的宽高比和视口的宽高比相同。
便利的投影矩阵生成函数
3D APIs都提供了诸如gluPerspective(fov, aspect, near, far)或者D3DXMatrixPerspectiveFovLH(pOut, fovY, Aspect, zn, zf)这样的函数为用户提供快捷的透视矩阵生成方法。我们还是用OpenGL的相应方法来分析它是如何运作的。
gluPerspective(fov, aspect, near, far)
fov即视野,是视锥体在xz平面或者yz平面的开角角度,具体哪个平面都可以。OpenGL和D3D都使用yz平面。
aspect即投影平面的宽高比。
near是近裁剪平面的距离
far是远裁剪平面的距离。
上图中左边是在xz平面计算视锥体,右边是在yz平面计算视锥体。可以看到左边的第3步top = right / aspect使用了除法(图形程序员讨厌的东西),而右边第3步right = top x aspect使用了乘法,这也许就是为什么图形APIs采用yz平面的原因吧!
下篇
原文地址:http://www.alchemy3d.cn/blog/post/5.html
在上一篇文章中我们讨论了透视投影变换的原理,分析了OpenGL所使用的透视投影矩阵的生成方法。正如我们所说,不同的图形API因为左右手坐标系、行向量列向量矩阵以及变换范围等等的不同导致了矩阵的差异,可以有几十个不同的透视投影矩阵,但它们的原理大同小异。这次我们准备讨论一下 Direct3D(以下简称D3D)以及J2ME平台上的JSR184(M3G)(以下简称M3G)的透视投影矩阵,主要出于以下几个目的:
(1) 我们在写图形引擎的时候需要采用不同的图形API实现,当前主要是OpenGL和D3D。虽然二者的推导极为相似,但D3D的自身特点导致了一些地方仍然需要澄清。
(2) DirectX SDK的手册中有关于透视投影矩阵的一些说明,但并不详细,甚至有一些错误,从而使初学者理解起来变得困难,而这正是本文写作的目的。
(3) M3G是J2ME平台上的3D开发包,采用了OpenGL作为底层标准进行封装。它的透视投影矩阵使用OpenGL的环境但又进行了简化,值得一提。
本文努力让读者清楚地了解D3D与M3G透视投影矩阵的原理,从而能够知道它与OpenGL的一些差别,为构建跨API的图形引擎打好基础。需要指出的一点是为了完全理解本文的内容,请读者先理解上一篇文章《深入探索透视投影变换》的内容,因为OpenGL和它们的透视投影矩阵的原理非常相似,因此这里不会像上一篇文章从基础知识讲起,而是对比它们的差异来推导变换矩阵。我们开始!
前面提到,不同API的基本差异导致了最终变换矩阵的不同,而导致OpenGL和D3D的透视投影矩阵不同的原因有以下几个:
(1) OpenGL默认使用右手坐标系,而D3D 默认使用左手坐标系。
(2) OpenGL使用列向量矩阵乘法而D3D使用行向量矩阵乘法。
(3) OpenGL的CVV的Z范围是[-1, 1],D3D的CVV的Z范围是[0, 1]。
以上这些差异导致了最终OpenGL和D3D的透视投影矩阵的不同。
D3D的透视投影矩阵推导
我们先来看最最基本的透视关系图(上一篇文章开始的时候使用的图):
这里我们考察的是xz平面上的关系,yz平面上的关系同理。这里o是相机位置。np是近裁剪平面,也是投影平面,N是它到相机的距离。fp是远裁剪平面,F是它到相机的位置。p是需要投影的点,p'是投影之后的点。根据相似三角形定理,我们有
则有
注意到OpenGL使用右手坐标系,因此应该使用-N(请参考上一篇文章的这一步),而D3D使用左手坐标系,因此使用N,这是二者的不同点之一。这样,我们得到投影之后的点
第三个信息点是变换之后的z在投影平面上的位置,也就是N,它已经没用了,我们把p'写成
从而用第三个没用信息点它来存储z(如果读者对这一点不太了解,请参考上一篇文章)。接下来我们求出a和b,从而在z方向上构建CVV。请注意这里是OpenGL和D3D的另一个不同点,OpenGL的CVV的z范围是[-1, 1],而D3D的CVV的z范围是[0, 1]。也就是说,D3D 中在近裁剪平面上的点投影之后的点会处于CVV的z=0平面上,而在远裁剪平面上的点投影之后的点会在CVV的z=1平面上。这样我们的计算方程就是
从而我们得到了透视投影矩阵的第一个版本
即
这个时候第三个分量变换到CVV情形了,CVV的z范围是[0,1]。接下来根据上一篇文章所讲到的,我们要把前两个分量变成CVV情形,CVV的 x和y范围是[-1, 1],如下图所示:
使用线性插值,我们有:
这里left和right是投影平面的左右范围,top和bottom是投影平面的上下范围。xcvv和ycvv是我们需要算出的在CVV情形中的 x和y,也就是我们要计算出的结果。但在算出它们之前,我们先把上面的式子写成:
这里有一个需要注意的地方,如果投影平面在x方向上居中,则
那么第一个式子就可以销掉等号两边的1/2,写成
同理,如果投影平面在y方向上居中,则第二个式子可以写成
则我们现在分两种情况讨论:
(1) 投影平面的中心和x-y平面的中心重合(在x和y方向上都居中)
(2) 一般情况
我们分别讨论:
(1)特殊情况方程
这组是特殊情况,方程比较简单,但也是使用频率最高的方式(这是D3DXMatrixPerspectiveLH、 D3DXMatrixPerspectiveRH、D3DXMatrixPerspectiveFovLH、 D3DXMatrixPerspectiveFovRH四个方法所使用的情况)。我们导出它:
则我们反推出透视投影矩阵:
其中
而r-l和t-b可以分别看作是投影平面的宽w和高h。最后那个矩阵就是D3D的透视投影矩阵之一。另外呢,如果我们不知道right、left、 top以及bottom这几个参量,也可以根据视野(FOV - Field Of View)参量来求得。下面是两个平面的视野关系图:
其中,两个fov分别是在x-z以及y-z平面上的视野。如果只给了一个视野,也可以通过投影平面的宽高比计算出来:
用一个视野算出w或者h,然后用宽高比算出h或者w。
(2)一般情况的方程
这组方程比较繁琐,但更具一般性(和OpenGL一般矩阵的推导一致,这也是D3DXMatrixPerspectiveOffCenterLH和 D3DXMatrixPerspectiveOffCenterRH两个方法所使用的情况)。我们导出它:
我们继续反推出透视投影矩阵:
其中
最后那个矩阵就是D3D的一般透视投影矩阵。
好了,目前为止,我们已经导出了D3D的两个透视投影矩阵。下面我把上一篇导出的OpenGL的透视投影矩阵写出来,大家可以拿它和刚刚导出的 D3D的一般性透视投影矩阵做一个对比。
如果仔细观察,可以发现二者在元素的布局上是一个转置的关系,这个就是由它们使用的左右手坐标系以及使用的行列矩阵的差异造成的。而另外在一些元素的细节上也存在着差异,这是由于D3D的CVV的z范围不同造成的。可见在原理相同的情况下,细微的环境差异可以造成非常大的变化,而这就是透视投影矩阵存在诸多不同版本的原因。一般情况的透视投影矩阵也可以使用视野方式来定义,方法和特殊情况相同。
M3G是对OpenGL进行的一个封装,它的透视投影变换矩阵被放到了类Camera里面。因为它封装了OpenGL,因此环境和OpenGL相同:右手坐标系、列向量乘法、CVV范围[-1, 1]。它唯一和OpenGL有些差异的地方就在于它只使用投影平面的中心和x-y平面的中心重合(在x和y方向上都居中)的情况(就是我们上面D3D的第一种特殊情况)。我们用OpenGL透视投影矩阵最终版本来说明(再次提醒,如果读者对此感到迷惑,请参考第一篇文章):
上面是OpenGL透视投影矩阵的最终版本,也是一般性版本,我们要把它变成特殊性,版本,非常简单,和上面D3D的特殊情况一样,我们从对x和y 进行插值的那一步来看:
和D3D的第一种情况一样,销掉两边的1/2,得到:
则我们反推出透视投影矩阵:
最右边那个矩阵就是M3G的透视投影矩阵。仍然可以通过视野参数来设置透视投影矩阵,这里请读者自行推导,方法与上面D3D的完全相同。
二维图形的几何变换
正如我们在附录中提到的那样,用齐次坐标表示点的变换将非常方便,因此在本节中所有的几何变换都将采用齐次坐标进行运算。二维齐次坐标变换的矩阵的形式是:
这个矩阵每一个元素都是有特殊含义的。
形进行平移变换;[g h]是对图形作投影变换;[i]则是对图形整体进行缩放变换。
1)平移变换 
2)缩放变换 
3)旋转变换![]()

![]()
4)对称变换
对称变换其实只是a、b、d、e取0、1等特殊值产生的一些特殊效果。例如:
当b=d=0,a=-1,e=1 时有x´=-x,y´=y,产生与y轴对称的图形。
当b=d=0,a=-1,e=-1时有x´=x,y´=-y,产生与x轴对称的图形。
当b=d=0,a=e=-1时有x´=-x,y´=-y,产生与原点对称的图形。
当b=d=1,a=e=0时有x´=y,y´=x,产生与直线y=x对称的图形。
当b=d=-1,a=e=0时有x´=-y,y´=-x,产生与直线y=-x对称的图形。
5)错切变换
当d=0时,x´=x+by,y´=y,此时,图形的y坐标不变,x坐标随初值 (x,y)及变换系数b作线性变化。
当b=0时,x´=x,y´=dx+y,此时,图形的x坐标不变,y坐标随初值 (x,y)及变换系数d作线性变化。
6)复合变换
如果图形要做一次以上的几何变换,那么可以将各个变换矩阵综合起来进行一步到位的变换。复合变换有如下的性质:
对同一图形做两次平移相当于将两次的平移两加起来:
两次连续的缩放相当于将缩放操作相乘:

两次连续的旋转相当于将两次的旋转角度相加:
缩放、旋转变换都与参考点有关,上面进行的各种变换都是以原点为参考点的。如果相对某个一般的参考点(xf,yf)作缩放、旋转变换,相当于将该点移到坐标原点处,然后进行缩放、旋转变换,最后将(xf,yf)点移回原来的位置。切记复合变换时,先作用的变换矩阵在右端,后作用的变换矩阵在左端。
![]()


Qleader 设置中的某个选项存储位置
Qleader 设置中有个“默认提示文字宽度”选项,默认是勾选的,当去除后保持图形,下次打开设置中会保持,这说明该选项是随图保存的,翻遍了Help也没有找到变量控制。经过一番研究,终于找到了存储位置:)
1 打开一个新图,什么都不做,直接另存为 dxf
2 另开一个新图,运行 Qleader -〉设置-〉去掉“默认提示文字宽度”选项,确认退出,不要标注,另存为 dxf
3 用 Uedit 或者其他软件比较两个 dxf,找出不同点
经过查找,排除一些无关的差异(比如编辑时间等等),会找到一个 68 的位置由 1 变为 0,初步判断应该是这里(Acad 中 1 和 0 很多地方表示开关),往前翻,看到 Xrecord 字样,再找到前面最近的 5 (5 是句柄标志),打开该 dxf,用 handent 查找这个句柄,会看到是 AcadDim 词典,再次试验 Qleader 命令,
勾选时状态:
_$ (dictsearch (namedobjdict) "AcadDim")
((-1 . <图元名: 7eb1e4b8>) (0 . "XRECORD") (5 . "2FF7") (102 . "{ACAD_REACTORS") (330 . <图元名: 7efc0c60>) (102 . "}") (330 . <图元名: 7efc0c60>) (100 . "AcDbXrecord") (280 . 1) (90 . 990106) (3 . "") (60 . 0) (61 . 0) (62 . 1) (63 . 3) (64 . 0) (65 . 0) (66 . 0) (67 . 3) (68 . 1) (69 . 0) (70 . 0) (71 . 0) (72 . 0) (40 . 0.0) (170 . 0))
_$
关闭默认:
_$ (dictsearch (namedobjdict) "AcadDim")
((-1 . <图元名: 7eb1e9e0>) (0 . "XRECORD") (5 . "3094") (102 . "{ACAD_REACTORS") (330 . <图元名: 7efc0c60>) (102 . "}") (330 . <图元名: 7efc0c60>) (100 . "AcDbXrecord") (280 . 1) (90 . 990106) (3 . "") (60 . 0) (61 . 0) (62 . 1) (63 . 3) (64 . 0) (65 . 0) (66 . 0) (67 . 3) (68 . 0) (69 . 0) (70 . 0) (71 . 0) (72 . 0) (40 . 0.0) (170 . 0))
_$
其他的没有操作命令没有,可以看到只有 68 发生了变化,可以断定就是这个值了,下面用 Lisp 试试
(vl-catch-all-apply
(function
(lambda (/ acdim)
(setq acdim (dictsearch (namedobjdict) "Acaddim"))
(if (assoc 68 acdim)
(entmod (subst '(68 . 0)
'(68 . 1)
acdim
)
)
)
)
)
nil
)
用 entmod 测试成功。
通过这个可以看到 Cad 中的有些命令的默认值是保存在词典中,只是没有相关资料,要自己动手试验。而且这个 AcadDim 词典要在用过 Dim 类命令后就会自动生成,打开cad不做任何操作是没有这个词典的。其他的参数暂时没有试验是控制哪些选项的,有兴趣了再试!
一个文字左对齐并重新编号程序
排序设置
一个求点集合的凸包的lisp程序
;;;************************************************************************
;;;一个求点集合的凸包的lisp程序--------------------------------------------
;;;------采用的算法为礼品包扎法--------------------------------------------
;;;方法为最右端的点开始处理,将该点作为凸包边界的第一个点P1,从最初的垂直线
;;;方向绕P1顺时针旋转,直到碰到另一个P2这就是凸包边界的第二个点P2,依此类推
;;;p2求得p3......直到又重新回到p1,已经考虑了各种退化情况和浮点运算,其算法
;;;时间不超过O(n.h),其中h是凸包的复杂度,时间还是很快的。大家不妨验证。
;;;参考文献<<计算几何-算法及其应用>>(第二版),以及参考了其他网站的一些源代码
;;;------------------------------------------------------------------------
;;;其中程序主段是核心算法,其他的附加程序为取得点集,画凸包边界线,测试大量
;;;点集函数处理所花费的时间。----------------------------------------------
;;;用法: 加载lisp运行test选取点,直线段,或多义线(全是直线段组成)即可。----
;;;************************************************************************
(defun C:test (/ sel t0 ptlist pp 2Pi)
(setq 2Pi (* 2 pi))
;;(setq sel (ssget (list '(0 . "POINT")))) ;选择点集
(setq sel (ssget))
(if (setq ptlist (getpt sel)) ;构造点集
(progn
(setq t1 (getvar "CDATE")) ;计时开始
;;(setq t0 (getvar "TDUSRTIMER")) ;开始计时
(setq p1 (hull ptlist)) ;求凸包
(setq t2 (getvar "CDATE")) ;计时结束
(setq pp (Max-distance p1))
(setq t3 (getvar "CDATE"))
(princ "\n求点集的凸包用时:")
(princ (* (- t2 t1) 1e6))
(princ "秒")
(princ "\n凸包共有")
(princ (length p1))
(princ "个顶点")
(princ "\n求凸包的直径用时:")
(princ (* (- t3 t2) 1e6))
(princ "秒")
(princ "\n总用时=最大距离点对用时:")
(princ (* (- t3 t1) 1e6))
(princ "秒")
;;(princ (* (- (getvar "TDUSRTIMER") t0) 86400)) ;结束计时
)
)
(if (null pp)
(alert "点的有效数目太小,请重新输入!")
(entmake ;画凸包
(append
'( (0 . "LWPOLYLINE")
(100 . "AcDbEntity")
(100 . "AcDbPolyline")
)
(list (cons 90 (length pp))) ;顶点个数
(mapcar '(lambda (x) (cons 10 x)) pp) ;多段线顶点
(list (cons 70 0)) ;闭合的
(list (cons 62 1)) ;红色的
)
)
)
(princ)
)
;;;==========================
;;;程序主段,可以单独成为函数
;;;==========================
;;;右半部的凸包
(defun hull1 (pts MaxPt MinPt / nextPt hullPt)
(if (< (length pts) 3)
pts
(progn
(setq nextPt (Max-angle1 pts MaxPt)) ;从最上面的点开始
(setq hullPt (cons nextPt (cons MaxPt hullPt))) ;顺时针求得第一点
(while (not (equal nextPt MinPt 1e-8)) ;到最下面的点为止
(setq nextPt (Max-angle pts nextPt)) ;循环求凸包每一点
(setq hullPt (cons nextPt hullPt)) ;把每点加入凸包集
)
)
)
)
;;;左半部的凸包
(defun hull (ptlist / revPts 2ndPts maxYp1 minYp1 maxYp2 minYp2
ptlst1 ptlst2 +ptlst -ptlst hullp1 hullp2)
(setq revPts (mapcar 'reverse ptlist)) ;点表的X和Y交换
(setq 2ndPts (mapcar 'cadr ptlist)) ;点表的Y值的表
(setq maxYp1 (reverse (assoc (apply 'max 2ndPts) revPts)));最上面的点
(setq minYp1 (reverse (assoc (apply 'min 2ndPts) revPts)));最下面的点
(setq maxYp2 (list (- (car maxYp1)) (cadr maxYp1))) ;镜像后最上面的点
(setq minYp2 (list (- (car minYp1)) (cadr minYp1))) ;镜像后最下面的点
(foreach n ptlist ;把点表分成两部分
(if (> (det minYp1 n maxYp1) 0) ;如果左转
(setq ptlst1 (cons n ptlst1)) ;加入右半部分
(setq ptlst2 (cons n ptlst2)) ;否则左半部分
)
)
(setq +ptlst (cons minYp1 (cons maxYp1 ptlst1))) ;右半部分
(setq -ptlst (Mirror-list-X ptlst2)) ;左半部分以Y轴镜像
(setq hullp1 (hull1 +ptlst maxYp1 minYp1)) ;右半部分的凸包
(setq hullp2 (hull1 -ptlst maxYp2 minYp2)) ;左半部分镜像的凸包
(setq hullp2 (cdr (reverse (cdr hullp2))))
(setq hullp2 (Mirror-list-X hullp2)) ;左半部分的凸包
(append hullp1 hullp2) ;把凸包左右相加
)
;;;镜像左半部分
(defun Mirror-list-X (ptlist)
(mapcar (function (lambda (x)(list (- (car x))(cadr x)))) ptlist)
)
;;;求点集中夹角的最大值的点
(defun Max-angle (ptlist pt / An)
(setq An (mapcar (function (lambda (x) (angle pt x))) ptlist))
(nth (- (length An) (length (member (apply 'max An) An))) ptlist)
)
(defun Max-angle1 (ptlist pt / An)
(setq An
(mapcar
(function
(lambda (x)
(if
(and
(equal (cadr x) (cadr pt) 1e-8)
(> (car x) (car pt))
)
(+ 2Pi (- (car x) (car pt)))
(angle pt x)
)
)
)
ptlist
)
)
(nth (- (length An) (length (member (apply 'max An) An))) ptlist)
)
;;定义三点的行列式,即三点之倍面积
(defun det (p1 p2 p3 / dx1 dy1 dx2 dy2)
(setq dx1 (- (car p2) (car p1))
dy1 (- (cadr p2) (cadr p1))
dx2 (- (car p3) (car p1))
dy2 (- (cadr p3) (cadr p1))
)
(- (* dx1 dy2) (* dx2 dy1))
)
(defun Max-distance (ptlist / maxD halfPi HullP1 l HullP2 midPts
i j Pi+1 Qi+1 Ai+1 D-i PairPt)
(setq MaxD nil)
(setq 2Pi (* 2 pi))
(setq halfPi (/ Pi 2))
(setq HullP1 (Hull ptlist))
(setq l (1- (length HullP1)))
(setq HullP2 (append (cdr HullP1) (list (car HullP1))))
(setq midPts (mapcar 'mid-pt HullP1 HullP2))
(setq i 1)
(foreach pt (reverse (cdr (reverse HullP1)))
(setq j i)
(setq Pi+1 (nth i HullP1))
(setq Qi+1 (nth i midPts))
(setq Ai+1 (ang Qi+1 pt Pi+1))
(while (and (< Ai+1 halfPi) (< j l))
(setq j (1+ j))
(setq Ai+1 (ang (nth j midPts) Pt (nth j HullP1)))
)
(setq D-i (distance pt (nth j HullP1)))
(setq MAXD (cons (list D-i (1- i) j) MAXD))
(setq i (1+ i))
)
(setq PairPt (assoc (apply 'Max (mapcar 'car MaxD)) MaxD))
(list
(nth (cadr PairPt) HullP1)
(nth (caddr PairPt) HullP1)
)
)
(defun mid-pt (p1 p2)
(list
(* (+ (car p1) (car p2)) 0.5)
(* (+ (cadr p1) (cadr p2)) 0.5)
)
)
;;;============
;;;程序主段结束
;;;============
;;;依据晓东网站的代码改写而成的取点函数
(defun getpt1 (ss / a b c d i p)
(setq i 0)
(if ss
(repeat (sslength ss)
(setq a (ssname ss i))
(setq b (entget a))
(setq c (cdr (assoc 10 b)))
(setq c (list (car c) (cadr c)))
(setq p (cons c p))
(setq i (1+ i))
)
)
p
)
;;定义三点的夹角函数
(defun ang (p1 p2 p3 / x)
(setq x (abs (- (angle p1 p3) (angle p1 p2))))
(if (< x Pi) x (- 2Pi x))
)
(defun C:tt (/ p1 p2 p3)
(initget 1)
(setq p1 (getpoint "\n输入第一点:"))
(setq p2 (getpoint "\n输入第二点:"))
(setq p3 (getpoint "\n输入第三点:"))
(ang p1 p2 p3)
)
;;;取点函数2
(defun getpt (ss / i listpp a b c d)
(setq i 0)
(if ss
(repeat (sslength ss)
(setq a (ssname ss i))
(setq b (entget a))
(setq ename (cdr (assoc 0 b)))
(cond
( (= ename "LWPOLYLINE")
(setq c (get-LWpolyline-vertexs b))
(setq listpp (append c listpp))
)
( (= ename "LINE")
(setq c (cdr (assoc 10 b)))
(setq d (cdr (assoc 11 b)))
(setq c (list (car c) (cadr c)))
(setq d (list (car d) (cadr d)))
(setq listpp (cons c listpp))
(setq listpp (cons d listpp))
)
( (= ename "POINT")
(setq c (cdr (assoc 10 b)))
(setq c (list (car c) (cadr c)))
(setq listpp (cons c listpp))
)
)
(setq i (1+ i))
)
)
listpp
)
;;取得多边形顶点
(defun get-LWpolyline-vertexs (entlst / n lst)
(foreach n entlst
(if (= (car n) 10)
(setq lst (cons (cdr n) lst))
)
)
lst
)
AutoCAD 2000启动时各个文件加载的顺序
| 问题: |
| 如果知道AutoCAD 2000启动时各个文件加载的顺序,可能就会明白一个文件对另一个文件的影响。例如,已经在从acad.lsp文件加载的LISP程序中定义了一个函数,但是在启动AutoCAD 2000后,这个函数却不工作。这是因为acaddoc.lsp是在acad.lsp文件之后加载,此函数已经被acaddoc.lsp文件重新定义了。 |
解决方法: |
| 以下是第一次启动AutoCAD 2000时,AutoCAD、Express Tools和自定义文件的加载顺序。 |
| 文件 | 使用者 | |
| acad2000.lsp | AutoCAD | |
| acad.rx | User | |
| acad.lsp | User | |
| acad2000doc.lsp | AutoCAD | |
| acetutil.fas | Express Tools | |
| acaddoc.lsp | User | |
| mymenu.mnc | User | |
| mymenu.mnl | User | |
| acad.mnc | AutoCAD | |
| acad.mnl | AutoCAD | |
| acetmain.mnc | Express Tools | |
| acetmain.mnl | Express Tools | |
| s::startup | User |
DBX-DwgScan
;;;************************************************************************
;;; Filename: DBX-DwgScan.LSP
;;; Author: David Stein
;;; Date: April 2002
;;; Purpose: ObjectDBX Drawing Scan Example for Visual LISP Developers Bible book
;;; Copyright (C)2002 David M. Stein, All Rights Reserved.
;;;************************************************************************
;;; Usage: (DWGSCAN tablename itemname drawings)
;;;
;;; Where:
;;; tablename = string name of table (ex. Blocks, Ltypes, DimStyles, etc.)
;;; itemname = string name of item to search for
;;; drawings = list of drawing filenames (full paths included for each)
;;;
;;; Returns: A list of filenames that contain the itemname being searched for.
;;;************************************************************************
;;; For use with AutoCAD 2000, 2000i and 2002 or related vertical products
;;; only. Will NOT work with R14, any LT or Inventor products.
;;; Must be compiled as a separate-namespace VLX application
;;;************************************************************************
(vl-doc-export 'dwgscan)
(vl-load-com)
(defun DLLRegister (dll)
(startapp "regsvr32.exe" (strcat "/s \"" dll "\""))
)
(defun ProgID->ClassID (ProgID)
(vl-registry-read
(strcat "HKEY_CLASSES_ROOT\\" progid "\\CLSID")
)
)
(defun DBX-Register (/ classname)
(setq classname "ObjectDBX.AxDbDocument")
(cond
((ProgID->ClassID classname))
((and
(setq server (findfile "AxDb15.dll"))
(DLLRegister server)
(ProgID->ClassID classname)
)
(ProgID->ClassID classname)
)
((not (setq server (findfile "AxDb15.dll")))
(alert
"Error: Cannot locate ObjectDBX Type Library (AxDb15.dll)..."
)
)
(T
(DLLRegister "ObjectDBX.AxDbDocument")
(or
(ProgID->ClassID "ObjectDBX.AxDbDocument")
(alert
"Error: Failed to register ObjectDBX ActiveX services..."
)
)
)
)
)
(defun DBX-GetTableList
(filename tblname / dbxdoc out name)
(cond
((findfile filename)
(if (not (DBX-Register))
(vl-exit-with-error
"\nUnable to register ObjectDBX from Visual LISP."
)
)
(setq dbxdoc
(vla-GetInterfaceObject
(vlax-get-acad-object)
"ObjectDBX.AxDbDocument"
)
)
(cond
((vl-catch-all-error-p
(vl-catch-all-apply
'vla-Open
(list
dbxdoc
(findfile filename)
)
)
)
(princ (strcat "\nUnable to open drawing: " filename))
)
(T
(vlax-For tblItem (DBX-TableGet tblName dbxdoc)
(setq name (vla-get-Name tblItem))
(if (/= (substr name 1 1) "*")
(setq out
(cons name out)
)
)
)
)
) ; cond
(vlax-release-object dbxdoc)
)
(T
(strcat (princ "\nUnable to open file: " filename))
)
) ; cond
(if out
(reverse out)
)
)
(defun DBX-TableGet (tName object)
(cond
((= (strcase tName) "BLOCKS") (vla-get-Blocks object))
((= (strcase tName) "LAYERS") (vla-get-Layers object))
((= (strcase tName) "TEXTSTYLES")
(vla-get-textstyles object)
)
((= (strcase tName) "DIMSTYLES") (vla-get-dimstyles object))
((= (strcase tName) "LINETYPES") (vla-get-linetypes object))
((or
(= (strcase tName) "PLOTCONFIGURATIONS")
(= (strcase tName) "PAGESETUPS")
)
(vla-get-plotconfigurations object)
)
((= (strcase tName) "LAYOUTS") (vla-get-Layouts object))
((= (strcase tName) "GROUPS") (vla-get-Groups object))
(T
(vl-exit-with-error
"\n(dbx-dwgscan error): Invalid table name specified."
)
)
)
)
(defun DWGSCAN
($table $name $dwgfiles / $files
$dwgs $path $collection n
out
)
(cond
((and $table $name $dwgfiles)
(princ
(strcat
"\nScanning "
(itoa (length $dwgfiles))
" drawings for "
(strcase (substr $table 1 (1- (strlen $table))) t)
" ["
$name
"]..."
)
)
(foreach n $dwgfiles
(cond
((setq $collection (DBX-GetTableList n $table))
(cond
((member (strcase $name) (mapcar 'strcase $collection))
(setq out (cons n out))
)
)
(setq $collection nil)
)
;;( T (princ "\nUnable to query table collection in target drawing.") )
)
)
)
(T
(princ "\nUsage: (DWGSCAN tablename itemname drawingfiles)")
)
)
(if out
(reverse out)
)
)
(princ)
发图前处理下图形 Beta 0.01
;;发图前处理下图形 Beta 0.01
;;配合 dwfout -> dwfin 使用
;;如果可能再将图形不等比缩放一点点(不包括图框)
;;需要改进的:
;; 1 增加处理 arc circle spline 及保持宽度的Pline部分功能
;; 2 对Line分解为一点点间隙的特定长度的Trace
;;
;; Writen By eachy 2003.07.15
;; Email: eachy@21cn.com
;; 图形比较大时要耐心等等呦
(defun c:killdwg (/ $same_layer activedoc modelspace
lay ln tn ss sp ep
n ptlst layers blocks
)
(setvar "cmdecho" 0)
(if (null (tblsearch "layer" "Defpoints"))
(vl-cmdf "_.layer" "m" "Defpoints" "")
)
(if (/= (getvar "clayer") "Defpoints")
(setvar "clayer" "Defpoints")
)
;;统一图层并保持颜色
(defun $same_layer (vla-object laylst / la)
(setq la (vla-get-layer vla-object))
(vla-put-layer vla-object "Defpoints")
(if (= 256 (vla-get-color vla-object))
(vla-put-color vla-object (cadr (assoc la laylst)))
)
)
(setq activedoc (vla-get-activedocument (vlax-get-acad-object))
modelspace (vla-get-modelspace activedoc)
layers (vla-get-layers activedoc)
blocks (vla-get-blocks activedoc)
)
;;获取图层列表(("layer1" color1) ("layer2" color2) ... )
(vlax-for item layers
(setq
lay (cons (list (vla-get-name item) (vla-get-color item)) lay)
)
)
(princ "\n正在处理实体....")
;;统一图层并保持颜色
(vlax-for for-item modelspace
($same_layer for-item lay)
)
;;用DWFOUT-》DWFIN的图去掉处理图块
;;处理块定义
(princ "\n正在处理图块定义...")
(vlax-for for-item blocks
(vlax-for obj for-item
($same_layer obj lay)
)
)
;;处理图块结束
;|循环分解图中所有块
(while (setq ss (ssget "x" '((0 . "region,insert,dimension,hatch"))))
(vl-cmdf "_.explode" ss)
)|;
;;变换图中Line实体
(setq n 0)
(setq ss (ssget "x" '((0 . "line"))))
(if ss
(progn
(princ "\n正在处理Line实体...")
(repeat (sslength ss)
(setq ln (vlax-ename->vla-object (ssname ss n)))
(setq sp (vlax-curve-getstartpoint ln)
ep (vlax-curve-getendpoint ln)
)
(setq ptlst (apply 'append
(list (polar sp (angle sp ep) 1e-10)
;此值可自行调整以防止用程序反处理经过本程序处理的图形
(polar sp (/ pi 3) 1e-10)
(polar ep (/ pi 3) 1e-10)
(polar ep (angle ep sp) 1e-10)
)
)
)
;;增加trace
(setq tn (vla-addtrace
modelspace
(vlax-safearray-fill
(vlax-make-safearray vlax-vbDouble '(0 . 11))
ptlst
)
)
)
(vla-put-color tn (vla-get-color ln))
(vla-erase ln)
(setq n (1+ n))
)
)
)
(vl-cmdf "_.purge" "all" "*" "n")
(princ "\n\t===处理完成,OK!====")
(princ)
)
字串中西文拆分
;;中西文拆分 By Eachy
(defun yb:string_split (str / strlst strlst1 hz_str e_str)
(setq strlst (vl-string->list str)
strlst1 '()
)
(while strlst
(cond
((> (car strlst) 159)
(setq e_str nil)
(if hz_str
(setq hz_str (append (list (car strlst)) hz_str))
(setq hz_str (list (car strlst)))
)
(setq strlst (cdr strlst))
(if (or (< (car strlst) 159)
(= strlst "")
)
(if strlst1
(setq strlst1 (append strlst1 (list hz_str)))
(setq strlst1 (list hz_str))
)
)
)
((< (car strlst) 159)
(setq hz_str nil)
(if e_str
(setq e_str (append (list (car strlst)) e_str))
(setq e_str (list (car strlst)))
)
(setq strlst (cdr strlst))
(if (or (> (car strlst) 159)
(= strlst "")
)
(if strlst1
(setq strlst1 (append strlst1 (list e_str)))
(setq strlst1 (list e_str))
)
)
)
)
)
(mapcar 'vl-list->string (mapcar 'reverse strlst1))
)
删除图层过滤器
给甲方图之前简单处理下图纸
;;给甲方图之前简单处理下图纸
;;修改所有实体至 0 图层
;;不要用源图,一定要用 save as 的图纸!!!
(defun ea:chglay (/ for-item docment modelspace)
(setq docment (vla-get-activedocument (vlax-get-acad-object))
modelspace (vla-get-modelspace docment)
)
(vlax-for for-item modelspace
(vla-put-layer for-item "0")
)
)
;;如果有块处理不了用下面的再处理
;;处理图块定义
(defun ea:chgblklay (/ docment for-item blocks)
(setq docment (vla-get-activedocument (vlax-get-acad-object))
blocks (vla-get-blocks docment)
)
(vlax-for for-item blocks
(vlax-for obj for-item
(vla-put-layer obj "0")
)
)
)
;;修理完成后在 purge all
使用vlisp在Ms Access中添加新的记录
(defun c:CreateLT ()
(vl-load-com)
(setq pDoc (vla-get-ActiveDocument (vlax-get-acad-object)))
;;get the DBConnect
(setq pDBObj (vla-GetInterfaceObject
(vlax-get-acad-object)
"CAO.dbConnect"
)
)
(if (null pDBObj)
(progn
(alert "Cannot create CAO Automation server.")
(exit)
)
)
;;get the linktemplates
(setq pLTs (vlax-invoke-method pDBObj "GetLinkTemplates" pDoc))
;;prepare the keydescriptions
(setq pKeyDescs (vla-GetInterfaceObject
(vlax-get-acad-object)
"CAO.KeyDescriptions"
)
)
(vlax-invoke-method pKeyDescs "ADD" "TAG_NUMBER" 3 nil nil)
(vlax-invoke-method
pKeyDescs "ADD" "Manufacturer" 1 nil nil)
;;create two link templates
(vlax-invoke-method
pLTs "ADD" "jet_dbsamples" nil
nil "COMPUTER" "LTCreatedByVLispCAO"
pKeyDescs
)
(vlax-invoke-method
pLTs "ADD" "jet_dbsamples"
nil nil "COMPUTER" "LTtobeDeleted"
pKeyDescs
)
;;sample code to show how to delete the link template
(vlax-invoke-method pLTs "delete" "LTtobeDeleted")
)
屏幕菜单切换
;;
;;屏幕菜单开关与切换控制For 2000+,R14中需手动打开屏幕菜单 written by eachy
;;
(defun c:xscrmnu (/ scr #scr_display #acadver)
;;保存屏幕菜单初始状态,0 菜单关闭
(setq #scr_display (getvar "screenboxes")
#acadver (atof (getvar "acadver"))
)
(if (and (< #acadver 15.0)
(= #scr_display 0)
)
(progn
(princ "\n\t您使用的版本为R14,请先手动加载屏幕菜单!!!")
(exit)
)
)
(if #xdscrmnu
(progn
(if (and (> #acadver 15.0)
(if (> #acadver 15.0)
(= (vl-bb-ref '#mnu_display) 0)
)
)
(c:xscrmnud)
(progn
(setq scr (last (xdrx_string_tok (getvar "menuname") "\\")))
(menucmd (strcat "s=" scr ".screen"))
)
)
(setq #xdscrmnu nil)
)
(progn
(if (> #acadver 15.0)
(progn
(setvar "menuctl" 0)
(cond
((= (getvar "screenboxes") 0)
(c:xscrmnud)
)
((= (vl-bb-ref '#mnu_display) 0)
(c:xscrmnud)
)
(T)
)
)
)
(menucmd "s=xdsoft.screen")
(setq #xdscrmnu T)
)
)
(princ)
)
两个矢量的叉积
;|两个矢量的叉积 2003年02月11日
| i j k |
v1xv2 = | x1 y1 z1 |= (y1z2-y2z1, z1x2-z2x1, x1y2-x2y1)
| x2 y2 z2 |
|;
;;向量的差积;
(defun ea:vector_CrossProduct
(ve1 ve2 / x1 y1 z1 x2 y2 z2)
(setq x1 (car ve1)
y1 (cadr ve1)
z1 (last ve1)
x2 (car ve2)
y2 (cadr ve2)
z2 (last ve2)
)
(list (- (* y1 z2) (* y2 z1))
(- (* z1 x2) (* z2 x1))
(- (* x1 y2) (* x2 y1))
)
)
纯LSP获取指定UCS到WCS的转换矩阵
| 。 | |
|
代码
2003年02月11日:
| |
QAFLAGS
利用AutoCAD几何计算器实现快速定位
AutoCAD中非图形特征的存取方法及应用
命令修饰符
命令修饰符:
一、自
命令行: 在定位点的提示下,输入from
基点:指定一个点用作基点
<偏移>:输入相对偏移
在 AutoCAD 定位点的提示下,输入 from,然后输入临时参照或基点(自该基
点指定偏移以定位下一点)。输入自该基点的偏移位置作为相对坐标,或使用直接
距离输入。
注意: 在命令中进行拖动时(如 MOVE 和 COPY)不能使用这个方法。通过键盘输
入或使用定点设备指定绝对坐标值,取消 FROM 命令。
二、直接距离输入
命令行: 在定位点的提示下,输入数字值
使用直接距离输入,可以相对于输入的最后一点快速指定一点。在任意 AutoCAD
点位置提示下,首先移动光标以指定方向,然后输入数字距离。
下例中直线的第二点将沿光标方向定位 5 个单位。输入的直接距离将沿从最后一
点到光标当前位置的路径进行测量。此功能通常在“正交”或“捕捉”模式打开的状态
下使用。
命令:line
指定第一点: 指定点
指定下一点: 将光标移到需要的方向并输入 5
三、座标过虑器
命令行: 输入 .x、.y、.xy、.xz、 或 .yz
在任意 AutoCAD 定位点的提示下,可以输入点过滤器以通过提取几个点的 X、Y
和 Z 值来指定单个坐标。在下例中,直线的起点具有从第一个选定对象的中点的 X
值以及第二个选定对象的中点的 Y 和 Z 值构造的坐标。
命令:line
指定第一点: .x
于 mid
选择对象
(需要 YZ)mid
选择另一个对象
到点: 指定点
四、追踪
命令行: 在定位点的提示下,输入 tracking
追踪可打开“正交”模式并可在“捕捉”模式下使用。
在任何 AutoCAD 定位点的提示下,输入 tracking、track 或 tk。
第一个追踪点:指定一个位置或距离
下一点(按 ENTER 键结束追踪):指定第二个位置或距离
追踪可指定一系列临时点,每个点均自上一点偏移。因此,可从一系列方向和
距离指定新的点位置。要确定每个临时点的位置,可使用直接距离输入。首先移动
光标以指定方向,然后输入数字距离。
另外,追踪可通过结合两个指定点的 X 和 Y 值来建立新点。可以根据在第一
个点之后的光标方向,以任意次序指定这两个点。直接距离输入沿光标当前位置的
方向指定距离。坐标过滤器 将不同点的 X、Y 和 Z 值结合为单个点。相对坐标输
入相对于最后输入的点定位点。
也可以通过按住 SHIFT 键并单击鼠标右键以显示对象捕捉快捷菜单来访问追踪。
DIM 组码分析
在Dimension
的DXF组码中一些特性不能直接得到,下面的程序是为了获取字体高度,测试中觉得有意思,发上来给初学Lisp者在编程方面作为解决问题的一种思考方式。
第一个是ActiveX方法,是为了检验能否获取Dim的子实体,在块内实体以及选择集遍历方面,VLa方法更简单易用,第二个是在ActiveX方法测
试成功后用纯Lisp
方式来实现,在测试中也看到了Dimension内部各个子实体的规律,先是线然后是两侧的斜,接着写文字,最后是几个Point。 说明:测试仅检验了简单的垂直标注,不含公差等等其他形式,以上测试仅提供一种方法 2004年02月18日 | |
|
|
统一修改块名
(mapcar '(lambda (b1 b2)
(if (tblsearch "block" b1)
(command ".rename" "b" b1 b2)
)
)
(list . . . .);_ old block name list
(list . . . .);_ new block name list
)
[日积月累]:CAD2006下的Hatch合并、重建边界、统计面积编程
;|
2006对Hatch的支持使得以前的很多程序得以简化,成功率也有了很大的提高,一切都源于
Hatch 命令的增强。
[非关联(DI)/样式(S)/特性(P)/绘图顺序(DR)/添加边界(AD)/删除边界(R)/重新创建边界(B
)/关联(AS)/独立图案(H)/原点(O)] <特性>:
上面是基于命令行模式的各个选项,对话框模式的编辑和填充命令实际是一个,包括了所有的
填充选项。
下面的程序就是用Hatchedit实现批处理,其中的 getkword 用法和以前的版本也略有变化
eachy [www.xdcad.net] Email: eachy@21cn.com 2005.5.9
|;
;;(if (>= (atof (getvar "acadver")) 16.2)
(defun c:tt (/ ss e kw ssl _$area)
(vl-load-com)
(if (setq ss (ssget '((0 . "Hatch"))))
(progn
(setvar "cmdecho" 0)
(setq ssl (sslength ss))
(if (= ssl 1)
(progn
(initget "B H O A S")
(setq kw
(getkword
"\n模式选择\n[重建边界(B)/独立图案(H)/原点(O)/统计面积(A)/实心填充(S)]: "
)
)
)
(progn
(initget "B H A S")
(setq kw
(getkword
"\n模式选择\n[重建边界(B)/独立图案(H)/统计面积(A)/实心填充(S)]: "
)
)
)
)
(cond
((= kw "B") ;_重建边界
(while (> ssl 0)
(setq e (ssname ss (setq ssl (1- ssl))))
(if (= (cdr (assoc 450 el)) 1)
(progn
(initdia)
(command "hatchedit" e)
)
(command ".-hatchedit" e "B" "p" "y")
)
)
)
((= kw "H") ;_ 分解为独立填充
(while (> ssl 0)
(setq e (ssname ss (setq ssl (1- ssl))))
(command ".-hatchedit" e "h")
)
)
((= kw "O") ;_指定原点
(vl-cmdf ".-hatchedit"
(ssname ss 0)
"o"
"s"
(getpoint "\n新原点: ")
"y"
)
)
((= kw "S") ;_ 变实心填充
(while (> ssl 0)
(setq e (ssname ss (setq ssl (1- ssl))))
(command ".-hatchedit" e "p" "s")
)
)
(t ;_求面积
(setq _$area 0.)
(while (> ssl 0)
(setq e (ssname ss (setq ssl (1- ssl))))
(setq _$area
(+ _$area
(vla-get-area (vlax-ename->vla-object e))
)
)
)
(princ "\n填充总面积: ")
(princ _$area)
)
)
)
)
(princ)
)
;;(princ "\n仅适用AutoCAD 2006以上版本!")
;;)
;;对填充合并可以先利用生成Region边界,然后用Hatch命令重新填充,有兴趣的可以自己
;;添加这部分。
今天处理一个地形图,写了个标高块转字的程序
(defun c:tt (/ ss ssl e att obj ms)
(if (setq ss (ssget '((66 . 1))))
(progn
(setq ssl (sslength ss)
ms (vla-get-modelspace
(vla-get-activedocument (vlax-get-acad-object))
)
)
(while (> ssl 0)
(setq e (ssname ss (setq ssl (1- ssl))))
(setq att (car (vlax-invoke
(setq obj (vlax-ename->vla-object e))
'getattributes
)
)
)
(vla-addtext
ms
(vla-get-textstring att)
(vla-get-insertionpoint obj)
(vla-get-height att)
)
)
(command ".erase" ss "")
)
)
(princ)
)
清理图形的程序
;;使用时可以用 vl-load-all 加载,格式如
;; (vl-load-all "c:\\cleardwg.vlx") 2005年06月22日
(vl-load-com)
(defun c:cleardwg (/ thisdrawing ss lyrlst olderr myerr)
(defun myerr (msg)
(if (/= msg "取消")
(princ "\*取消*")
)
(vla-endundomark thisdrawing)
(setq *error* olderr
myerr nil
)
(princ)
)
(setvar "cmdecho" 0)
(setq thisdrawing
(vlax-get-property
(vlax-get-acad-object)
'activedocument
)
)
(vla-startundomark thisdrawing)
(setq olderr *error*
*error* myerr
)
(vlax-map-collection
(vlax-get-property thisdrawing 'layers)
'(lambda (lyr / name s tf)
(setq name (vla-get-name lyr))
(if (or (setq tf (= (vla-get-freeze lyr) :vlax-true))
(= (vla-get-layeron lyr) :vlax-false)
)
(progn
(if (= (vla-get-lock lyr) :vlax-true)
(vla-put-lock lyr :vlax-false)
)
(if tf
(setq lyrlst (cons name lyrlst))
)
(vla-put-freeze lyr :vlax-false)
(if (setq s (ssget "X" (list (cons 8 name))))
(vl-cmdf ".erase" s "")
)
)
)
)
)
;|(if lyrlst
(vlax-map-collection
(vlax-get-property thisdrawing 'blocks)
'(lambda (b / bn)
(setq bn (vla-get-name b))
(if (and (/= bn "*MODEL_SPACE")
(/= bn "*PAPER_SPACE")
(not (wcmatch bn "`*PAPER_SPACE?"))
)
(vlax-map-collection
b
'(lambda (x)
(if (vl-position (vla-get-layer x) lyrlst)
(vla-delete x)
)
)
)
)
)
)
)|;
(if (setq ss (ssget "x" '((2 . "*网易*,*土木在线*,*园林站图块*"))))
(vl-cmdf ".erase" ss "")
)
(vla-purgeall thisdrawing)
(vla-purgeall thisdrawing)
(vla-purgeall thisdrawing)
(vla-zoomextents (vla-get-application thisdrawing))
(vla-save thisdrawing)
(vla-endundomark thisdrawing)
(setq *error* olderr
myerr nil
)
(princ)
)
(princ "\n清理图层, 命令: ClearDwg, eachy[www.xdcad.net]")
(princ)