个性化阅读
专注于IT技术分析

像2016年一样产生DWG:Teigha for Architecture

本文概述

在本文中, 我将向你介绍Teigha, 它是一个提供处理DWG文件和ACA对象的替代方法的库。我们将编写一小段代码, 用ACA对象创建房屋。

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

像2016年一样产生DWG:Teigha for Architecture

使用Teigha Architecture加快DWG生产

鸣叫

Teigha Architecture是一组库, 使你可以读取, 编写和处理原始AutoCAD及其衍生版本(如ACA)的对象。这些库还提供了许多辅助机制来帮助你处理AutoCAD对象和渲染设备以渲染DWG数据库。

Teigha的主要特点是:

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

设计师为什么要考虑比重?

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

使用Teigha而非AutoCAD ObjectARX的原因有很多:

  • 成本:如果开发的应用程序可以处理存储为.dwg文件的信息, 则有两个选择:开发AutoCAD插件或基于Teigha开发应用程序。开发AutoCAD插件意味着你的所有客户都必须拥有AutoCAD许可证, 这需要一笔巨款。 Teigha定价非常实惠。
  • 灵活性:基于Teigha, 你可以从头开始开发自己的CAD应用程序, 以满足客户的特定需求。你没有绑定到AutoCAD Core和GUI。你可以为CAD应用程序开发自己的特定GUI。或者, 如果你需要类似CAD的GUI, 则可以使用市场上可用的基于Teigha的廉价CAD应用程序之一(例如BricsCAD, ZWCad)作为插件的主机。
  • 许多受支持的平台:例如, 你可以为诸如iOS和Android之类的移动平台构建独立的CAD应用程序, 这在某些领域非常需要。
  • 源代码访问:Teigha源可用于创始成员。如果你需要未提供的特定功能或根据需要更改现有功能, 则可以购买Teigha货源并更改所需的任何东西。

Autodesk提供RealDWG-一个软件库, 允许C ++和.NET开发人员读取和写入AutoCAD®软件DWG和DXF文件。但是, 它再次昂贵得多, 需要支付年费, 并且仅提供DWG文件的加载/保存功能, 而Teigha提供渲染设备和其他有用的API, 以构建自己的CAD应用程序。

像2016年一样产生DWG:Teigha for Architecture

AutoCAD和ObjectARX的替代品

首先, 让我们加载并渲染DWG图形。我们可以使用Teigha分发包中的标准示例:

像2016年一样产生DWG:Teigha for Architecture

我们已经成功加载并渲染了DWG文件。 (顺便说一句, 你在本文开头看到的图片是呈现不同文件的结果。)标准示例是带窗口的C ++应用程序。它使你无需使用AutoCAD即可加载, 查看和编辑DWG文件。我还应该提到AutoCAD对象的API与Teigha对象的API几乎相同, 因此你可以轻松地修改现有的ObjectARX插件以支持基于Teigha的应用程序。

大多数AutoCAD替代方案, 例如Br​​icsCAD, ZWCad和IntelliCAD, 都使用Teigha来处理DWG格式和ACAD对象。

AutoCAD Architecture对象的特殊性

现在, 我将向你介绍建筑对象以及如何处理它们。 AutoCAD Architecture处理用于建筑设计的特殊高级对象:墙壁, 门, 窗户, 屋顶等。这些对象取决于视口, 也就是说, 可以根据摄影机方向对它们进行不同的渲染。对象是基于样式的。特定的样式分配给每个对象。如果更改样式, 则具有该样式的所有对象都会更改。每个对象都由组件组成, 并且每个组件都有自己的视觉设置:颜色, 线型, 材质和比例。例如, 3D门可能由框架, 门板和一块玻璃组成。对于不同的视图模式, 使用不同的几何体渲染对象, 因此对于不同的演示, 组件的数量和设置是不同的。

建筑比重(TA)

要处理建筑对象, 可以使用ACA及其开放式API, 使你可以创建插件。或者, 你可以使用Teigha for Architecture, 这是由开放设计联盟开发的库。

Teigha Architecture是С++类库, 它实现了ACA的所有基本原语, 例如墙, 窗, 门, 屋顶, 横梁, 开口等。该库允许你从任何版本的DWG格式加载这些对象并编写(转换)为最新的DWG版本。 TA可以在不同的视图和配置中渲染任何基元。 ACA对象彼此交互, 因此TA也支持ACA的辅助类和机制, 例如锚, 显示管理器, 属性集, 关系图等。

从Teigha Architecture API开始

我已经介绍了Teigha的主要功能。让我们仔细看看Teigha并编写第一个命令, 一个非常简单的命令。我将使用显然已经过时的Visual Studio 2005, 但是Teigha库是多平台的, 并且分发包中包含一个解决方案生成器, 该解决方案生成器适用于2015年之前的所有Visual Studio版本。根据你的许可证类型, 你将可以访问整个库的完整代码, 或仅预构建的二进制文件和头文件。

TA库的集合如下所示:

像2016年一样产生DWG:Teigha for Architecture

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

初始化ТeighaArchitecture

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

class MyServices : public ExSystemServices, public ExHostAppServices
{
protected:
  ODRX_USING_HEAP_OPERATORS(ExSystemServices);
};

该分发软件包包括两个针对Windows的现成扩展, 即ExSystemServices和ExHostAppServices, 我们可以在这种情况下使用它们。然后, 我们需要初始化库和图形子系统:

OdStaticRxObject<MyServices> svcs;
odInitialize( &svcs );
odgsInitialize();

_OdStaticRxObject_将_addRef / Release_逻辑添加到对象。该库保存对MyServices对象的引用, 并将该对象用于平台特定的操作。

让我们初始化ТА库:

  // Loading of all public Teigha Architecture DRX modules.
        // Note that not all calls are necessary for some of them depend on others
        // but here we list all of them.
        //
        // If a program uses TD doesn't modify or create binary files
        // it may not load any of DRX modules on start because they will be loaded automatically. 
        // But if a program modifies or creates binary files then it is highly recommended
        // to load all DRX modules program uses.
        ::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文件进行了链接, 但这还不够。我们还需要将它们初始化为模块。这是什么意思?在运行时, 内存包含已加载类的字典。该词典使你可以在不同类型的TA对象之间转换引用, 并使用集中式伪构造器机制创建TA类的实例。

例如, 当执行命令::: odrxDynamicLinker()-> loadApp(OD_T(” AecArchBase”))时, 将在框架中调用函数AECArchBase :: initApp()。基本上, initApp()将通过为每个类调用静态函数rxInit()来在全局字典中注册该库的所有类:

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

之后, 对象创建机制将可用。例如, 我们可以通过调用_AECDbWallPtr pWall = AECDbWall :: CreateAECObject()_来创建墙。否则, 如果我们尝试创建TA类的对象, 则会引发异常。

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

OdDbDatabasePtr pDatabase = svcs.createDatabase();

该数据库是中心对象。它是一个对象数据库, 已保存到DWG文件并从中加载。我们将添加所有创建的架构对象到该数据库。完成后, 我们将通过调用以下命令将数据库保存到DWG文件:

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

现在, 让我们初始化更多已加载的库和显示管理器:

AECArchDACHBaseDatabase( pDatabase ).Init();
AECScheduleDatabase( pDatabase ).Init();
AECStructureBaseDatabase( pDatabase ).Init();
  
init_display_system( pDatabase );

在数据库中创建一个AEC词典。该词典包含长度, 面积, 体积和角度以及打印设置的默认度量单位。模块中实现的显示表示已注册。

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

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

class MyServices : public ExSystemServices, public ExHostAppServices
{
protected:
  ODRX_USING_HEAP_OPERATORS(ExSystemServices);
};

int wmain(int argc, wchar_t* argv[])
{ 
  // Initialize TD with system services.
  // And create single instance of hostapp services
  // for TD database creation.
  OdStaticRxObject<MyServices> svcs;
  odInitialize( &svcs );
  odgsInitialize();


  // Loading of all public Teigha Architecture DRX modules.
  // Note that not all calls are necessary for some of them depend on others
  // but here we list all of them.
  //
  // If a program uses TD doesn't modify or create binary files
  // it may not load any of DRX modules on start because they will be loaded automatically. 
  // But if a program modifies or creates binary files then it is highly recommended
  // to load all DRX modules program uses.
  ::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") );

  // Create empty TD database.
  OdDbDatabasePtr pDatabase = svcs.createDatabase();;
  
  // Initialize database with default Teigha Architecture content.
  AECArchDACHBaseDatabase( pDatabase ).Init();
  AECScheduleDatabase( pDatabase ).Init();
  AECStructureBaseDatabase( pDatabase ).Init();


  init_display_system( pDatabase );

    
  // do something here with TA objects


  // Perform "zoom extents" on model space.
  {
    OdDbViewportTablePtr pVT =
      pDatabase->getViewportTableId().openObject( OdDb::kForRead );
    OdDbViewportTableRecordPtr pV =
      pVT->getActiveViewportId().openObject( OdDb::kForWrite );
    pV->zoomExtents();
  }

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

  return 0;
}

我添加了”缩放范围”命令, 以便在打开创建的文件时, 我们可以立即看到添加到该文件中的对象以及该库的对称取消初始化。为简化起见, 我删除了错误检查和主要动作周围的try / catch构造。

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

处理对象

为了向你展示如何使用TA类, 我将创建一栋由地板/地基, 墙壁, 窗户, 门和屋顶组成的房屋。让我们从墙开始。

首先, 我们将在工程图中添加一堵墙。要创建墙, 我们需要首先为其创建样式。让我们编写add_wall_style函数:

OdDbObjectId add_wall_style( OdDbDatabasePtr pDatabase )
{
  OdDbObjectId idResult =
    AECDbWallStyle::CreateAECObject( pDatabase, OD_T("Wall Style Created By 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 );
  AECDbDispPropsWallModelPtr pOverrideModel =
    AECDbDispPropsWallModel::cast( pWallStyle->OverrideDispProps(
    cDM.UpdateDisplayRepresentation( AECDbDispRepWallModel::desc() ) ).openObject( OdDb::kForWrite ) );
  if ( !pOverrideModel.isNull() )
  {
    pOverrideModel->SetIsDisplayOpeningEndcaps( false );
    pOverrideModel->GetBoundaryCompByIndex( 0 )->SetColor( colorAt( 4 ) );
  }

  AECDbDispPropsWallPtr pOverridePlan =
    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 );
  AECDbDispPropsWallModelPtr pOverrideModel =
    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中的显示表示和显示管理器机制以这种方式工作。该机制很灵活, 具有许多功能, 但是其逻辑并不明显, 需要你自己学习一些知识。

OdDbObjectId:运行时参考

数据库对象使用ObjectId对象引用其他数据库对象, 并且始终可以从有效的ObjectId对象中获取数据库对象指针。这种机制的作用是, 除非用户明确检查或修改了数据库对象, 否则不必将它们驻留在内存中。

用户必须在读取或写入对象之前显式打开对象, 并在操作完成后释放它。此功能允许Teigha支持数据库的部分加载, 其中数据库中所有对象都存在ObjectId对象, 但是在访问它们之前不需要加载实际的数据库对象。它还允许将未使用的数据库对象换出内存, 并在访问它们时将其重新加载。

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

例如, add_wall_style函数已返回idWallStyle。在这种情况下, 通过调用AECDbWallStyle :: CreateAECObject()显式创建了样式, 并且idWallStyle包含指向内存中实际对象的指针。要获得对样式对象的写访问权, 我们需要执行以下操作:

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

结果, ” openObject()”将返回指向该对象的实际指针, 我们可以使用它。

该库使用OdSmartPtr智能指针代替常规的С++指针:

typedef OdSmartPtr<AECDbWallStyle> AECDbWallStylePtr

如果对象已关闭, 则智能指针的析构函数将通知框架。结果, 可以重新计算相关对象, 发送通知等。

现在我们可以通过此调用添加墙:

OdDbObjectId idWall1 = add_wall( pDatabase, idWallStyle, OdGePoint2d( 0, 0 ), OdGePoint2d(   0, 110 ) );

The add_wall Listing

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对象被添加到数据库的模型空间。简单来说, 模型空间是一个特殊的字典, 其中包含我们渲染数据库时要渲染的所有对象。

然后, 为墙定义起点, 终点和曲率。请注意, 墙壁不必平坦。如果愿意, 可以使其凸出。

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

具有一个黄色矩形墙的DWG文件

实际上, 我已经在3D视图中手动旋转了相机。默认情况下, 你将拥有顶视图。

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

OdDbObjectId idWall1 = add_wall( pDatabase, idWallStyle, OdGePoint2d( 0, 0 ), OdGePoint2d(   0, 110 ) );
  OdDbObjectId idWall2 = add_wall( pDatabase, idWallStyle, OdGePoint2d( 0, 110 ), OdGePoint2d( 110, 110 ) );
  OdDbObjectId idWall3 = add_wall( pDatabase, idWallStyle, OdGePoint2d( 110, 110 ), OdGePoint2d( 110, 0 ) );
  OdDbObjectId idWall4 = add_wall( pDatabase, idWallStyle, OdGePoint2d( 110, 0 ), OdGePoint2d(   0, 0 ), -1 );

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

房子的基本结构

如你所见, 墙壁并非简单地渲染为单独的对象。而是自动添加了平滑结。它是TA的自动功能之一, 称为”清理”。

新增视窗

现在, 我们将窗户添加到我们的房屋中。我们可以像处理门一样处理窗口:我们需要为要添加到图形中的窗口创建样式, 然后使用该样式添加窗口对象。

OdDbObjectId idWindowStyle =  add_window_style( pDatabase );

OdDbObjectId add_window_style( OdDbDatabasePtr pDatabase )
{
  OdDbObjectId idWStyle =
    AECDbWindowStyle::CreateAECObject( pDatabase, OD_T("Window Style Created By 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 );
  AECDbDispPropsWindowPtr pOverrideModel =
    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 ) );
  }

  AECDbDispPropsWindowPtr pOverridePlan =
    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函数在第一堵墙中添加一个窗口:

OdDbObjectId idWindow01 = add_window( pDatabase, idWindowStyle, idWall1, 10, 10 );

// Inserts a window into a database using the specified window style.
// If idWall parameter is not null it also attaches the window to the wall.
// Returns Object ID of newly created window.
OdDbObjectId add_window( OdDbDatabasePtr pDatabase, const OdDbObjectId& idStyle, const OdDbObjectId& idWall, double dOffsetAlongX, double 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() );
}

add_window()函数类似于add_wall(), 但有一个区别:它使用锚对象。

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

该对象包含从墙的坐标系的原点到窗口的坐标系的原点沿X, Y和Z轴的偏移。当我们调用AttachWallAnchor()时, 将创建该对象的实例并将其添加到数据库中。墙壁本身不知道是否有窗户。创建锚点涉及另一个基本机制, 即关系图, 该关系图包含对象之间的关系:谁附加到谁, 谁包括谁以及谁拥有谁。如果修改墙, 则将通知关系图AECDbWall对象已更改。它将检查所有关系并触发相关对象的更新。在这种情况下, AECDbWindow将被更新。例如, 如果你移动墙, 则其中的窗口将自动移动, 因为它们将通过关系图得到通知。你可以访问关系图并请求特定对象的关系。实际上, 窗口知道它附加到哪个对象, 因为每个窗口都包含对创建的锚点的引用。

让我们看一下结果:

结果

我更改了墙壁的颜色, 以便你可以更清晰地看到窗户。 (代码创建了蓝色的墙, 我在写本文时只是选择了颜色。)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
    {
        esRectangular       =  0, esRound             =  1, esHalfRound         =  2, esQuarterRound      =  3, esOval              =  4, esArch              =  5, esTrapezoid         =  6, esGothic            =  7, esIsoscelesTriangle =  8, esRightTriangle     =  9, esPeakPentagon      = 10, esOctagon           = 11, esHexagon           = 12, esCustom            = 13
    };

我选择了AECDefs :: ewtGlider和AECDefs :: esRectangular。如你所见, 也可以使用许多其他形状。通过使用这些设置和其他设置, 你可以创建一个非常复杂的窗口类型, 在玻璃板上具有多个窗框和一个内部图案。最好的事情是, 你不必手动逐个创建它, 也不必以编程方式实施所有操作。你所需要做的就是为现有对象或样式设置一些参数。

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

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

OdDbObjectId 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 );
  OdDbObjectId idWindow04 = add_window( pDatabase, idWindowStyle, idWall1, 60, 60 );

  OdDbObjectId 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 );
  OdDbObjectId idWindow08 = add_window( pDatabase, idWindowStyle, idWall2, 60, 60 );

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

具有多个墙面显示的渲染形状。

我没有费心去完成代码, 但是你可以分别处理每个窗口:更改其打开百分比, 颜色等。如果更改样式, 则更改将立即应用于具有该样式的所有窗口。

在工程图中添加门

为了完成图片, 让我们添加一扇门。首先, 我们将为门板(带有窗孔的门扇)创建2D轮廓。然后, 我们将使用该配置文件创建样式。最后, 我们将能够创建具有该样式的门对象。另外, 我们可以使用默认样式。就像窗户(或其他任何开口)一样, 门也是用锚固定在墙上的。 add_profile_def, add_door_style和add_door清单。

// Inserts profile definition into a database.
// Returns Object ID of newly created profile definition.
OdDbObjectId add_profile_def( OdDbDatabasePtr pDatabase )
{
  OdDbObjectId idProfDef =
    AECDbProfileDef::CreateAECObject( pDatabase, OD_T("Profile Definition Created 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();

  // Forces the contour to be counter-clockwise.
  // So if the contour is already ccw this call is not needed.
  cProfile[ 0 ].makeCCW();

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

  cProfile[ 1 ].makeCCW( false );

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

  return( pProfileDefinition->objectId() );
}

// Inserts a door style into a database.
// Returns Object ID of newly created door style.
OdDbObjectId add_door_style( OdDbDatabasePtr pDatabase, const OdDbObjectId& idProfile )
{
  OdDbObjectId idDoorStyle =
    AECDbDoorStyle::CreateAECObject( pDatabase, OD_T("Door Style Created By 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 );
  AECDbDispPropsDoorPtr pOverrideModel =
    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 ) );
  }

  AECDbDispPropsDoorPtr pOverridePlan =
    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() );
}


