Teigha for Architecture: First project

    In AutoCAD Architecture: First project and Introduction to ACA articles, I briefly talked about what AutoCAD Architecture (ACA) is, how it differs from ordinary AutoCAD, what objects it implements, and showed the simplest case of working with walls from a .NET plugin.

    In this article I will talk about the Teigha library - an alternative for working with dwg files and ACA objects. We will write a small example that creates a house from ACA objects and saves it in a dwg file. Then, try opening this file in AutoCAD Architecture and checking if these files are compatible with the original AutoCAD.




    Alternative to AutoCAD and ObjectARX


    For working with dwg and acad objects, in addition to AutoCAD and ObjectARX, there is another library: Teigha from the Open Design Alliance. If you want to programmatically work with the dwg format and AutoCAD objects, then the choice is, in fact, only between ObjectARX and Teigha. All third-party components and applications that can read and write dwg files are based on Teigha.
    Teigha is a set of libraries that allows you to read, write and manipulate objects of both regular AutoCAD and its derivatives such as ACA. This library also implements many auxiliary mechanisms for working with AutoCAD objects and rendering devices for rendering dwg databases.
    Below is the official information from the Open Design Alliance about the features of the library:
    • Supported DWG, DXF, and BDXF file formats:
      • Read support for files of version 2.5 through 2014.
      • Write support for files of version 12 through 2014.
    • Supported DGN file formats:
      • Read support for V7 and V8 DGN (including V8 XM and V8I).
      • Write support for V8 DGN.
      • Convert from V7 to V8.
    • Render drawing files using GDI, OpenGL, or DirectX with the ability to select entities.
    • Recover and repair damaged .dwg files.
    • Edit and manipulate CAD data programmatically, including:
      • Explode an entity into a set of simpler entities.
      • Apply a transformation to an entity.
      • Modify arbitrary properties of database objects.
      • Clone a database object.
      • Many more.
    • Export to SVG, PDF, DWF, BMP, STL, DAE (Collda).
    • Import DWF / DAE / DGN files into a .dwg database.
    • Support custom objects - members can create custom objects that are usable within any Teigha host application (compatible with .dwg files only).
    • Support external files such as font files (TTF / SHX), linetype files (LIN / RSC), and hatch pattern files (PAT).
    • Recompute dimension geometry from dimension settings.
    • Support transactions and undo / redo capabilities for .dwg file data.
    • Support partial .dwg file load capabilities, allowing fast access to small subsets of drawing entities (entities are loaded from disk only if they are explicitly accessed by the client).
    • Customize memory management allowing member applications to control memory allocation / deallocation.
    • Support .dwg file “round-trip” data. For example, when saving a 2007 .dwg file back to R14, 2007-specific properties are saved to the R14 file as xdata and then restored if the file is loaded back into a 2007-compatible application.
    • Support ACIS / Parasolid data internally, including rendering (wireframe and shaded) for embedded 3D solids and access to the underlying boundary representation data.
    • Implement custom commands.
    • Sample source code applications
    • Integrate with third-party components.



    In general, "the library supports a lot of things." To begin with, let's try to download and draw some dwg drawing using the standard sample from the Teigha package:


    OK, we read and dwg the dwg file. However, like the dwg file that I used for the title picture for the article.
    This standard sample is a windowed C ++ application and allows you to download, view and edit dwg files without AutoCAD. Another important feature: the AutoCAD and Teigha APIs are almost identical, so you can easily rewrite the existing ObjectARX plugin to work with a Teigha-based application.
    Most analogues of AutoCAD, such as BricsCAD, ZWCad, IntelliCAD, use Teigha to work with dwg format and acad objects.

    Features of AutoCAD Architecture Objects


    Now let's move on to architectural objects and work with them. To do this, briefly recall the main points:
    AutoCAD Architecture operates with special high-level objects designed for architectural design: walls, doors, windows, roofs and other structural parts. Viewport-dependent objects, i.e. they can render themselves differently depending on the direction of the camera. Objects are assigned a style. If you change the style, then all the objects of this style will change. Objects consist of components, each of which has its own visual settings: color, type of lines, material, scale (for example, a door in 3D consists of a frame, canvas, glass). For different display options, the object draws different geometry, so the number and settings of components are different for different representations.

    Teigha for Architecture (TA) Library


    To work with architectural objects, in addition to the ACA itself and its open API for creating plug-ins, you can use the Teigha for Architecture library from the Open Design Alliance.
    TA is a C ++ class library that implements all the basic ACA primitives, such as walls, windows, doors, roofs, beams, openings, etc. The library allows you to read these objects from dwg formats of all versions, write (convert) to the latest version of dwg; the library implements rendering of all primitives for various representations and configurations. Because aca-objects interact with each other, TA also provides auxiliary classes and ACA mechanisms - these are anchor, display manager, property sets, relation graph and other bindings.

    Getting started with TA API


    General words, in my opinion, are more than enough. Now let's see what Teigha physically is and try to write the first simple command. I will use the old VS 2005, but the libraries are multi-platform and the solution includes a generator of solutions for studios until 2015. Depending on the type of license, you may be able to access either the full code of the entire library, or the built binaries and header files.

    A set of TA-libraries will be something like this:


    In fact, these are ordinary Windows dlls (you can build them for other platforms: ios, linux, unix, etc.). Lib files go to them in a separate folder. In addition to TA, Teigha Core libraries will be needed, because TA is an extension over Core objects. Core implements the basic mechanisms and objects of a conventional auto cad.

    TA initialization


    For the initial initialization of the library, we need a class that performs platform-specific file operations.
    class MyServices : public ExSystemServices, public ExHostAppServices
    {
    protected:
      ODRX_USING_HEAP_OPERATORS(ExSystemServices);
    };
    

    The kit already has ready-made extensions for Windows: ExSystemServices and ExHostAppServices; In this case, we will have enough of them.
    Next, initialize the library and graphics subsystem.
    OdStaticRxObject svcs;
    odInitialize( &svcs );
    odgsInitialize();
    

    OdStaticRxObject adds logic addRef \ Release; The library stores a reference to the MyServices object and uses it for platform-specific operations.

    We initialize the TA libraries:
      // 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 and all the rest are tx modules (i.e. dll libraries) from the picture above. They are already linked using lib files, but this is not enough. You must initialize them as modules. What does it mean? At run time, a dictionary of loaded classes exists in memory. This dictionary is used for the mechanism of castes of pointers between different types of TA-objects and for creating instances of TA-classes themselves through a centralized mechanism of pseudo-constructors.
    For example, when executing the command :: odrxDynamicLinker () -> loadApp (OD_T ("AecArchBase")), the AECArchBase :: initApp () function will be called inside the framework. Schematically, initApp () will register in the global dictionary the classes that are implemented in this library by calling the static rxInit () function for each:
    …
    AECDbSpaceBoundary::rxInit();
    AECDbStair::rxInit();
    AECDbWall::rxInit();
    AECDbZone::rxInit();
    …
    

    After that, the mechanism for creating objects will work and it will be possible to create, for example, a wall by calling AECDbWallPtr pWall = AECDbWall :: CreateAECObject (). Otherwise, an attempt to create a TA class object will throw an exception.

    Create an empty dwg base by calling
    OdDbDatabasePtr pDatabase = svcs.createDatabase();
    

    This is a central object; it is an object database that is saved and loaded from a dwg file. We will add all created architectural objects to it. Upon completion, we will save this database in a dwg file by calling
    OdWrFileBuf cBuffer( strFilename );
    pDatabase->writeFile( &cBuffer, OdDb::kDwg, OdDb::kDHL_CURRENT );
    

    Next, initialize the loaded libraries and display manager a little more
    AECArchDACHBaseDatabase( pDatabase ).Init();
    AECScheduleDatabase( pDatabase ).Init();
    AECStructureBaseDatabase( pDatabase ).Init();
    init_display_system( pDatabase );
    

    An AEC dictionary is created in the database with default settings for units of measure for length, area, volume, angles, print settings and display representations implemented in these modules are registered. About the display manager, display representations and related mechanisms I will write a separate post.

    This initialization is completed. If you skip some steps, the result may be different: either you will not create objects, or they will not be drawn (see a blank screen), or you will get some more glitches, depending on which step you skipped.

    At the moment, our full code looks like this:
    Program code
    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 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;
    }
    


    I added the zoom extents command so that when you open the created file, we immediately see the objects that added the symmetric de-initialization of the library there. For simplicity, I removed the error checking and try \ catch constructs around the main actions.
    Now the program will create an empty dwg file that can be opened and viewed by AutoCAD.

    Work with objects


    Adding walls to the drawing
    To demonstrate working with TA classes, I want to create a house consisting of a basement, walls, windows, doors and roofs. Let's start with the walls.
    First, add a wall to our drawing. To create the wall itself, you must first create a style for it. Let's write the add_wall_style function:
    Add_wall_style code
    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() );
    }
    


    This function creates an AECDbWallStyle wall style object, sets some settings for it, then calls the display manager and changes colors for plan display representation (2d top view) and model display representation (3d view).
    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 ) );
      }
    

    In this case, we set yellow for the wall in the 3D view. It looks complicated, but there are reasons for this: this is how the display representations and display manager mechanism in ACA works. It is flexible and has many features, but its logic is not immediately obvious and needs some study.

    OdDbObjectId - runtime reference
    OdDbObjectId is a class with which base objects refer to each other at run time. Inside, it stores a pointer to a real object in memory. Using OdDbObjectId :: openObject (), you can get a pointer to the object that ObjectId is associated with. The point is that until the object is accessed by this id, it may not be loaded into memory and the internal pointer OdDbObjectId may be NULL. When we specifically turned on this id for reading or writing, then the framework will create an instance of the real object and fill its fields with data from the database, and the internal OdDbObjectId pointer will receive the address of the created object. openObject () returns this pointer.
    Such a mechanism allows partial loading of a large database (dwg file). In this case, OdDbObjectId will exist for each object, but only those objects that are explicitly accessed will be actually created in memory.
    If you want the link to the object to be kept between starts, then you need to use OdDbHandle.

    For example, the add_wall_style function returned idWallStyle. In this case, the style was just explicitly created by calling AECDbWallStyle :: CreateAECObject () and idWallStyle stores the pointer to the real object in memory inside. To get write access to the style object, you need to perform the operation
    AECDbWallStylePtr pWallStyle = idResult.openObject( OdDb::kForWrite );
    

    openObject () will return a real pointer to the object and it can be used.

    The library uses OdSmartPtr smartpoints instead of regular C ++ pointers
    typedef OdSmartPtr AECDbWallStylePtr
    

    The destructor of such a smartpoint notifies the framework about the closure of the object, which can cause the recount of related objects, sending alerts, etc.

    Now add the wall by calling:
    OdDbObjectId idWall1 = add_wall( pDatabase, idWallStyle, OdGePoint2d( 0,     0 ), OdGePoint2d(   0, 110 ) );
    

    Add_wall code
    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() );
    }
    


    As you can see, add_wall does nothing special. It creates an AECDbWall object with the style that we created a little earlier. The AECDbWall object is added to the model space of the database - a special dictionary that stores all the objects that are rendered when rendering the database (this is a simplification).
    Next, the starting point, end point and curvature are set to the wall. Those. the wall can be not only straight, but also convex.

    If everything was done correctly, then we will get a dwg file with one yellow rectangular wall. In this case, I am viewing the file using the sample from the Teigha package, but in ACA it will be rendered in the same way.

    True, I manually deployed the camera in a 3D view. By default, you will have a top view.

    Now, let's try to add as many as 4 walls, with one convex:
    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 );
    


    We got a certain rudiment of a future house:

    As you can see, the walls were not just painted with separate objects, but a smooth transition was built between them at the points of contact with each other. This is one of the TA automatic features called cleanup walls.

    Full code at the moment
    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() );
    }
    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() );
    }
    int wmain(int argc, wchar_t* argv[])
    { 
      // Initialize TD with system services.
      // And create single instance of hostapp services
      // for TD database creation.
      OdStaticRxObject 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 );
     OdDbObjectId idWallStyle = add_wall_style( pDatabase );
      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 );
      // 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;
    }
    



    Adding windows to a drawing
    Now we add windows to our house. With windows, the logic is similar to doors: it is necessary to create the style of the windows that we want to add to the drawing, and then add window objects of this style.
    OdDbObjectId idWindowStyle =  add_window_style( pDatabase );
    

    Code add_window_style
    
    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() );
    }
    


    As you can see from the code, an AECDbWindowStyle object is created and added to the database. Next, the style is set to some settings (although you can use the default ones), and then the colors of several components are redefined for 2D and 3D representation. The components in this case are the physical parts of the window: glass, frame, sash.

    Add a window to the first wall with the add_window function:
    OdDbObjectId idWindow01 = add_window( pDatabase, idWindowStyle, idWall1, 10, 10 );
    

    Add_window function code
    
    // 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() );
    }
    


    The add_window () function is similar to add_wall () with one difference: the anchor object is used here.
    We create an AECDbWindow object and place it in the model space of the base. Then we expose some settings to this particular instance of AECDbWindow.
    After that, we insert the window into the wall. A wall and window are connected by a special object derived from AECDbAnchorEntToCurve. It contains the indentation along the x, y, z axes from the beginning of the coordinate system of the wall to the beginning of the coordinate system of the window. When calling AttachWallAnchor (), an instance of this object is created and placed in the database. The wall itself does not directly know whether the windows are inserted into it or not. Anchor creation affects another basic mechanism - relation graph. Relation graph contains relationships between objects - who is attached to whom, who belongs to whom, who owns whom. When modifying the wall, the relation graph will receive a message stating that the AECDbWall object has changed, will go through all the dependencies and cause the update of related objects (in this case AECDbWindow). So, if we moved the wall, then the windows will move along with it, because they’ll receive an alert from relation graph. You can access this graph and request dependencies for a particular object. The window, in principle, knows to whom it is attached, as it stores a link to the created anchor.

    We look at what happened:


    I specifically changed the color of the walls so that the window is better visible. In your code, the walls should be blue right away, I just picked the colors as I wrote the article.
    There are lots of predefined styles and types of windows in TA that are specified through enumerations:
    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
        };
    

    We chose AECDefs :: ewtGlider and AECDefs :: esRectangular, but, as you can see, there are many forms. Using other settings, you can create a very complex type of window with an internal pattern on the glass and many sashes. And all this does not need to be drawn manually or implemented programmatically, you only need to set a few settings for an existing object or style.
    In general, all TA objects are complex and have a ton of settings. This gives quite ample opportunities right out of the box.

    Add windows to all straight walls:
    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 );
    



    I did not begin to add the code, but each window can be controlled separately: change the percentage of its opening, color, etc. And if you change their style, then this change will affect all of them at once.

    Adding doors to the drawing
    To complete the picture, add a door. For the door, create a 2D profile for the canvas (leaf with a hole-window), then create a style with such a profile, and then we can create door objects of this style. But you can use the default styles. Doors, like windows (and all other openings) are joined to the walls using anchor.
    Code 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() );
    }
    


    And add in 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  );
    

    There is one new point here: we open the wall for reading and take its length to calculate the indent.

    As a result, our door was inserted into a round wall:


    Insert another window into a round wall:
    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 );
    


    As a result, we got a very holey house without a roof and floor:


    Adding a roof to the drawing
    Let's write the add_roof () function
    Roof function code
    
    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 );  
    }
    


    The roof is created on the basis of a two-dimensional profile, the bypass direction of which is counterclockwise. A call to makeCCW () just converts the bypass direction if it was the opposite. This is important, because the algorithm expects to enter just such a profile and will not work otherwise.
    Our profile coincides with the central line of the walls. Further, for each section of the profile, the slope of the roof slope is set, the number of faces to which the slope will be presented, the rise of the slope beginning in Z relative to the OXY plane (SetFaceHeightByIndex), and the roof overhang (overhang). SetSegmentCount () only works for segments that have curvature. This value sets the approximation accuracy - how many straight segments will be used to approximate the semicircular segment.

    It turned out such a roof:


    There are many variations of the roof settings and you can create a roof of almost any shape - gable, multi-slope, tent and so on. Each slope is a separate RoofSlab object that can be edited manually.

    Adding overlap to the drawing It
    remains to add at least a small imitation of the floor / foundation. To do this, use the slab object (overlap). Let's write the add_slab function
    Add_slab function code
    
    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 ) );
      }
    }
    


    In this case, we create a new overlap style and add components to it. A component is a piece of overlap and contains such parameters as thickness, rise above OXY, name, material, index, etc. Overlapping can contain several components that differ in their settings. For example, if they have a different margin from OXY, then one object-overlap of this style can draw all the floors and ceilings in a multi-story building.
    Style settings are applied to a specific object that stores the shape of this overlap. In this case, we create a slab, and initialize its profile with the same outline as the bottom of the walls, with only a slight indentation along the edges.
    The next step is working with the display manager to override the colors of different overlap components.

    In the final, our house will look like this:


    For the test, let's try to upload the resulting dwg file to Autodesk ACA:

    Here is our house, uploaded to Autocad Architecture. It looks even better.

    Full code
    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() );
    }
    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") );
      /*AECGe::Contour2D cContour;
      cContour.appendVertex( OdGePoint2d( 0,                      pWall->GetHeight()       ) );
      cContour.appendVertex( OdGePoint2d( pWall->GetLength() / 2, pWall->GetHeight() * 1.5 ) );
      cContour.appendVertex( OdGePoint2d( pWall->GetLength(),     pWall->GetHeight()       ) );
      AECWallCutLineSubPtr pTop = pWall->CreateTopCutLine();
      pTop->SetRawCutLine( cContour );*/
      return( pWall->objectId() );
    }
    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() );
    }
    // 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() );
    }
    // 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() );
    }
    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 );  
    }
    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 ) );
      }
    }
    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 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 );
      OdDbObjectId idWallStyle = add_wall_style( pDatabase );
      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 );
      AECDbWallPtr ptWall = AECDbWall::cast( idWall1.openObject( OdDb::kForRead ) ); 
      OdDbObjectId idWindowStyle = add_window_style( pDatabase );
      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 );
      OdDbObjectId idProfile =
        add_profile_def( pDatabase );
      OdDbObjectId idDoorStyle =
        add_door_style( pDatabase, idProfile );
      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(pDatabase);
      add_slab(pDatabase);
      // 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;
    }
    



    Total


    Using Teigha, we created an empty database, initialized it for working with architectural objects, created some of the most used types of objects, and successfully saved all this to a dwg file of the latest format.
    Of course, I simplified and described many aspects very superficially, but the purpose of this article is simply to demonstrate the capabilities of TA and give a general idea of ​​Teigha as an alternative opportunity for working with dwg files and AutoCAD objects.

    Also popular now: