作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
阿列克谢·阿布拉莫夫斯基的头像

Aleksey Abramovsky

Aleksey是一名领先的c++软件开发人员,拥有13年构建高效可靠的代码和长期项目维护的经验.

Expertise

工作经验

19

Share

在本文中,我将向您介绍 Teigha这个库提供了处理DWG文件和ACA对象的另一种方式. 我们将编写一小段代码,用ACA对象创建一个房子.

如果要处理DWG文件和 AutoCAD 对象,唯一的平台选项是 ObjectARX and Teigha. 所有可以读写DWG文件的第三方组件和应用程序都使用Teigha作为基础.

像2016年一样制作DWGs: Teigha For Architecture

加速你的DWG生产与Teigha架构

Teigha Architecture是一组库,使您能够阅读, write, 并处理原始AutoCAD及其衍生产品(如ACA)的对象. 这些库还提供了许多辅助机制来帮助您处理AutoCAD对象和渲染设备以渲染DWG数据库.

Teigha的主要特点是:

  • 支持DWG, DXF, BDXF, DGN文件格式
  • 渲染绘图文件使用GDI, OpenGL,或DirectX与选择实体的能力.
  • 编辑和操作 CAD 数据编程,包括:
  • 将一个实体分解为一组更简单的实体.
  • 对实体应用转换.
  • 修改数据库对象的任意属性.
  • 克隆数据库对象.
  • 导出为SVG, PDF, DWF, BMP, STL, DAE (Collada).
  • 导入DWF/DAE/DGN文件到a .dwg database.
  • 支持自定义对象——成员可以创建自定义对象,这些对象可以在任何Teigha主机应用程序(兼容 .dwg files only).
  • 内部支持ACIS/Parasolid数据, 包括为嵌入的3D实体绘制(线框和阴影)以及访问底层边界表示数据.
  • 实现自定义命令.

为什么设计师应该考虑Teigha?

AutoCAD将其数据保存到 .dwg file format. DWG是一种专有的二进制文件格式,用于存储二维和三维设计数据和元数据. DWG是一个工业标准. 成千上万的DWG图纸存在,需要支持. Besides AutoCAD, 只有一个库可以加载/保存和操作作为DWG文件存储的对象:Teigha.

使用Teigha而不是AutoCAD ObjectARX有几个原因:

  • Cost:如果您开发的应用程序使用存储为 .dwg file, 您有两种选择:开发AutoCAD插件或基于Teigha开发应用程序. 开发AutoCAD插件意味着您的所有客户都必须拥有AutoCAD许可证, 花了一大笔钱. Teigha的价格非常实惠.
  • Flexibility: 基于Teigha,您可以从头开始开发自己的CAD应用程序,以满足客户的某些需求. 您不受AutoCAD核心和GUI的约束. 您可以为您的CAD应用程序开发自己的特定GUI. Or, 如果您需要类似cad的GUI, 你可以使用一个基于teigha的廉价CAD应用程序在市场上(如BricsCAD), ZWCad)作为插件的主机.
  • 许多支持的平台: for example, 您可以为IOS和Android等移动平台构建独立的CAD应用程序, 哪些是在某些领域非常需要的.
  • 源代码访问: 创始成员可以使用Teigha资源. 如果你需要一个特定的功能,没有提供或改变现有的功能为您的需要-您可以购买Teigha源和改变任何你需要的.

Autodesk 提供RealDWG -一个软件库,允许 C++ and .NET 开发人员读写AutoCAD®软件DWG和DXF文件. But again, 它要贵得多, 有年费,只提供加载/保存功能的DWG文件, 而Teigha提供 rendering devices 和其他有用的API,用于构建您自己的CAD应用程序.

像2016年一样制作DWGs: Teigha For Architecture

AutoCAD和ObjectARX的替代方案

首先,让我们加载并渲染一个DWG图形. 我们可以使用Teigha的分发包中的标准样品:

像2016年一样制作DWGs: Teigha For Architecture

我们已经成功加载并渲染了DWG文件. (By the way, 在本文开头可以看到的图片是呈现另一个文件的结果.) 标准示例是一个窗口c++应用程序. 它允许您加载、查看和编辑DWG文件,而无需使用AutoCAD. 我还应该提到,AutoCAD对象的API几乎与Teigha对象的API相同, 因此,您可以轻松地修改现有的ObjectARX插件以支持基于teigha的应用程序.

AutoCAD的大多数替代方案, such as BricsCAD, ZWCad, and IntelliCAD, 使用Teigha来处理DWG格式和ACAD对象.

AutoCAD建筑对象的特点

现在我将向您介绍架构对象以及如何处理它们. AutoCAD架构处理特殊的高级对象,用于 建筑设计墙、门、窗、屋顶等. 对象依赖于视口, that is, 它们可以根据相机的方向进行不同的渲染. 对象是基于样式的. 为每个对象分配特定的样式. 如果更改样式,所有具有该样式的对象都会更改. 每个对象都由组件组成, 每个组件都有自己的视觉设置:颜色, line type, material, and scale. 例如,3D门可以由框架、门板和玻璃片组成. 不同的视图模式, 对象使用不同的几何图形呈现, 因此,不同的演示中,组件的数量和设置是不同的.

建筑学硕士(TA)

要处理体系结构对象,您可以使用ACA及其开放API,该API允许您创建插件. 或者,您可以使用Teigha for Architecture,这是由 开放设计联盟.

Teigha架构是一个С++类库,实现了ACA的所有基本原语, such as walls, windows, doors, roofs, beams, openings, etc. 该库允许您从任何版本的DWG格式加载这些对象,并写入(转换)到最新的DWG版本. TA可以在不同的视图和配置中呈现任何原语. ACA对象之间相互作用, 所以TA也支持ACA的辅助类和机制, such as anchor, display manager, property sets, relation graph, etc.

从Teigha Architecture API开始

我已经描述了Teigha的主要特点. 让我们仔细看看Teigha并编写我们的第一个命令,一个非常简单的命令. 我将使用Visual Studio 2005, 这显然已经过时了, 但是Teigha库是多平台的, 该发行包包含了一个适用于2015年之前所有Visual Studio版本的解决方案生成器. 取决于您的许可证类型, 您将可以访问整个库的完整代码, 或者只针对预构建的二进制文件和头文件.

TA库的集合是这样的:

像2016年一样制作DWGs: Teigha For Architecture

Basically, 这些是常规的Windows DLL文件(你也可以为其他平台构建它们:iOS, Linux, UNIX, etc.). 您可以在单独的文件夹中找到它们的LIB文件. In addition to TA, 我们需要Teigha Core库, 因为TA是Core对象之上的扩展. Core实现了原AutoCAD的主要机制和对象.

初始化Тeigha架构

要初始化库,需要一个对文件执行平台特定操作的类.

类MyServices:公共ExSystemServices,公共ExHostAppServices
{
protected:
  ODRX_USING_HEAP_OPERATORS (ExSystemServices);
};

发行包包括两个现成的Windows扩展, ExSystemServices和ExHostAppServices, 在这种情况下我们可以用什么呢. 然后我们需要初始化库和图形子系统:

OdStaticRxObject svcs;
odInitialize( &svcs );
odgsInitialize();

_OdStaticRxObject_ adds the _addRef/Release_ 对象的逻辑. 该库保存对MyServices对象的引用,并将该对象用于特定于平台的操作.

让我们初始化ТА库:

  //加载所有公共Teigha Architecture DRX模块.
        //注意并非所有调用都是必需的,因为其中一些调用依赖于其他调用
        //但是这里我们列出了所有的.
        //
        //如果一个程序使用TD,它不会修改或创建二进制文件
        //它可能不会在启动时加载任何DRX模块,因为它们会自动加载. 
        //但是如果一个程序修改或创建二进制文件,那么强烈建议使用它
        //加载程序使用的所有DRX模块.
        ::odrxDynamicLinker()->loadApp( OD_T("AecBase") );
        ::odrxDynamicLinker()->loadApp( OD_T("AecArchBase") );
        ::odrxDynamicLinker()->loadApp( OD_T("AecArchDACHBase") );
        ::odrxDynamicLinker()->loadApp( OD_T("AecScheduleData") );
        ::odrxDynamicLinker()->loadApp( OD_T("AecSchedule") );
        ::odrxDynamicLinker()->loadApp( OD_T("AecStructureBase") );

AecBase, AecArchBase等. TX模块(即,DLL库)显示在上面的屏幕截图. 它们已经使用LIB文件进行了链接,但这还不够. 我们还需要将它们初始化为模块. What does it mean? 在运行时,内存包含已加载类的字典. 字典允许您在不同类型的TA对象之间强制转换引用,并通过使用集中的伪构造函数机制创建TA类的实例.

例如,当命令 ::odrxDynamicLinker()->loadApp( OD_T("AecArchBase") ) 正在执行的函数 AECArchBase: initApp () 将在框架中调用. Basically, initApp() 是否会通过调用静态函数在全局字典中注册库的所有类 rxInit() for each of them:

…
AECDbSpaceBoundary: rxInit ();
AECDbStair: rxInit ();
AECDbWall: rxInit ();
AECDbZone: rxInit ();
…

之后,对象创建机制就可用了. 例如,我们可以通过呼叫来创建一堵墙 _AECDbWallPtr pWall = AECDbWall::CreateAECObject()_. 否则,如果尝试创建TA类的对象,就会抛出异常.

让我们通过调用创建一个空的DWG数据库


OdDbDatabasePtr pDatabase = svcs.createDatabase();

这个数据库是一个中心对象. 它是一个对象数据库,保存到DWG文件中并从DWG文件中加载. 我们将把我们创建的所有建筑对象添加到数据库中. 当我们完成后,我们将通过调用以下命令将数据库保存为DWG文件:


OdWrFileBuf cBuffer(strFilename);
pDatabase->writeFile( &cBuffer, OdDb::kDwg, OdDb::kDHL_CURRENT);

现在让我们初始化一些加载的库和显示管理器:


aecarchdachbaseddatabase.Init();
AECScheduleDatabase(pDatabase).Init();
aecstructurebaseddatabase (pDatabase).Init();
  
init_display_system(pDatabase)

在数据库中创建一个AEC字典. 该字典包含长度的默认度量单位, area, volume, and angle, 以及打印设置. 在模块中实现的显示表示是注册的.

初始化完成. 如果你跳过了一些步骤, 结果将取决于您跳过的步骤:对象将不会被创建或渲染(您将看到一个空屏幕), 或者可能还有其他小故障.

到目前为止,完整的代码如下:


类MyServices:公共ExSystemServices,公共ExHostAppServices
{
protected:
  ODRX_USING_HEAP_OPERATORS (ExSystemServices);
};

