最 近在学习用ObjectAXR 2002做AutoCAD二次开发,总体感觉还好,主要难点在于理解AutoCAD的数据组织方式。AutoCAD的数据是采用数据库形式进行存储和管理 的,表格,记录,游标等概念,在数据库中都已经接触过;图层,图块等和GIS中的概念基本一致。在有了这两方面的基础之后,加上在学校的时候,自己对 AutoCAD有过一些了解,用AutoCAD画过一些图,对AutoCAD的命令有一定了解,因此学习起来基本上是得心应手了。带我的老师原来为我准备 的两个星期的内容一上午就搞定了。经过差不多两个星期的学习,在看了基本几本入门书之后,觉得几乎都了解的差不多了。
只是,到最后在研究三维图形的时候,遇到了麻烦。
二 维的时候,无非就是那一套,基本类型(AcGePoint3d,AcGeLine等)组成实体(Entity),再是块表记录,块表,图层记录,图层表, 然后是数据库,熟了差不多一目了然。到三维情况就发生了变化。原来以为不过是直接在二维的基础上再进行深挖,哪知AutoCAD竟然采用了另外一套完全不 同的东西:ACIS系统--另外一家公司的三维模型,然后把数据都封装起来了。这样的话,要访问三维数据得先和ACIS打交道,这就是AcBr库。原来在 二维环境下那套思路,完全用不上(其实也不尽然,用选择集或acedEndSel来选择三维实体,然后用一个massProp的函数可以得到一些基本信息 的,比如体积,质心,旋转轴等,但是无法得到组成solid的元数据,比如长方体的几个顶点坐标等,而这些才是关键的数据)。而关于这个AcBr库的文 档,就只有ObjectAXR附带文档中的那一章了,网上也找不到什么有用的资料。而那个文档太难啃了,很多拓扑学和几何学上面的专业词汇难以解释,那些 概念原理也都说的模棱两可,而且不知出于什么目的,文档写的极其简洁,很多地方我都觉得有些简单的过分(作为一份官方文档,这实在是不应该)。因此这么一 份全英文文档,对我来说无异于天书,啃了几天都没什么进展,最后一怒之下,决定自己动手把他翻译过来,于是连续两天放下手头的活,硬着头皮把它翻译成中文 了。现在把它放到这里吧,也许以后用得着。
说明:由于水平有限,加之英文不过关,很多地方都没理解透,因此不保证翻译的准确性。
AcBr库说明文档(基于ObjectARX2002)
本节展示如何使用AcBr库(acbr15.dll)来访问拓扑对象,几何对象,还有分析AutoCAD® 实体(entities)如solids,bodies和regions(也就是AcDb3dSolid,AcDbBody,和 AcDbRegion类的对象)所包含的数据,以及种种派生类型(例如,AcDbPart,AcAsSurface类对象,还有兼容的客户端定义的类型compatible client-defined types)。
AcBr库可以在如下AutoCAD实体中使用:
² AcDb3dSolid:代表实体,封装了一个或多个体积
² AcDbRegion:代表平坦表面(planar surface),可能包含多个共面的表面(multiple coplanar surfaces)
² AcDbBody:是所有不被AcDb3dSolid或AcDbRegion所覆盖的边界表示法(boundary representation)对象的具体基类,包括Autodesk Mechanical Desktop和他的客户程序所定义的继承的类型。
² AcDbPart:represents a solid or sheet body in the context of an assembly or feature in Autodesk Mechanical Desktop
² AcAsSurface:表示Autodesk Mechanical Desktop里作为切片体(sheet body)的单一表面(single surface)。
AcBr库提供对AutoCAD实体(solids)里的建模数据(modeling data)的子集(subset)的只读访问。不要求这些实体是数据库活动的(database active),可以由如下方式创建:
² AutoCAD对象创建命令 (例如SPHERE),或者等价的AutoLISP脚本。
² Autodesk Mechanical Desktop对象创建命令(例如ADREVOLVE),或者等价的AutoLISP脚本。
² Invocation of the AutoCAD EXPLODE command on a part or assembly in Autodesk Mechanical Desktop.
² 使用文件导入功能,例如:OPEN, DXFIN, ACISIN, ADSATIN, VDAFSIN, STEPIN, AMIDFIN, or IGESIN
² 节目实例化(Programmatic instantiation),简单的使用AcDb3dSolid::createFrustum(), AcDb3dSolid::createBox(), AcDb3dSolid::createWedge(), AcDb3dSolid::createSphere(), AcDb3dSolid::createTorus(), AcDbRegion::createFromCurves().
AcBr的典型用法如下:
² 传送实体或子实体数据到你的程序里进行显示,分析或维护。
² 查找实体里感兴趣部分的详细信息并且查询相关数据,比如几何结构。
² 传送实体数据到其他的建模系统中(这就是数据交换)。
² 网格化实体的表面(surface)数据进行显示,分析或维护。
² 支撑分析(比如点和线的包含关系,界定块,以及聚合属性mass properties)。
AutoCAD的实体(solids)是用边界进行表示的(通常被成为B-rep模型),由一组拓扑连通对象和相关联的几何边界对象组成。拓扑对象由AcBr库定义,在本节稍后进行介绍,而几何对象则由AcGe库定义。
AcBr库定义或产生的对象驻留在(reside)三维欧几里德模型空间(E3)。唯一的例外是定义在二维参数空间表面(surface)的几何对象(例如参数曲线parameter curves 和参数点parameter points)。
通常,AcBr库只支持二重拓扑域(2-manifold topological domain)。支持单一顶点(Singularities,即几何上的退化。译者注:应该是表示某中特殊的点,实在找不到合适的名词来对应,只有用“单一顶点”来表示,可能不太准确。)的是为了表示圆锥的顶点,但是有线形体(body)和混合维度实体(solid,可能包含悬挂线和面)不被支持,他们也无法被AutoCAD识别。
一般的非多重域(nonmanifold domain)是二重拓扑域的超集(superset),允许不同的实体体积(solid volumes)接触单一的点,曲线和面;也允许再构造,切片和实体对象(wireframe, sheet, and solid objects)的任何组合。AutoCAD和AcBr库支持下面的非多重域对象:
² 两个二重域实体通过共享的边或顶点联合
² 包含单个面的AcDbBody对象
只有在如下情况中拓扑对象允许没有边界界定(unbounded,也就是没有更低维度的边界拓扑no lower dimensional bounding topology):
² 封闭表面,在u和v方向上固有界定(intrinsically bounded)的(例如完全环或球full torus or sphere),由不包含循环边界的表面所表示。
² 封闭曲线,固有界定(intrinsically bounded)的(例如完全圆或椭圆full circle or ellipse),由拥起点和终点相同的边表示。
某些操作不支持非一致缩放(nonuniform scaling)。包括所有返回扩展(external)曲线或表面(包括NURBS表面)的函数。
在AcBr对象的子实体路径被设定后,整个子实体路径变换(transforms)的链(chain)被缓存起来(从效率来考虑)。如果一个块引用被删除,它将会被指向一个新的变换矩阵,但AcBr对象不会知道它缓存了一个已经过时了的变换。如果一个插入(insert)被改为引用另一个AutoCAD对象,子实体路径完全不再适用,因此在被用来重新初始化所有相关AcBr对象之前应该被更新来反映这个新的实体引用。
在AutoCAD中单一点(例如圆锥顶点)映射到边,并且可以被用来初始化专门查询顶点的AcBREdge,但是不能查询曲线结构(geometry)或用来设置AcBrLoopEdgeTraverser。用AcBrLoopVertexTraverser也可以访问它们,作为面(face)的单循环边界(single loop boundary)的单一点的对应。
AcBr库的类继承是ObjectARX类继承的一个子集,它定义了如下类:
注意:AcBR对象不是从AcDbObject继承的,因此不能用AutoCAD数据库注册。
拓扑对象是主要的或者次要的,取决于它们是否被绑定到一个指定的拓扑维度上。
主拓扑对象被用来完全覆盖评价模型空间(evaluated model space completely)。它们由点集合定义,被n-simplexes引用,n是它们的拓扑维度。0-simplexes是一个顶点,1-simplexes是一条边,2-simplexes是一个面(face),3-simplexes是组合体(complex)。它们不包括边界,但是它们可以被任意低维度的simplexes所界定。
主拓扑对象有:
² Complex:在R3 in E3 里连接的拓扑的三维区域的点(points)。是由顶点(vertices),面(face),边组成的体积。Complex通常由一个或多个shell界定。
² Face:在R2 in E3 里连接的拓扑的二维区域的点(points)。是complex上一个外壳边界(shell boundary)的一个表面(surface)的被界定的,可定向子集。
² Edge:在R1 in E3 里连接的拓扑的一维区域的点(points)。是face上一个单循环边界上一个曲线(curve)的被界定的,可定向子集。
² Vertex:在R0 in E3 里连接的拓扑的0维度区域的点(points)。是面(face)上的单一点。一个顶点被它自己所界定。
所有这些主拓扑对象返回的结构可以被要求进一步使用Autodesk Geometry Library。
次级拓扑对象是主拓扑对象的连接的集合(connected collections),并且不需要被绑定到指定的拓扑维度上。它们表示从高维度simplex到定义它的边界的连接的部分的低维度集合的边界映射。每个主拓扑对象至少属于一个次级拓扑对象。
次级拓扑对象有:
² Brep:评价空间里所有事物的集合;也就是,一个唯一E3 里所有主要和其他相关次级拓扑对象的集合。集合里至少包含一个单一complex。
² Shell:界定complex的面(faces)的无序集合。 集合里至少包含一个单一面。There may be at most one exterior shell,and there must be an exterior shell for there to be interior shells (voids)。
² Loop:一个面的连接边界里的边和顶点的有序集合,可能由一个单个顶点(single vertex)(对单一顶点,例如圆锥的顶点)或顺序连接的边组成。There may be at most one exterior loop, and there must be an exterior loop for there to be interior loops (holes).
正确使用AcBr库涉及到几个内部和外部对象之间的交流:AcBrEntity, AcBrTraverser,以及它们的派生类:AcDbFullSubentityPath和它来自AcDb库的组成部分(AcDbObjectId, AcDbSubentId, and so on);还有来自AcGe库的几何对象(AcGeSurface, AcGeCurve3d, AcGePoint3d, and so on)。
在边界表示模型中,拓扑对象好比“胶水”,是用来把模型粘到一起的,几何数据是被拓扑对象引用的。要访问一个边界表示模型图形(shape)的细节,你可以横切拓扑对象(使用traverser)或者直接处理对拓扑对象感兴趣的部分(使用AcDbFullSubentPath来响应屏幕选取)。
创建AcBrBrep对象,并用set()进行初始化—使用已经提前用kNullSubentId 和AutoCAD object ID设置好的AcDbFullSubentPath。创建一个AcBrBrepFaceTraverser来获取对整个实体的面(face)的访问,并用setBrep()进行初始化。对于traverser的拓扑邻接表(反复调用next()函数)的每个位置,getFace()提供可以访问几何结构的AcBrFace。其他的traversers提供对低级别拓扑对象以及与它们相关结构的访问。
创建适当的AcBr对象(AcBrFace, AcBrEdge, or AcBrVertex),用set()进行初始化使用已经提前用kNullSubentId 和AutoCAD object ID设置好的AcDbFullSubentPath。
调用适当的已经初始化的AcBr对象的get*函数来创建相关的AcGeSurface, AcGeCurve3d, or AcGePoint3d。
创建一个AcBrMesh2dFilter,使用适当的AcBr对象和AcBrMesh2dControl对象,调用已经实例化的AcBrMesh2d对象的generate()来产生表面(surface)网格。该网格可以被横切来访问节点结构和其他数据。
横切器(Traversers)被用来遍历实体的拓扑结构。它们是全局的或低级的(global or hierarchical)。
全局横切(例如AcBrBrepFaceTraverser 和 AcBrBrepEdgeTraverser)提供对实体(complexes, shells, faces, edges, vertices)中所有拓扑对象的横切能力。
低级查找提供遍历实体中当前面积(local areas)的能力,上-下或者左-右。几个此类横切器,例如AcBrLoopEdgeTraverser 和 AcBrLoopVertexTraverser,也提供对同一个边或顶点的多重使用之间的区别的查询。
例如,一条边不包含它正被那个面(face)所引用的信息。因此,由这条边所定义的下层表面(underlying surface)的参数曲线数据,可以由AcBrLoopEdgeTraverser来查询。
面向下层(Downward hierarchical)的横切器提供的对组成给定拓扑对象(也就是横切器的拥有者)边界的拓扑邻接子实体的访问。
拓扑横切器表示连接到更高维度拓扑对象上下文(context)的拓扑对象的邻接表。
每个类型的横切器使用get*和set*函数来同时暴露它正用作上下文(context)的对象(也就是邻接表的拥有者)和它当前正指向的对象(也就是邻接表的位置)。
Class |
Objects |
AcBrBrepComplexTraverser |
AcBrBrep (owner) AcBrComplex (position) |
AcBrBrepShellTraverser |
AcBrBrep (owner) AcBrShell (position) |
AcBrBrepFaceTraverser |
AcBrBrep (owner) AcBrFace (position) |
AcBrBrepEdgeTraverser |
AcBrBrep (owner) AcBrEdge (position) |
AcBrBrepVertexTraverser |
AcBrBrep (owner) AcBrVertex (position) |
AcBrShellFaceTraverser |
AcBrShell (owner) AcBrFace (position) |
AcBrFaceLoopTraverser |
AcBrFace (owner) AcBrLoop (position) |
AcBrLoopEdgeTraverser |
AcBrLoop (owner) AcBrEdge (position) |
AcBrLoopVertexTraverser |
AcBrLoop (owner) AcBrVertex (position) |
AcBrVertexLoopTraverser |
AcBrVertex (owner) AcBrLoop (position) |
AcBrVertexEdgeTraverser |
AcBrVertex (owner) AcBrEdge (position) |
AcBrEdgeLoopTraverser |
AcBrEdge (owner) AcBrLoop (position) |
Ø Vertices for edges:一条边最多可以有两个顶点。这由AcBrEdge类的外部函数暴露,作为一个横切器,这么细琐的邻接可能有些浪费。
Ø Vertices for loops:一个环(loop)可以有很多顶点,但至少得有一个(在单个边的情况下,或单一顶点--即没有边结构,例如圆锥的顶点)。LoopVertex横切器同时覆盖面上(face)顶点边界(vertex boundaries)的通用链表和单一顶点。这可能比dumping环上的所有边要更经济,如果是仅仅对面边界(face boundary)的点结构(point geometry)感兴趣的话。
Ø EdgeLoop traversal:本类定义与对共享普通边的面上的射线分类(radial ordering)有关的函数。为了提供对边列表横切器(AcBrLoopEdgeTraverser)的最严密的连接(tightest coupling),面(face)由共享边上的环边界来表示。
setEdgeAndLoop()函数设置边的所有者和环的起始位置。setEdge() 函设置边的所有者和环的起始位置。环的位置不能从边来单独设置,因为射线横切器应该被紧紧的连接到face-contextual的边列表上(也就是AcBrLoopEdgeTraverser)。
Ø VertexLoop traversal:本类定义与共享一个普通顶点的面上的射线分类相关的函数。为了提供与边类表横切器(AcBrLoopEdgeTraverser)最严密的连接,面由共享顶点上的环边界表示。
setVertexAndLoop()函数设置顶点的所有者和环的起始位置。setVertex()函数设置顶点的所有者和环的起始位置。环的位置不能从顶点单独设置,因为射线横切器应该被严密的连接到face-contextual 顶点列表上(也就是AcBrLoopVertexTraverser)。
网格横切器表示连接到更高维度网格对象上下文上的网格对象的邻接表。
每个类型的横切器使用get*和set*函数同时暴露它正用作上下文的对象(也就是邻接表的所有者)和它当前正指向的对象。
Mesh traversers |
|
Class |
Objects |
AcBrMesh2dElement2dTraverser |
AcBrMesh2d (owner) AcBrElement2d (position) |
AcBrElement2dNodeTraverser |
AcBrElement2d (owner) AcBrNode (position) |
Ø MeshElement traversal:本类定义与2D元素相关的函数。It is used to seed a downward hierarchical traversal of a 2D mesh, or to traverse all of the unique 2D elements and nodes in a 2D mesh.
Ø ElementNode traversal:This class defines the functions that are pertinent to a node in the context of a 2D element. It is used to get access to node data and the geometry of the original surface, such as surface normals and UV parameter pairs. Nodes are used by more than one mesh element and may be associated with more than one surface since there is node sharing at the common boundaries of the original surfaces。
Acbr库由几个功能部件组成。所有调用AcBr对象的函数以引用方式传递它们。因此,在从横切器或环中调用getFace()函数前必须创建一个AcBrFace实例。
边界表示对象的典型构建方法是使用默认的AcBr构造函数,再使用set()函数或横切器和它的get*()函数来初始化。
所有AcBr类都支持拷贝构造函数;委托运算符(assignment operators); isEqualTo(), isNull(), 和 set(), get() ;以及其他函数和查询。
实体(Entity)类包括:
l AcBrEntity
l AcBrBrep
l AcBrComplex
l AcBrShell
l AcBrFace
l AcBrLoop
l AcBrEdge
l AcBrVertex
包容对象从不由用户直接创建。它们由从AcBrEntity派生的实体(entities)的包容查询(containment queries)返回。
AcBrHit类是一个包容类。
网格对象从不由用户自己创建,除了在ObjectARX Reference 中提到的以外。它们由网格横切器的查询返回。
网格类包括如下:
l AcBrMeshEntity
l AcBrMesh
l AcBrMesh2d
l AcBrElement
l AcBrElement2d
l AcBrNode
l AcBrMesh2dFilter
l AcBrMeshControl
l AcBrMesh2dControl
横切器对象的典型用法是由默认的AcBrTraverser*构造函数构建,使用set*()函数之一来初始化。注意:列表所有者必须在列表位置能独立设置之前设置,这是为了提供上下文。
所有从AcBrTraverser派生的类支持拷贝构造函数,委托运算符(assignment operators), isEqualTo(),和isNull(),以及通用的横切函数。
初始化函数被语义性的绑定到适合指定横切器的AcBr类型上(也就是,在派生类名字中包含的两种类型,如AcBrBrep 和 AcBrEdge 之于AcBrBrepEdgeTraverser).)。
所有的初始化函数重新设定next()和done()的标准。它们可以归入如下通用算法类型:
Ø 从其他横切器来的setListOwnerAndCurrentPosition,使用它的列表所有者作为当前位置,当前位置作为列表所有者(就是交换列表所有者和当前位置)。本算法仅在相关联的横切器之间进行映射时才可使用,例如AcBrLoopEdgeTraverser 和 AcBrEdgeLoopTraverser。
Ø 来自AcBr对象的setListOwnerAndCurrentPosition,使用它作为当前位置,它的所有者作为列表拥有者。本算法仅在列表所有者是确定的(unambiguous)情况下使用,比如在设置AcBrShellFaceTraverser 时面上的shell所有者。
Ø 从其他横切器来的setListOwner,使用当前位置作为列表所有者,默认当前位置为新邻接表的第一个位置。本算法仅在使用另来自下一个向上级别(next level up)的另一个向下级别横切器来设置向下级别横切器时可用(例如使用AcBrShellFaceTraverser来初始化 AcBrFaceLoopTraverser),或者使用来自下一个向下级别(next level down)的另一个向上级别横切器来设置向上级别横切器(例如,使用AcBrVertexEdgeTraverser来初始化一个 AcBrEdgeLoopTraverser)。
Ø 来自AcBr对象的setListOwner,使用它作为列表所有者,默认当前位置为新邻接表的第一个位置。所有横切器都可使用本算法。
Ø 来自Acbr对象的setCurrentPosition,在一个已经建立的列表里使用它作为当前位置。大多数横切器类型都可以使用本算法,但要求之前已经设置好列表所有者。
横切器类包括如下:
l AcBrTraverser
l AcBrBrepComplexTraverser
l AcBrBrepShellTraverser
l AcBrBrepFaceTraverser
l AcBrBrepEdgeTraverser
l AcBrBrepVertexTraverser
l AcBrComplexShellTraverser
l AcBrShellFaceTraverser
l AcBrFaceLoopTraverser
l AcBrLoopEdgeTraverser
l AcBrLoopVertexTraverser
l AcBrVertexLoopTraverser
l AcBrVertexEdgeTraverser
l AcBrEdgeLoopTraverser
l AcBrMesh2dElement2dTraverser
l AcBrElement2dNodeTraverser
AcBr结构体包含AcBr库来所独有的枚举类型,它们被用作返回代码或者本地类的函数的参数列表。在接下来部分将描述各种枚举域(fields)。
所有非boolean(non-Boolean)值的函数返回AcBr::ErrorStatus枚举类型。错误代码可能是全局AutoCAD错误代码(如Acad::ErrorStatus中所列),转换到局部AcBr::ErrorStatus枚举,或者局部边界表示库的错误代码。局部错误代码使用开始位置(starting base)3000来定义,因此不会与AutoCAD, AutoCAD Mechanical API,或者Autodesk Mechanical Desktop错误代码相冲突。
AcBr::ValidationLevel枚举设置AcBr对象的确认级别。如果指定了kFullValidation(默认对象实例化之上),所有访问brep拓扑(直接或间接)的函数首先检查向关联的AutoCAD数据库对象来确定从AcBr对象上次被设置后它是否被修改过。这是一个开销很大的操作,但是它保证所有brep数据都在范围(scope)之内。如果kNoValidation被指定,将不会检查数据库除非是完成函数任务所必须的。这样做更高效,但是不保证brep数据都在范围内。在对象到对象(object-by-object)间设置确认级别基本能预防同时载入的程序之间的冲突。
AcBr::ShellType枚举将外壳分为内部的(interior),外部的(exterior),等等。外部壳(Peripheral shells)被当作kShellExterior来返回,每个brep或region只有一个这样的外壳(由行业惯例决定)。Voids被当作kShellInterior返回,每个brep或complex可能有几个这样的(假设同样也有一个外部壳)。
AcBr::LoopType枚举将一个环分为内部的,外部的,等等。外部环(Peripheral loops)是kLoopExterior,而且每个面只有一个这样的环(由行业习惯决定)。洞(Holes)被当作kLoopInterior返回,每个面可能有好几个(假设同样也有一个外部环)。圆锥和圆柱(不管是椭圆还是圆形底部)至少有两个底部环(base loops)(如果它们在u和v方向是完整的),受只能有一个外部环的限制,它们被当作kLoopWinding返回而不是kLoopExterior。单一顶点(例如圆锥顶点)返回为kLoopUnclassified。球面(spheric)和圆环表面(toric surfaces)上的所有环,还有闭合周期的NURBS,返回为kLoopUnclassified。
AcBr::Element2dShape枚举控制由2d网格产生元素的图形。它只与网格过滤器(mesh filter)相关。一维网格(通过对原始面边界曲线进行离散化)可通过指定kAllPolygons来模拟。
为了进行连接,使用AcBr库的程序必须使库文件acbr15.dll可用。
更重要的是,库需要被用来确保在运行时正确的注册AcBr类和ObjectARX。
因此,让应用程序明确的载入acbr15.dll 就很重要了,如果它不是已经被模块或其他程序载入的话。做到这一点的最好方法是使用acrxDynamicLoader()和acrxClassDictionary()来载入并检查acbr15.dll 。
下面的代码段提供了示例:
AcRx::AppRetCode
acrxEntryPoint(AcRx::AppMsgCode msg, void* pkt)
{
switch (msg) {
case AcRx::kInitAppMsg:
if (!acrxClassDictionary->at("AcBrEntity")) {
acrxDynamicLinker->loadModule("acbr15.dll", 1);
acutPrintf(""n acbr15 loaded "n");
}
acedRegCmds->addCommand(
"MY_APP",
"MY_CMD",
"MY_CMD",
ACRX_CMD_MODAL,
&myCmdImp);
acrxUnlockApplication(pkt); // try to allow unloading
break;
case AcRx::kUnloadAppMsg:
acedRegCmds->removeGroup("MY_APP");
break;
default:
break;
}
return AcRx::kRetOK;
}
注意:从程序中退出时卸载acbr15.dll 也很重要,因为其他程序(或模块)可能仍然需要它。
在ObjectARX SDK的objectarx"utils"brep"samples"brepsamp文件夹下有一个使用边界表示库的ObjectARX例子。该目录包含一个Readme 文件解释这个例子以及如何使用它。
示例程序是基于交互方法的,可能不是所有的程序的最佳方案。使用选择集,resbufs和GS marker来选择对象,用AcDbFullSubentPaths传送到AcBr库。