// Inserts a door into a database using the specified door style.
// If idWall parameter is not null it also attaches the door to the wall.
// Returns Object ID of newly created door.
OdDbObjectId add_door( OdDbDatabasePtr pDatabase, const OdDbObjectId& idStyle, const OdDbObjectId& idWall, double dOffsetAlongX, double 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;

  OdDbObjectId 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() );

  // Initialize roof profile.
  // By default all edges of Roof Profile have single slope of 45 degrees.
  pRoof->GetProfile()->Init( cProfile );

  pRoof->SetThickness( 2 );

  //// Manually modify Roof Segments.
  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 );  
}

屋顶是根据2D轮廓创建的, 该轮廓的运行方向为逆时针方向。如果顺时针调用makeCCW()会更改遍历的方向。这样做很重要, 因为该算法需要一个配置文件, 该配置文件的运行方向为逆时针方向, 否则将无法正常运行。

轮廓与墙的中心线重合。然后, 我们需要为轮廓的每个线段设置倾斜角度, 屋顶中的面数, XY平面上方的每个面的顶点的高程(Z坐标)(SetFaceHeightByIndex)以及悬垂。 SetSegmentCount()仅适用于具有曲率的线段。此参数设置近似精度, 即近似弧的线段数。

这是我们的屋顶:

带屋顶的房子

屋顶设置很多, 因此你可以创建几乎任何形式的屋顶:山墙屋顶, 臀部屋顶, 臀部和山谷屋顶。每个面都是一个单独的RoofSlab对象, 你可以对其进行手动编辑。

在工程图中添加地板

现在, 我们至少需要添加一个非常基本的楼层/基础。为此, 我们可以使用slab对象。让我们编写add_slab函数。

void add_slab( OdDbDatabasePtr pDatabase )
{
  OdDbObjectId idStyle =
    AECDbSlabStyle::GetAECObject( 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 );
  AECDbDispPropsSlabPtr pOverrideModel =
    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 ) );
  }

  AECDbDispPropsSlabPlanPtr pOverridePlan =
    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平面上方的标高, 名称, 材料, 索引等参数的地板块。地板可以由具有不同设置的多个组件组成。例如, 如果它们在XY平面上方的高度不同, 则可以使用该样式的一个地板对象来渲染多层建筑物中的所有地板和天花板。

样式设置将应用于包含地板形状的特定对象。在这种情况下, 我们将创建一个平板, 并使用与墙的底部相同的轮廓来初始化其轮廓, 但在边缘处只有很小的偏移量。然后, 我们使用显示管理器重新定义地板不同组件的颜色。

最后, 我们创建了一个看起来像这样的房子:

成品房,包括地板

可以肯定的是, 让我们尝试将生成的DWG文件加载到Autodesk ACA中:

渲染的DWG文件

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

总结

使用Teigha, 我们创建了一个空数据库, 对其进行了初始化以处理建筑对象, 创建了一些常用的对象类型, 然后将它们成功保存到最新格式的DWG文件中。

当然, 我简化了许多事情, 并以粗略的方式描述了它们。但是, 本文的目的是演示Teigha Architecture的功能, 并为你提供Teigha的一般概念, 作为处理DWG文件和AutoCAD对象的替代解决方案。

赞(1)
未经允许不得转载:srcmini » 像2016年一样产生DWG:Teigha for Architecture

评论 抢沙发

评论前必须登录!