Int wmain(Int argc, wchar_t* argv[])
{ 
  //用系统服务初始化TD.
  //创建一个hostapp服务实例
  //用于创建TD数据库.
  OdStaticRxObject svcs;
  odInitialize( &svcs );
  odgsInitialize();


  //加载所有公共Teigha Architecture DRX模块.
  //注意并非所有调用都是必需的,因为其中一些调用依赖于其他调用
  //但是这里我们列出了所有的.
  //
  //如果一个程序使用TD,它不会修改或创建二进制文件
  //它可能不会在启动时加载任何DRX模块,因为它们会自动加载. 
  //但是如果一个程序修改或创建二进制文件,那么强烈建议使用它
  //加载程序使用的所有DRX模块.
  ::odrxDynamicLinker()->loadApp( OD_T("AecBase") );
  ::odrxDynamicLinker()->loadApp( OD_T("AecArchBase") );
  ::odrxDynamicLinker()->loadApp( OD_T("AecArchDACHBase") );
  ::odrxDynamicLinker()->loadApp( OD_T("AecScheduleData") );
  ::odrxDynamicLinker()->loadApp( OD_T("AecSchedule") );
  ::odrxDynamicLinker()->loadApp( OD_T("AecStructureBase") );

  //创建空的TD数据库.
  OdDbDatabasePtr pDatabase = svcs.createDatabase ();;
  
  //初始化数据库与默认的Teigha架构内容.
  aecarchdachbaseddatabase.Init();
  AECScheduleDatabase(pDatabase).Init();
  aecstructurebaseddatabase (pDatabase).Init();


  init_display_system(pDatabase)

    
  //在这里处理TA对象


  //对模型空间执行“缩放范围”.
  {
    OdDbViewportTablePtr pVT =
      pDatabase->getViewportTableId().openObject(OdDb::kForRead);
    oddbviewporttableecordptr pV =
      pVT->getActiveViewportId().openObject(OdDb::kForWrite)
    pV->zoomExtents();
  }

  OdWrFileBuf cBuffer("H:\\TA_test . txt ".dwg" );
  pDatabase->writeFile( &cBuffer, OdDb::kDwg, OdDb::kDHL_CURRENT);
  
  odgsUninitialize ();
  odUninitialize();

  return 0;
}

我添加了“缩放范围”命令, 这样当我们打开创建的文件时, 我们可以立即看到添加到它的对象和库的对称取消初始化. 为了让事情更简单, 我删除了错误检查和主要操作周围的try/catch结构.

现在程序将创建一个空的DWG文件,我们可以在AutoCAD中打开并查看.

Handling objects

向你们展示如何使用助教课程, 我要建造一个由地板/地基组成的房子, walls, windows, a door, and a roof. 让我们从墙壁开始.

首先,我们将在我们的绘图中添加一面墙. 要创建这个墙,我们需要首先为它创建一个样式. Let’s write the add_wall_style function:


add_wall_style(OdDbDatabasePtr)
{
  OdDbObjectId idResult =
    AECDbWallStyle::CreateAECObject(pDatabase, OD_T("由Teigha(R) Architecture创建的墙样式"));

  AECDbWallStylePtr pWallStyle =
    idResult.openObject(OdDb::kForWrite)

  pWallStyle->SetDescription( OD_T("Wall Style Description") );
  pWallStyle->SetDictRecordDescription( OD_T("Dialog caption") );

  pWallStyle->SetWallWidth( 4 );
  pWallStyle->SetWallWidthUsed( true );

  pWallStyle->SetBaseHeight( 110 );
  pWallStyle->SetBaseHeightUsed( true );

  pWallStyle->SetJustification( AECDefs::ewjLeft );
  pWallStyle->SetJustificationUsed( true );

  pWallStyle->SetAutomaticCleanups( true );
  pWallStyle->SetAutomaticCleanupsUsed( true );

  pWallStyle->SetCleanupRadius( 4 );
  pWallStyle->SetCleanupRadiusUsed( true );

  pWallStyle->SetFloorLineOffset( 3 );
  pWallStyle->SetFloorLineOffsetUsed( false );

  pWallStyle->SetRoofLineOffset( -3 );
  pWallStyle->SetRoofLineOffsetUsed( false );

  AECDisplayManager cDM(pDatabase)
  aecdbdispropswallmodelptr poorridemodel =
    AECDbDispPropsWallModel::cast( pWallStyle->OverrideDispProps(
    cDM.UpdateDisplayRepresentation(AECDbDispRepWallModel::desc())).openObject(OdDb::kForWrite);
  if ( !pOverrideModel.isNull() )
  {
    pOverrideModel->SetIsDisplayOpeningEndcaps( false );
    pOverrideModel->GetBoundaryCompByIndex( 0 )->SetColor( colorAt( 4 ) );
  }

  aecdbdispropswallptr poorrideplan =
    AECDbDispPropsWall::cast( pWallStyle->OverrideDispProps(
    cDM.UpdateDisplayRepresentation(AECDbDispRepWallPlan::desc())).openObject(OdDb::kForWrite);
  if ( !pOverridePlan.isNull() )
  {
    pOverridePlan->GetBoundaryCompByIndex( 0 )->SetColor( colorAt( 4 ) );
  }

  return( pWallStyle->objectId() );
}

该函数创建AECDbWallStyle对象并设置它的一些参数. 然后调用显示管理器并更改平面显示表示(2D顶视图)和模型显示表示(3D视图)的颜色。.


AECDisplayManager cDM(pDatabase)
  aecdbdispropswallmodelptr poorridemodel =
    AECDbDispPropsWallModel::cast( pWallStyle->OverrideDispProps(
    cDM.UpdateDisplayRepresentation(AECDbDispRepWallModel::desc())).openObject(OdDb::kForWrite);
  if ( !pOverrideModel.isNull() )
  {
    pOverrideModel->SetIsDisplayOpeningEndcaps( false );
    pOverrideModel->GetBoundaryCompByIndex( 0 )->SetColor( colorAt( 2 ) );
  }

在本例中,我们将3D视图中的墙壁设置为黄色. 看起来很复杂, 但这是有原因的:ACA中的显示表示和显示管理器机制是这样工作的. 该机制是灵活的,具有许多功能, 但它的逻辑并不明显,需要你学习.

oddbobjid:运行时引用

数据库对象使用ObjectId对象引用其他数据库对象, 数据库对象指针总是可以从有效的ObjectId对象中获得. 这种机制的效果是,数据库对象不必驻留在内存中,除非用户显式地检查或修改它们.

用户必须在读写一个对象之前显式地打开它, 并且应该在操作完成后释放它. 这个功能允许Teigha支持数据库的部分加载, 其中objecd对象存在于数据库中的所有对象, 但是实际的数据库对象在被访问之前不需要加载. 它还允许从内存中交换未使用的数据库对象, 并在被访问时重新加载.

如果需要在程序启动之间保留对对象的引用,请使用OdDbHandle.

For example, the add_wall_style 函数已返回 idWallStyle. 在本例中,样式只是通过调用显式创建的 AECDbWallStyle: CreateAECObject (), and idWallStyle 包含指向内存中实际对象的指针. 要获得对style对象的写访问权限,我们需要执行以下操作:


AECDbWallStylePtr pWallStyle = idResult.openObject(OdDb::kForWrite)

因此,' openObject() '将返回指向该对象的实际指针,我们可以使用它.

库使用OdSmartPtr智能指针,而不是常规的С++指针:


typedef OdSmartPtr AECDbWallStylePtr

如果对象已经关闭,智能指针的析构函数将通知框架. 因此,可能会重新计算相关对象,发送通知等.

现在我们可以通过下面的调用来添加墙:


oddbobjid = add_wall(pDatabase, idWallStyle, OdGePoint2d(0,0), OdGePoint2d(0,110));

add_wall清单

OdDbObjectId add_wall(OdDbDatabasePtr pDatabase, const OdDbObjectId& idStyle,
                      const OdGePoint2d& ptStart, const OdGePoint2d& ptEnd, double dBulge = 0)
{
  AECDbWallPtr pWall =
    AECDbWall::CreateAECObject( pDatabase->getModelSpaceId(), idStyle );

  pWall->Set( ptStart, ptEnd, dBulge );
  pWall->SetDescription( OD_T("A Wall") );

  return( pWall->objectId() );
}

正如您所看到的,add_wall没有做任何特别的事情. 它只是用我们之前创建的样式创建AECDbWall对象. AECDbWall对象被添加到数据库的模型空间中. In simple terms, 模型空间是一个特殊的字典,它包含了在呈现数据库时要呈现的所有对象.

然后定义墙体的起点、终点和曲率. 注意,墙壁不一定是平的. 如果你愿意,你可以让它凸出.

如果我们做的一切都是正确的,我们将得到一个带有黄色矩形墙的DWG文件. 我正在使用来自Teigha分发包的代码示例来查看该文件, 但在ACA中会以完全相同的方式呈现.

一个DWG文件与一个黄色矩形墙

实际上,我在3D视图中手动旋转了摄像机. 默认情况下,您将拥有顶视图.

现在让我们添加4个墙,包括一个凸墙:


oddbobjid = add_wall(pDatabase, idWallStyle,
    OdGePoint2d(0,0), OdGePoint2d(0,110));
  oddbobjid = add_wall(pDatabase, idWallStyle,
    OdGePoint2d(0,110), OdGePoint2d(110,110));
  oddbobjid = add_wall(pDatabase, idWallStyle,
    OdGePoint2d(110, 110), OdGePoint2d(110, 0));
  oddbobjid = add_wall(pDatabase, idWallStyle,
    OdGePoint2d(11,0), OdGePoint2d(0,0), -1);

我们已经有了房子的基本结构:

房子的基本结构

正如你所看到的,墙壁并不是简单地渲染成单独的物体. 相反,平滑连接是自动添加的. 这是TA的自动功能之一,被称为“清理”.”

Adding windows

现在让我们给房子加装窗户吧. 我们可以像处理门一样处理窗户:我们需要为我们想要添加到绘图中的窗户创建样式, 然后用那个样式添加窗口对象.


oddbobjid idWindowStyle = add_window_style(pDatabase);

add_window_style(OdDbDatabasePtr pDatabase)
{
  oddbobjid idWStyle =
    AECDbWindowStyle: CreateAECObject (pDatabase, OD_T("由Teigha(R) Architecture创建的窗口样式"));

  AECDbWindowStylePtr pWindowStyle = idWStyle.openObject(OdDb::kForWrite)

  pWindowStyle->SetDescription( OD_T("Window Style Description") );
  pWindowStyle->SetDictRecordDescription( OD_T("Dialog caption") );
  pWindowStyle->SetAutoAdjustToWidthOfWall( true );
  pWindowStyle->SetFrameWidth( 2 );
  pWindowStyle->SetFrameDepth( 5 );
  pWindowStyle->SetSashWidth( 2 );
  pWindowStyle->SetSashDepth( 3 );
  pWindowStyle->SetGlassThickness( 1 );
  pWindowStyle->SetWindowType( AECDefs::ewtGlider );
  pWindowStyle->SetWindowShape( AECDefs::esRectangular );

  AECDisplayManager cDM(pDatabase)
  aecdbdppropswindowptr贫穷模型=
    AECDbDispPropsWindow::cast( pWindowStyle->OverrideDispProps(
    cDM.UpdateDisplayRepresentation(AECDbDispRepWindowModel::desc())).openObject(OdDb::kForWrite);
  if ( !pOverrideModel.isNull() )
  {
    pOverrideModel->GetFrameComp()->SetColor( colorAt( 1 ) );
    pOverrideModel->GetSashComp()->SetColor( colorAt( 2 ) );
    pOverrideModel->GetGlassComp()->SetColor( colorAt( 3 ) );
  }

  aecdbdppropswindowptr贫穷计划=
    AECDbDispPropsWindow::cast( pWindowStyle->OverrideDispProps(
    cDM.UpdateDisplayRepresentation(AECDbDispRepWindowPlan::desc())).openObject(OdDb::kForWrite);
  if ( !pOverridePlan.isNull() )
  {
    pOverridePlan->GetFrameComp()->SetColor( colorAt( 1 ) );
    pOverridePlan->GetSashComp()->SetColor( colorAt( 2 ) );
    pOverridePlan->GetGlassComp()->SetColor( colorAt( 3 ) );
  }

  return( pWindowStyle->objectId() );
}

正如您在代码中看到的,AECDbWindowStyle对象被创建并添加到数据库中. 然后为样式设置一些设置(尽管我们可以使用默认设置). 之后,为2D视图和3D视图重新定义一些组件的颜色. 在这种情况下,组件是窗户的物理部分:框架、窗扇和一片玻璃.

的方法在第一面墙上添加一个窗口 add_window function:


oddbobjid idWindow01 = add_window(pDatabase, idWindowStyle, idwall1,10,10);

//使用指定的窗口样式将窗口插入数据库.
//如果idWall参数不为空,它也会将窗口附加到墙上.
//返回新创建窗口的对象ID.
add_window(OdDbDatabasePtr pDatabase, const OdDbObjectId .& idStyle, const oddbobjid& idWall,
                        双dOffsetAlongX,双dOffsetAlongZ)
{
  AECDbWindowPtr pWindow = AECDbWindow::CreateAECObject( pDatabase->getModelSpaceId(), idStyle );

  pWindow->SetRise( 10 );
  pWindow->SetWidth( 40 );
  pWindow->SetHeight( 40 );

  pWindow->SetOpenPercent( 60 );
  pWindow->SetMeasureTo( AECDefs::eomtOutsideFrame );
  pWindow->SetLeaf( 10 );

  if ( !idWall.isNull() )
  {
    pWindow->AttachWallAnchor( idWall );

    AECDbAnchorEntToCurvePtr pAnchor = pWindow->GetAnchor().openObject(OdDb::kForWrite)
    pAnchor->GetXParams()->SetOffset( dOffsetAlongX );       
    pAnchor->GetZParams()->SetOffset( dOffsetAlongZ );
  }

  return( pWindow->objectId() );
}

The add_window() 功能类似于 add_wall(),但有一个区别:它使用锚对象.

我们创建AECDbWindow对象并将其添加到数据库的模型空间中. 然后我们为AECDbWindow的特定实例设置一些设置. 之后,我们把窗户装到墙上. 从AECDbAnchorEntToCurve派生的一个特殊对象将窗口附加到墙上.

该对象包含沿X的偏移量, Y, 和Z轴从墙的坐标系原点到窗口的坐标系原点. 当我们调用AttachWallAnchor()时,该对象的一个实例被创建并添加到数据库中. 墙本身不知道它是否有窗户. 创建锚涉及另一种基本机制, 关系图, 它包含对象之间的关系:Who is attached to Who, who includes whom, and who owns whom. 如果修改了墙,关系图将收到AECDbWall对象已更改的通知. 它将检查所有关系并触发相关对象的更新. 在这种情况下,AECDbWindow将被更新. For example, 如果你移动墙, 其中的Windows将自动移动, 因为它们会被关系图通知. 您可以访问关系图并请求特定对象的关系. Actually, 窗口知道它附着在哪个对象上, 因为每个窗口都包含对所创建的锚的引用.

让我们来看看结果:

The result

我已经改变了墙壁的颜色,这样你就能更清楚地看到窗户. (代码创建了蓝色的墙,我只是在写这篇文章时选择了颜色.) TA包含许多预定义的窗口样式和类型,您可以通过枚举来使用:


enum WindowType
    {
        ewtPicture = 1,
        ewtSingleHung = 2;
        ewtDoubleHung = 3,
        ewtAwningTransom = 4,
        ewtDoubleCasement = 5,
        ewtGlider = 6;
        ewtHopperTransom = 7,
        ewtPassThrough = 8;
        ewtSingleCasement = 9,
        ewtSingleHopper = 10;
        ewtSingleAwning = 11,
        ewtVerticalPivot = 12,
        ewtHorizontalPivot = 13,
        ewtUnevenSingleHung = 14,
        ewtUnevenDoubleHung = 15
    };
enum Shape
    {
        esrectangle = 0;
        esRound = 1,
        eshalfound = 2,
        esQuarterRound = 3,
        esOval = 4;
        搜索= 5;
        esTrapezoid = 6;
        esGothic = 7;
        等腰三角形= 8,
        esRightTriangle = 9,
        esPeakPentagon = 10;
        esOctagon = 11,
        esHexagon = 12,
        esCustom = 13
    };

I’ve selected AECDefs::ewtGlider和AECDefs:: esrectangle. 正如你所看到的,还有很多其他的形状可供选择. 通过使用这些和其他设置, 您可以创建非常复杂的窗口类型, 有多个饰带,玻璃上有内部图案. 最好的事情是,您不必手动一点一点地创建它,也不必以编程方式实现所有内容. 您所需要做的就是为现有对象或样式设置一些参数.

实际上,所有的Teigha Architecture对象都非常复杂,并且有很多设置. 正因为如此,Teigha for Architecture提供了大量“开箱即用”的机会.”

让我们在所有平墙上添加窗户:


oddbobjid idWindow01 = add_window(pDatabase, idWindowStyle, idwall1,10,10);
  OdDbObjectId idWindow02 = add_window(pDatabase, idWindowStyle, idWall1, 60,10);
  OdDbObjectId idWindow03 = add_window(pDatabase, idWindowStyle, idWall1, 10,60);
  oddbobjid idWindow04 = add_window(pDatabase, idWindowStyle, idWall1, 60,60);

  oddbobjid idWindow05 = add_window(pDatabase, idWindowStyle, idWall2, 10,10);
  OdDbObjectId idWindow06 = add_window(pDatabase, idWindowStyle, idWall2, 60,10);
  OdDbObjectId idWindow07 = add_window(pDatabase, idWindowStyle, idWall2, 10,60);
  oddbobjid = add_window(pDatabase, idWindowStyle, idWall2, 60,60);

  oddbobjid idWindow09 = add_window(pDatabase, idWindowStyle, idWall3, 10,10);
  OdDbObjectId idWindow10 = add_window(pDatabase, idWindowStyle, idWall3, 60,10);
  oddbobjid idWindow11 = add_window(pDatabase, idWindowStyle, idWall3, 10,60);
  oddbobjid idWindow12 = add_window(pDatabase, idWindowStyle, idWall3, 60,60);

显示多个墙壁的渲染形状.

我没有费心去完成代码, 但是您可以单独处理每个窗口:更改其打开百分比, color, etc. 如果更改样式,则更改将立即应用于具有该样式的所有窗口.

在绘图中添加门

为了完成这幅图,让我们添加一扇门. 首先,我们将为门板创建一个2D配置文件(一个带窗孔的门扇). 然后我们将用该配置文件创建一个样式. 最后,我们将能够创建具有该样式的门对象. 或者,我们可以使用默认样式. 就像窗户(或任何其他开口)一样,门是用锚固定在墙上的. The add_profile_def, add_door_style, and add_door listing.


//将配置文件定义插入数据库.
//返回新创建配置文件定义的对象ID.
add_profile_def(OdDbDatabasePtr)
{
  OdDbObjectId idProfDef =
    AECDbProfileDef: CreateAECObject (pDatabase, OD_T("Profile Definition By Teigha(R) Architecture"));

  AECDbProfileDefPtr pProfileDefinition = idProfDef.openObject(OdDb::kForWrite)

  AECGe: Profile2D cProfile;
  cProfile.resize( 2 );

  cProfile[ 0 ].appendVertex(OdGePoint2d(0,0));
  cProfile[ 0 ].appendVertex(OdGePoint2d(1,0));
  cProfile[ 0 ].appendVertex(OdGePoint2d(1,1));
  cProfile[ 0 ].appendVertex(OdGePoint2d(0,1));
  cProfile[ 0 ].setClosed();

  //强制等高线为逆时针.
  //因此,如果轮廓已经是ccw,则不需要此调用.
  cProfile[ 0 ].makeCCW();

  cProfile[ 1 ].addvertex (OdGePoint2d.2, 0.2 ) );
  cProfile[ 1 ].addvertex (OdGePoint2d.2, 0.8 ) );
  cProfile[ 1 ].addvertex (OdGePoint2d.8, 0.8 ) );
  cProfile[ 1 ].addvertex (OdGePoint2d.8, 0.2 ) );
  cProfile[ 1 ].setClosed();

  cProfile[ 1 ].makeCCW(false);

  pProfileDefinition->GetProfile()->Init( cProfile );

  return( pProfileDefinition->objectId() );
}

//在数据库中插入一个门的样式.
//返回新创建的门样式的对象ID.
add_door_style(OdDbDatabasePtr pDatabase, const OdDbObjectId .& idProfile )
{
  oddbobjid idDoorStyle =
    AECDbDoorStyle::CreateAECObject(pDatabase, OD_T("由Teigha(R) Architecture创建的门样式")));
  AECDbDoorStylePtr pDoorStyle = idDoorStyle.openObject(OdDb::kForWrite)

  pDoorStyle->SetDescription( OD_T("Door Style Description") );
  pDoorStyle->SetDictRecordDescription( OD_T("Dialog caption") );
  pDoorStyle->SetAutoAdjustToWidthOfWall( true );
  pDoorStyle->SetFrameWidth( 2 );
  pDoorStyle->SetFrameDepth( 5 );
  pDoorStyle->SetStopWidth( 2 );
  pDoorStyle->SetStopDepth( 3 );
  pDoorStyle->SetShapeAndType( AECDefs::esCustom, AECDefs::edtSingle );
  pDoorStyle->SetProfile( idProfile );
  pDoorStyle->SetGlassThickness( 1 );

  AECDisplayManager cDM(pDatabase)
  aecdbdppropsdoorptr贫穷模型=
    AECDbDispPropsDoor::cast( pDoorStyle->OverrideDispProps(
    cDM.UpdateDisplayRepresentation(AECDbDispRepDoorModel::desc())).openObject(OdDb::kForWrite);
  if ( !pOverrideModel.isNull() )
  {
    pOverrideModel->GetPanelComp()->SetColor( colorAt( 1 ) );
    pOverrideModel->GetFrameComp()->SetColor( colorAt( 2 ) );
    pOverrideModel->GetStopComp()->SetColor( colorAt( 3 ) );
    pOverrideModel->GetSwingComp()->SetColor( colorAt( 4 ) );
    pOverrideModel->GetGlassComp()->SetColor( colorAt( 5 ) );
  }

  aecdbdppropsdoorptr poorrideplan =
    AECDbDispPropsDoor::cast( pDoorStyle->OverrideDispProps(
    cDM.UpdateDisplayRepresentation(AECDbDispRepDoorPlan::desc())).openObject(OdDb::kForWrite);
  if ( !pOverridePlan.isNull() )
  {
    pOverridePlan->GetPanelComp()->SetColor( colorAt( 1 ) );
    pOverridePlan->GetFrameComp()->SetColor( colorAt( 2 ) );
    pOverridePlan->GetStopComp()->SetColor( colorAt( 3 ) );
    pOverridePlan->GetSwingComp()->SetColor( colorAt( 4 ) );
    pOverridePlan->GetDirectionComp()->SetColor( colorAt( 5 ) );
  }

  return( pDoorStyle->objectId() );
}


//使用指定的门样式将门插入数据库.
//如果idWall参数不为空,它还将门附加到墙上.
//返回新创建门的对象ID.
add_door(OdDbDatabasePtr pDatabase, const OdDbObjectId .& idStyle, const oddbobjid& idWall,
                      双dOffsetAlongX,双dOffsetAlongZ)
{
  AECDbDoorPtr pDoor = AECDbDoor::CreateAECObject( pDatabase->getModelSpaceId(), idStyle );

  pDoor->SetRise( 10 );
  pDoor->SetWidth( 40 );
  pDoor->SetHeight( 50 );

  pDoor->SetOpenPercent( 20 );
  pDoor->SetMeasureTo( AECDefs::eomtOutsideFrame );
  pDoor->SetLeaf( 10 );

  if ( !idWall.isNull() )
  {
    pDoor->AttachWallAnchor( idWall );

    AECDbAnchorEntToCurvePtr pAnchor = pDoor->GetAnchor().openObject(OdDb::kForWrite)
    pAnchor->GetXParams()->SetOffset( dOffsetAlongX );
    pAnchor->GetZParams()->SetOffset( dOffsetAlongZ );
  }

  return( pDoor->objectId() );
}

让我们将以下代码添加到main中:


AECDbWallPtr pWall = idWall4.openObject(OdDb::kForRead);
  double dLength = pWall->GetLength();
  double dOWidth = 40;
  double dL1 = 10;
  double dL3 = dLength - dOWidth - 10;
  double dL2 = dL1 + dOWidth + (dL3 - (dL1 + 2 * dOWidth)) / 2;

  oddbobjid idDoor = add_door (pDatabase, idDoorStyle, idWall4, dL2, 0);

这是有区别的, 虽然:我们用读访问打开一个墙,并得到它的长度,以便计算偏移量.

因此,我们在凸墙上放了一扇门:

凸墙上的门

我们还可以在凸墙上添加窗口:


OdDbObjectId idWindow13 = add_window (pDatabase, idWindowStyle, idWall4, dL1, 10);
  OdDbObjectId idWindow14 = add_window (pDatabase, idWindowStyle, idWall4, dL3, 10);
  OdDbObjectId idWindow15 = add_window (pDatabase, idWindowStyle, idWall4, dL1, 60);
  OdDbObjectId idWindow16 = add_window (pDatabase, idWindowStyle, idWall4, dL2, 60);
  OdDbObjectId idOpening = add_window (pDatabase, idWindowStyle, idWall4, dL3, 60);

结果是一座没有屋顶和地板的房子:

没有屋顶或地板的房子

让我们编写' add_roof() '函数.


void add_roof(OdDbDatabasePtr pDatabase)
{
  AECGe: Profile2D cProfile;
  cProfile.resize( 1 );
  cProfile.front().appendVertex(OdGePoint2d(0,0));
  cProfile.front().appendVertex(OdGePoint2d(0,110));
  cProfile.front().appendVertex(OdGePoint2d(110,110));
  cProfile.front().appendVertex(OdGePoint2d(110, 0), -1);
  cProfile.front().setClosed();
  cProfile.front().makeCCW();
  
  AECDbRoofPtr pRoof =
    AECDbRoof::CreateAECObject( pDatabase->getModelSpaceId() );

  //初始化屋顶轮廓.
  //默认情况下,屋顶轮廓的所有边都有一个45度的斜度.
  pRoof->GetProfile()->Init( cProfile );

  pRoof->SetThickness( 2 );

  ////手动修改车顶段.
  AECGeRingSubPtr pRoofLoop = pRoof->GetProfile()->GetRingByIndex( 0 );
  if ( !pRoofLoop.isNull() )
  {
    OdUInt32 i, iSize = pRoofLoop->GetSegmentCount();
    for ( i = 0; i < iSize; i++ )
    {
      AECGeRoofSegmentSubPtr pSeg = pRoofLoop->GetSegments()->GetAt( i );
      pSeg->SetFaceCount(1);
      pSeg->SetFaceHeightByIndex(0, 110);
      pSeg->SetBaseHeight(0);
      pSeg->SetOverhang(10.0);
      pSeg->SetFaceSlopeByIndex(0, OdaPI4);
      pSeg->SetSegmentCount(10);
    }
  }

  pRoof->setColorIndex( 3 );  
}

屋顶是基于二维轮廓创建的,其穿越方向是逆时针的. Calling makeCCW() 如果是顺时针,则改变遍历方向. 这样做很重要,因为算法期望剖面的遍历方向是逆时针的, 否则它将不起作用.

轮廓与墙壁的中心线重合. 然后我们需要为剖面的每个部分设置斜率角, 屋顶上的面数, 每个面的顶点在XY平面上的高度(Z坐标)(SetFaceHeightByIndex), and the overhang. SetSegmentCount() 只对有曲率的线段有效. 此参数设置近似精度, that is, 近似弧的线段数.

Here’s our roof:

加了屋顶的房子

有很多屋顶设置, 所以你可以创造一个几乎任何形式的屋顶:山墙屋顶, a hip roof, 山谷形屋顶, you name it. 每个面都是一个独立的RoofSlab对象,你可以手动编辑.

在绘图中添加一个楼层

现在我们至少需要添加一个非常基本的地板/基础. 为此,我们可以使用slab对象. Let’s write the add_slab function.

void add_slab(OdDbDatabasePtr)
{
  oddbobjid idStyle =
    AECDbSlabStyle:: gettaecobject (pDatabase, OD_T("Slab Style"));
  if ( idStyle.isNull() )
  {
    idStyle = AECDbSlabStyle::CreateAECObject(pDatabase, OD_T("Slab Style"));
  }

  AECDbSlabStylePtr pStyle =
    idStyle.openObject(OdDb::kForWrite)
  if ( !pStyle.isNull() )
  {
    pStyle->GetComponents()->Clear();

    AECSlabStyleCompPtr pCmp = AECSlabStyleComp::createObject();
    pCmp->SetName( OD_T("Base") );
    pCmp->GetPosition()->GetThickness()->SetUseBaseValue( false );
    pCmp->GetPosition()->GetThickness()->SetBaseValue( 6 );
    pCmp->GetPosition()->GetThicknessOffset()->SetUseBaseValue( false );
    pCmp->GetPosition()->GetThicknessOffset()->SetBaseValue( - 6 );
    pStyle->GetComponents()->Insert( pCmp );
  }

  AECDbSlabPtr pSlab =
    AECDbSlab::CreateAECObject( pDatabase->getModelSpaceId(), idStyle );

  {
    AECGe: Profile2D cBase;
    cBase.resize( 1 );
    cBase.front().appendVertex(OdGePoint2d(-5, -5), 1);
    cBase.front().appendVertex(OdGePoint2d(115, -5));
    cBase.front().appendVertex(OdGePoint2d(115,115));
    cBase.front().appendVertex(OdGePoint2d(- 5,115));
    cBase.front().setClosed();
    cBase.front().makeCCW();

    pSlab->GetSlabFace()->Init( cBase );
  }

  pSlab->SetThickness( 5 );

  pSlab->SetVerticalOffset( 0 );
  pSlab->SetHorizontalOffset( 0 );

  pSlab->SetPivotPoint( OdGePoint3d::kOrigin );

  AECDisplayManager cDM(pDatabase)
  aecdbdispropsslabptr poorridemodel =
    AECDbDispPropsSlab::cast( pSlab->OverrideDispProps(
    cDM.UpdateDisplayRepresentation(AECDbDispRepSlabModel::desc())).openObject(OdDb::kForWrite);
  if ( !pOverrideModel.isNull() )
  {
    pOverrideModel->GetBoundaryCompByIndex( 0 )->SetColor( colorAt( 1 ) );
    pOverrideModel->GetBaselineComp()->SetColor( colorAt( 4 ) );
    pOverrideModel->GetPivotPointComp()->SetColor( colorAt( 5 ) );
    pOverrideModel->GetFasciaComp()->SetColor( colorAt( 6 ) );
    pOverrideModel->GetSoffitComp()->SetColor( colorAt( 7 ) );
    pOverrideModel->GetShrinkWrapBodyComp()->SetColor( colorAt( 8 ) );
  }

  aecdbdispropsslabplanptr poorrideplan =
    AECDbDispPropsSlabPlan::cast( pSlab->OverrideDispProps(
    cDM.UpdateDisplayRepresentation(AECDbDispRepSlabPlan::desc())).openObject(OdDb::kForWrite);
  if ( !pOverridePlan.isNull() )
  {
    pOverridePlan->SetIsOverrideCutPlane( false );
    pOverridePlan->GetHatchComp()->SetColor( colorAt( 1 ) );
    pOverridePlan->GetBelowCutPlaneBodyComp()->SetColor( colorAt( 2 ) );
    pOverridePlan->GetAboveCutPlaneBodyComp()->SetColor( colorAt( 3 ) );
    pOverridePlan->GetBelowCutPlaneOutlineComp()->SetColor( colorAt( 4 ) );
    pOverridePlan->GetAboveCutPlaneOutlineComp()->SetColor( colorAt( 5 ) );
  }
}

在这种情况下,我们创建一个新的地板样式,然后添加组件. 组件是包含诸如厚度等参数的地板部件, 高于XY平面的高度, name, material, index, etc. 地板可以由不同设置的几个组件组成. For example, 如果它们在XY平面上有不同的高度, 您可以使用该样式的一个地板对象来渲染多层建筑中的所有地板和天花板.

样式设置应用于包含地板形状的特定对象. In this case, 我们创建一个板,并初始化它的轮廓与底部的墙相同的轮廓, 只是在边缘有一个小的偏移. 然后我们使用显示管理器来重新定义地板不同组件的颜色.

最后,我们创造了一个像这样的房子:

完工的房子,包括地板

为了确保,让我们尝试在Autodesk ACA中加载生成的DWG文件:

呈现的DWG文件

这是我们的房子在AutoCAD中加载的. 这里看起来更好,不是吗?

Conclusion

Using Teigha, 我们创建了一个空数据库, 初始化它以处理体系结构对象, 创建了一些常用的对象类型, 并成功将它们全部保存为最新格式的DWG文件.

当然,我已经简化了许多事情,并以粗略的方式描述了它们. 但本文的目的是演示Teigha架构的功能,并让您大致了解Teigha作为处理DWG文件和AutoCAD对象的替代解决方案.

聘请Toptal这方面的专家.
Hire Now
阿列克谢·阿布拉莫夫斯基的头像
Aleksey Abramovsky

Located in 下诺夫哥罗德,俄罗斯

Member since October 22, 2013

About the author

Aleksey是一名领先的c++软件开发人员,拥有13年构建高效可靠的代码和长期项目维护的经验.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

Expertise

工作经验

19

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

Toptal Developers

Join the Toptal® community.

" class="hidden">记者网