明经通道 AutoLISP 函数   
trans
 

将一个点(或位移量)从一个坐标系转换成另一个坐标系

(trans pt from to [disp])   

参数

pt

三维实数列表,既可以被解释成一个三维点,又可以被解释成一个三维位移(矢量)。

from

整数代码、图元名或三维拉伸矢量,指定 pt 的坐标系。整数代码可以为如下值:

0 世界坐标系(WCS)

1 用户坐标系(当前 UCS)

2 与代码 0 或代码 1 一起使用时,表示当前视口的显示坐标系 (DCS)。与代码 3 一起使用时,表示当前模型空间视口的 DCS。

3 图纸空间 DCS(仅用于代码 2)

to

整数代码、图元名或三维拉伸矢量,指定返回点的坐标系。有效的整数代码取值请参见 from 参数。

disp

如果存在且不为 nil,则将 pt 作为三维位移而不是三维点看待。

如果 from 或 to 参数是图元名,它必须是由 entnext、entlast、entsel、nentsel 和 ssname 等函数返回的图元名。这样就可以将某个特定对象的对象坐标系 (OCS) 中的点来回进行转换(对于某些对象,OCS 等价于 WCS,对这些对象,OCS 和 WCS 之间的转换是空操作)。使用三维拉伸矢量(三个实数组成的一个表)是来回转换对象 OCS 的另一种方法。但是,对于 OCS 等价于 WCS 的那些对象,这种转换也不做任何操作。

返回值

由参数 to 指定的坐标系表示的一个三维点(或位移)。

示例

在下面的样例中,UCS 绕世界坐标系的 Z 轴旋转 90 度:

命令: (trans '(1.0 2.0 3.0) 0 1)

(2.0 -1.0 3.0)

命令: (trans '(1.0 2.0 3.0) 1 0)

(-2.0 1.0 3.0)

关于坐标系的详细信息,请参见 Visual LISP 开发人员手册中的坐标系转换。

例如,要在不使用对象捕捉的情况下,从一行文本的插入点画一条直线,需要将文本对象的插入点从文本对象的 OCS 转换到 UCS:

(trans text-insert-pointtext-ename 1)

然后就可以将结果传给“起点:”提示。

相反地,将点值送到 entmod 函数中去之前,必须将该点(或位移)值转换成用该对象的 OCS 表示。例如,如果要将圆相对 UCS 偏移 (1,2,3)(不使用 MOVE 命令),就需要将该偏移量从 UCS 转换成圆的 OCS:

(trans '(1 2 3) 1 circle-ename)

然后就可以将结果偏移量加到圆的圆心上去。

例如,如果用户输入了一个点,且想要找出一条直线的哪一个端点看起来离该点更近,就先得将用户输入的点从 UCS 转换到 DCS:

(trans user-point 1 2)

然后再将直线的每一个端点从 OCS 转换到 DCS:

(trans endpoint line-ename 2)

这样就可以计算出用户输入的点与直线每一个端点间的距离(忽略 Z 坐标),从而确定哪一个端点看起来距用户输入的点更近。

trans 函数也可以转换二维点,这需要通过给 Z 坐标赋一个适当的值来实现。所使用的 Z 分量取决于所指定的 from 坐标系,以及该值是作为一个点还是作为一个位移。如果是作为一个位移,那么其 Z 值总是为 0.0;如果是作为一个点,那么其 Z 值由下表确定。

转换二维点时的 Z 坐标值

From

填入的 Z 坐标值

WCS

0.0

UCS

当前标高

OCS

0.0

DCS

投影到当前构造平面(UCS XY 平面 + 当前标高)

PSDCS

投影到当前构造平面(UCS XY 平面 + 当前标高)

Highflybird讲解trans

让我们来看看OCS.

一些平面物体有它们自己的物体坐标系(OCS),譬如轻多段线,块参照,文本等等,另外多段线还有一个标高数据。

举例说:一个UCS(用户坐标系,图中绿色表示)它的原点在WCS(世界坐标系,图中白色表示)的坐标是(1.402 0.347 4.866),并且三个轴被旋转了。在UCS下插入一个图块(图中橙色部分)到点(0 0 0),旋转角度是0.

因而,所有的OCS的原点跟WCS的原点是相同的。

并且所有具有相同的拉伸方向(DXF组码的210代码值 或者Normal属性)的物体都具有相同的OCS.
为了计算一个三点确定的平面的法线矢量,你可以用Norm_3pts这个程序,参考下面链接:

http://www.theswamp.org/index.php?topic=11561.0

我认为,如果知道了这些,那去理解trans 函数在三维上的用法,就容易多了,对一些代码也很有用。

下面举例:

去获得块参照的插入点在WCS的坐标,一个通常的作法是用图元名:

[code=lisp] (trans (cdr (assoc 10 (entget ent))) ent 0) [/code] -> (1.402 0.347 4.866) ,这点正是UCS的原点在WCS的坐标,也是图块插入点在世界坐标系的坐标。
同样我们也可获得插入点在UCS下的坐标:

[code=lisp] (trans (cdr (assoc 10 (entget ent))) ent 1) [/code]返回 (4.440e-016 4.440e-016 8.881e-016)很接近(0.0 0.0 0.0)--译者注:这个是由于运算过程中的浮点误差产生的。

 

它也可以用210 DXF组码来实现:
[code=lisp] (trans (cdr (assoc 10 (entget ent))) (cdr (assoc 210 (entget ent))) 0) [/code]

entmake …或者vla-add UCS平面上创建一个二维物体,必须要用到另外一个方法,因为这时候实体还不存在。所以我们必须计算UCS XY平面的法线矢量。

[code=lisp] (setq ucszdir (trans '(0 0 1) 1 0 T)) [/code]

在上述trans表达式中,UCSZdir (UCS z方向) ent 或者 (cdr (assoc 210 (entget ent)))

一样的用法。--译者注:注意此处的 (trans .T) T
[code=lisp] (cons 210 ucszdir) [/code]

Ucszdir 同样也能通过(vla-put-Normal ...)去赋予一个物体的法线属性。

 

现在看来我们理解了坐标系统,但如何理解旋转角呢?

来获得OCSX轴线跟UCS X的轴线的夹角:
[code=lisp] (angle '(0 0 0) (trans (getvar "UCSXDIR") 0 ucszdir)) [/code]-> 0.39099

便是块参照的DXF数据中的旋转角。

为完成它,需要获取轻多段线的标高(类似块参照,文本的Z坐标等)
entmake 或者vla-add…去创建一个在UCS平面的轻多段线,我们必须为其指定标高,
[code=lisp] (caddr (trans '(0 0 0) 1 ucszdir)) [/code] -> 4.519作为块参照插入点的Z坐标(10 DXF组码)


至此,我希望此贴对你们有所帮助和借鉴。对我来说,用英语解说非常困难,如果你懂法语,你可以在如下链接找到这个主题的更多信息。

*编辑:我最初写的是“要求”  是弄混了这两者的含义。(要求和请求的区别)

 

--译者注:这两个链接已经失效。实际作者的英语是相当棒,比我强多了。所以本文如有翻译理解错误的请大家指正。

                         CAD的坐标系统

 

看完了此文,不知读者有何感受。如果你喜欢,请跟我继续深层次地理解CAD的坐标系统和trans的工作原理,以及它的一些有趣的用法。

 

CAD的坐标系统是什么,有哪些坐标系统,你如果到网上去搜索,或者百度的话,你将会得到一些错误的解答。

 

首先我们来看CAD的最常用的坐标系统:

一、世界坐标系 -- WCStrans的代码为0.

世界坐标系即CAD内部处理时使用的三维坐标系 ,它为右手型,如下图所示:

x

z

y

0


它的定义的原点在左下角,

从计算机屏幕的角度来看,x轴正方向为屏幕从左向右,y轴正方向为屏幕从下向上,z轴正方向为屏幕从里向外。而进行旋转操作时需要指定的角度θ的方向则由右手法则来决定,即右手握拳,大拇指直向某个坐标轴的正方向,那么其余四指指向的方向即为该坐标轴上的θ角的正方向(即θ角增加的方向),在上图中用圆弧形箭头标出。

 

二、计算机中关于屏幕坐标系是一个二维的坐标系,有下图的两种,但是我们最常用的是左边的那种:原点在屏幕的最左上角,X正方向在是由原点从左至右,Y正方向是从上至下边。

理解这个,有助于你的DCL编程时候的对图像和图像按钮控件的设计。

 

三、用户坐标系 -- UCStrans的代码为1.

顾名思义,用户是自己定义的坐标系,可以使用以下任意方法和其组合定义用户坐标系:

1.  通过定义新的坐标原点。

2.  三点法得到UCS

3.  与现有对象或者当前观察方向对齐。

4.  绕当前X轴,Y轴线,或者Z轴或者任意轴旋转得到 UCS

5.  恢复保存的 UCS

等等。它也是右手型的坐标系统。设置好UCS后,当你打开了正交时,正交方向也平齐UCS

如果当下是UCS时,可以通过系统变量UCSXDIR, UCSYDIR, UCSORG分别获得其X方向,Y方向和坐标原点在WCS的位置。

关于如何获得其变换矩阵可以参考我的帖子:

http://bbs.mjtd.com/thread-99926-1-1.html

WORLDUCS 系统变量指示当前UCS是否跟WCS 完全相同。如果为0则不同,否则相同。

 

四、对象坐标系OCStrans 的代码是图元名。

上面我已经谈了一些,再抄些来自CAD的开发帮助文件的段落:

 

要节省图形数据库(和 DXF 文件)的空间,可以按照图元自己的对象坐标系 (OCS) 来表示每个图元的相关点。在 OCS 中,描述图元在三维空间中的位置所需的唯一附加信息是描述 OCS Z 轴和标高值的三维矢量。

对于给定的 Z 轴(或拉伸)方向,有无限个坐标系。这些坐标系是通过在三维空间中转换原点并围绕 Z 轴旋转 X Y 轴而定义的。但对于同一个 Z 轴方向,只有一个 OCS。其特性如下:

1.  它的原点与 WCS 原点重合。

2.  XY 平面中的 X Y 轴的方向以任意但一致的方式计算。AutoCAD 使用任意轴算法(参见任意轴算法)执行此计算。

对于某些图元,OCS 等同于 WCS,所有点(DXF 10-37)都用世界坐标表示。参见下表。

 

 

 

 

 

与图元类型关联的坐标系

图元

注意

三维图元,如直线、点、三维面、三维多段线、三维顶点、三维网格、三维网格顶点

这些图元不在特定平面上。所有点都用世界坐标表示。这些图元中,只有直线和点可以拉伸。它们的拉伸方向可以不同于世界 Z

二维图元,如圆、圆弧、实面、宽线、文字、属性、属性定义、形、插入、二维多段线、二维顶点、优化多段线、图案填充、图像

这些图元本质上是平面。所有点都用对象坐标表示。这些图元可以拉伸。它们的拉伸方向可以不同于世界 Z

标注

一些标注点用 WCS 表示,一些用 OCS 表示

视口

用世界坐标表示

 

AutoCAD 为给定图元建立 OCS 后,OCS 将按如下方式工作:与图元一起存储的标高值表示沿 Z 轴移动 XY 平面(从 WCS 原点)多少距离可以使其和包含图元的平面重合。用户定义的标高值并不重要。

通过 UCS 输入的任何二维点将转换为相对于 UCS 移动和旋转的 OCS 中相应的二维点。

以下是该过程的几个结果:

1.  获得图元时,不一定会找到所用的 UCS

2.  在给定的 UCS 中输入图元的 XY 坐标并执行 SAVEAS 后,可能无法识别 DXF 文件中的 XY 坐标。必须知道 AutoCAD 计算 X Y 轴的方法才能使用这些值。

3.  DXF 文件的图元和输出一起存储的标高值是 UCS XY 平面和 OCS XY 平面之间 Z 坐标差别和绘制图元时用户指定的标高值总和。

 

五、显示坐标系DCS trans代码 2.

这个坐标系,是指当前视口的显示坐标系统,它是相当于把世界坐标系或者用户坐标系的投影到屏幕上,譬如,如果我们采用了轴测图观察图形,这时候把世界坐标或者用户坐标投影

到屏幕,我们就需要用到显示坐标系,它是由以下方面决定的:

1、      自观察点和原点与世界坐标系的X的夹角。

2、      自观察点和原点与世界坐标系的XY平面的夹角。

同样,你也可以从我这个帖子http://bbs.mjtd.com/thread-99926-1-1.html里面的关于轴测图的变换矩阵了解到其坐标系统。

 

坐标系统讲完了,进入下一个话题。

 

Trans函数的工作原理

 

Trans函数,对许多人来说,是一个比较难以理解的函数。

一言以蔽之,trans函数,就相当于矩阵变换,它把一个矢量或者一个点从一个坐标系统变换到另外一个坐标系统。

如果阅读了我前面写的,估计现在理解trans函数容易多了。

示例:

在下面的样例中,UCS 绕世界坐标系的 Z 轴旋转 90 度:

 

[code=lisp](trans '(1.0 2.0 3.0) 0 1) [/code]

(2.0 -1.0 3.0)   意思就是:WCS的坐标(1.0 2.0 3.0),在UCS中是(2.0 -1.0 3.0)

[code=lisp] (trans '(1.0 2.0 3.0) 1 0) [/code]

(-2.0 1.0 3.0)   意思就是:UCS 的坐标(1.0 2.0 3.0),在WCS中是(-2.0 1.0 3.0)

 

剩下的,基本就可以理解了。

如何区别点跟矢量,

[code] (trans pt from to [disp]) [/code]disp如果存在且不为 nil,则将 pt 作为三维位移而不是三维点看待。这就是他们的区别。

譬如[code=lisp](trans '(1.0 2.0 3.0) 0 1 T) [/code]则将’(1.0 2.0 3.0)当做一个矢量,即三维位移看待,而不是三维点看待。

一些情况下与不加这个参数,它们的结果是相同的,而一些情况下则是不同的。

至于其它情况:明经通道翻译的那本Autolisp函数参考中trans的翻译说明已经够详细。

如果不懂,请单独提出。

 

Trans函数中如果第二个参数或者第三个参数是一个三维点或者矢量,该怎么理解其变换呢?例如[code=lisp] (trans ‘(1 2 3) 0 ‘(2 4 3)) [/code],将得到怎样的结果?

 

    首先抄上开发帮助中“任意轴算法”的章节:

AutoCAD 内部使用任意轴算法为所有使用对象坐标的图元生成任意但一致的对象坐标系。

假定一个作为坐标系的 Z 轴的单位长度矢量,任意轴算法将为坐标系生成相应的 X 轴。Y 轴可以通过应用右手定则来确定。

可以使用该方法检查给定的 Z 轴(也称为普通矢量)。如果它距离正向或负向世界 Z 轴很近,可以用给定的 Z 轴跨越世界 Y 轴到达任意 X 轴。如果不是很近,可以用给定的 Z 轴跨越世界 Z 轴到达任意 X 轴。所选的边界应该既便于计算又能在不同计算机上使用。为此,可以通过安装一种方形环形封口来实现,环形封口的边界是 1/64,是用六位十进制分数和六位二进制分数精确指定的。

算法如下(假定所有矢量都在三维空间中并在世界坐标系中指定):

假定普通矢量为 N

假定世界 Y 轴为 Wy,它总是位于点 (0,1,0)

假定世界 Z 轴为 Wz,它总是位于点 (0,0,1)

现在我们寻找任意 X Y 轴以便与普通 N 匹配。它们将被称为 Ax AyN 也可以按如下方式称为 Az(任意 Z 轴):

如果 (abs (Nx) < 1/64) (abs (Ny) < 1/64)   

     Ax = Wy X N  (其中“X”是叉积运算符)。

否则,

     Ax = Wz X N

Ax 缩放到单位长度。

获得 Ay 矢量的方法如下:

Ay = N X Ax   Ay 缩放到单位长度。

 

由此可见,如果第二个或者第三个参数传递的是一个点或者矢量,那么CAD统一把这个参数当做一个矢量来看待,就像一个物体的拉伸方向一样。然后通过它的变换矩阵来转化。 

下面我将用Gile的程序结合上面的段落来理解这个用法.

[code=lisp]

;;OCS的变换矩阵,或叫法线矢量的变换矩阵

(defun OcsMatrix (zdir / xdir)

  (or (equal 1.0 (distance '(0 0 0) zdir) 1e-8)

      (setq zdir (Normalize zdir))       ; 先把矢量单位化。

  )

  (if (and (< (abs (car zdir)) 0.015625)    ; 如果(abs (Nx) < 1/64)

          (< (abs (cadr zdir)) 0.015625)    ; (abs (Ny) < 1/64)

      )

    (setq xdir (Normalize (CrossProduct '(0 1 0) zdir)))    ; Ax = Wy X N   (叉积)

    (setq xdir (Normalize (CrossProduct '(0 0 1) zdir)))    ;否则 Ax = Wz X N

  )

  (list xdir (Normalize (CrossProduct zdir xdir)) zdir)      ;Y方向满足右手型坐标系统

)

 

;; CrossProduct

;; Returns the cross product (vector) of two vectors

;; Arguments : two vectors

;; 两个矢量的叉积,参数两个矢量,返回值一个矢量.

(defun CrossProduct (v1 v2)

  (list   (- (* (cadr v1) (caddr v2)) (* (caddr v1) (cadr v2)))

        (- (* (caddr v1) (car v2)) (* (car v1) (caddr v2)))

        (- (* (car v1) (cadr v2)) (* (cadr v1) (car v2)))

  )

)

 

;; Normalize

;; Returns the single unit vector

;; Argument : a vector

;; 把一个矢量单位化.

(defun Normalize (v)

  ((lambda (l)

     (if (/= 0 l)

       (mapcar (function (lambda (x) (/ x l))) v)

     )

   )

    (distance '(0 0 0) v)

  )

)

;; Apply a transformation matrix to a vector by Vladimir Nesterovsky

(defun mxv (m v)

  (mapcar (function (lambda (r) (apply '+ (mapcar '* r v)))) m)

)

[/code]

 

我们来做下试验:

[code=lisp] (mxv (OcsMatrix '(-1 2 1)) '(2 -3 1)) [/code] ->(-0.447214 2.37346 -2.85774)

[code=lisp] (trans '(2 -3 1) 0 '(-1 2 1)) [/code]  ->(-0.447214 2.37346 -2.85774)

可见我们用程序跟trans 函数得到了相同的结果。

这个就是trans函数的工作原理。

 

下面提及一下trans的一个妙用:

如下图:请求出点Pt (5 2) 到直线A B的距离。A点坐标(1 1),B点坐标(3 5)

[code=lisp] (car (trans (mapcar '- '(5 2) '(1 1)) 0 (mapcar '- '(3 5) '(1 1)))) [/code]

得出结果-3.1305 .负数表明这点在直线方向的下面。

是不是很简单?一句LISP搞定。

如何去理解呢,如果把AB轴方向矢量当成一个物体的法线矢量,那么,PT在这个坐标系统的投影,其X值就是距离。

明经通道 版权所有 未经许可 不得传播

 评论