Working with Applications

Creating Custom Applications

Teigha provides the facilities for creating dynamically loaded custom modules and custom objects derived from Teigha classes.

Creating a Custom Module

To create a custom module, create a subclass of OdRxModule as in the following example:

class CustomObjectsModule : public OdRxModule { protected: void initApp() { // register the custom object types defined in this module ExCustObject::rxInit(); ExCustEntity::rxInit(); ExSphere::rxInit(); // custom commands can be registered here } void uninitApp() { // unregister the custom object types defined in this module ExSphere::rxUninit(); ExCustEntity::rxUninit(); ExCustObject::rxUninit(); // remove custom commands here } }; // macro that defines the entry point for this library ODRX_DEFINE_DYNAMIC_MODULE(CustomObjectsModule);

A custom module should contain exactly 1 subclass of OdRxModule, but can define any number of custom objects.

Naming Convention for Custom Modules

The naming rule for the shared library containing the subclass of OdRxModule is < application name >.drx, where < module name > is the same module name that is used to define the custom object classes in this context.

Creating Client Applications

Smart Pointers

DWGdirect clients should use the supplied smart pointer classes when accessing database objects. In the above example, OdDbDatabasePtr is a smart pointer class that wraps a reference to an OdDbDatabase object. All database classes have corresponding smart pointer classes with the "Ptr" suffix attached. For example, a smart pointer to an OdDbCircle is an OdDbCirclePtr. When used properly, the DWGdirect smart pointer classes guarantee the proper management of heap objects both, in normal execution cases as well as in cases where exceptions transfer control out of the current scope.

Accessing Database Objects

This section describes how to retrieve various types of data from a database.

Accessing System Header Variables

You can access header variables directly from an instance of OdDbDatabase.

Example:

pDb->setLTSCALE(1.2); std::cout << " LTSCALE: " << pDb->getLTSCALE() << std::endl;

In the general case, all header variable access functions are in the form OdDbDatabase::getVARNAME() and OdDbDatabase::setVARNAME().

Accessing Table Entries

To retrieve table data, open the table object and then use an iterator to step through the table entries. The following function iterates through the Layers table and prints the name of each layer, assuming pDb is a valid database and os is an output stream.

void DbDumper::dumpLayers(OdDbDatabase* pDb, STD(ostream) & os) { // Layer table smart pointer, opened for read. OdDbLayerTablePtr pLayers = pDb->getLayerTableId().safeOpenObject(); os << STD(endl) << pLayers->desc()->name() << STD(endl); // Get a new layer table iterator (as a smart pointer) OdDbSymbolTableIteratorPtr pIter = pLayers->newIterator(); for (pIter->start(); !pIter->done(); pIter->step()) { // Layer Record smart pointer, opened for read. OdDbLayerTableRecordPtr pLayer = pIter->getRecordId().safeOpenObject(); // Read access to the layer record data: os << " " << pLayer->getName().c_str() << " <" << pLayer->desc()->name() << ">"; os << ", " << (pLayer->isOff() ? "Off" : "On"); os << ", " << (pLayer->isLocked() ? "Locked" : "Unlocked"); os << ", " << (pLayer->isDependent() ? "Dep. on XRef" : "Not dep. on XRef"); os << STD(endl); } } // end DbDumper::dumpLayers

In this example, the use of smart pointers eliminates the need to explicitly close or delete the pointers returned by the calls to OdDbObjectId::safeOpenObject(). Entries from other tables can be obtained in a similar manner.

Accessing Block Entities

Entities are owned by blocks (OdDbBlockTableRecord). Therefore, you must open the block first to access its entities. The following code iterates through all entities in all blocks of a drawing and calls the dumpEntity() function for each entity:

Accessing Objects in the Named Objects Dictionary

The starting point for accessing objects in a database is the main dictionary object. The following example prints the object hierarchy starting from the main dictionary and recurses into any found subdictionaries (it does not traverse the entire object hierarchy).

The above object dump can be initiated from the main dictionary:

To retrive items in a dictionary by item name, use the OdDbDictionary::getAt() function. The following code segment retrieves the entry called "ACAD_LAYOUT" from the specified dictionary, if it exists:

Note: The layout dictionary ID can be accessed directly from a database by calling OdDbDatabase::getLayoutDictionaryId().

Creating Objects

The base class for all objects that require runtime type identification is OdRxObject - the base class for all database objects and many other auxiliary classes that Teigha uses. You can create instances of OdRxObject by calling the static OdRxClass::createObject() function.

Example:

The following code segment creates a dictionary object:

An object created using this method does not belong to a database when it is first created. The client must explicitly add it to a database. The following code segment creates a new dictionary, an xrecord, and adds the new objects the main dictionary. As the following example demonstrates, an OdDbObjectID is assigned to an object when the object is added to a database and this OdDbObjectID is normally returned by the function that added the object (OdDbDictionary::setAt in this case).

To add an object to a database, perform the following steps:

  1. Open the parent object for writing. The parent is the object that will own the newly created object within the database hierarchy.

  2. Create the new object.

  3. Set the minimal number of properties required to add the object to the database:
    Table entries must have a name before adding them to the database.
    Entities can be added to a database without setting any properties.
    Objects can be added to a database without setting any properties.

  4. Add the new object to its parent, getting back the new Object ID for this object. Adding the object to a database does not change its open status. The object remains open for writing after it was added to a database.

  5. Set the remaining properties for the new object.
    Note: Some properties can only be set on a database resident object. As a general rule, it is easier to set all remaining properties (other than those specified above) after the object was added to the database.

  6. Close the object and its parent. This happens implicitly when the smart pointers used to open and create the objects are destroyed. Alternatively, you can explicitly destroy them by calling the OdSmartPtr::release() method on the appropriate smart pointer objects.

Creating New Table Objects

To add table objects to a database, do the following:

  1. Open the table to which the new object will be added, in OdDb::kForWrite mode.

  2. Create the new table entry.

  3. Set the name of the entry, and any other desired properties.

  4. Add the new entry to the open table object.

  5. Close the table entry and table objects (this is done automatically when the smart pointers holding references to these objects go out of scope).

Example:

The following function adds a new layer to a database:

Creating New Entities

Entities are owned by OdDbBlockTableRecord objects. To create an entity and add it to a block, do the following:

  • Open the OdDbBlockTableRecord to which you want to add the new entity in OdDb::kForWrite mode.

  • Create the new entity.

  • Add the new entity to the open OdDbBlockTableRecord object.

  • If necessary, set the properties for the newly created entity.

  • Close the OdDbBlockTableRecord object and the new entity. This is automatically done when the smart pointers holding references to these objects go out of scope.

The following example adds an OdDbBlockReference entity to a specified block:

Other entities, such as circles and lines can be added in a similar manner.

Advanced Data Extraction

This section describes advanced functionality and techniques for extracting data from a DWG/DXF file using Teigha.

Dispatching on Entity Type

Applications that process entity data from a database require functionalities to identify each entity by its type and perform type-specific processing on the entity. Teigha uses a mechanism called protocol extension to provide this functionality. Protocol extension allows a client application to create a new class and associate a class instance with a particular class of database objects. The new class instance may be retrieved from a generalization of the class with which it was registered, providing an effective means of dispatching by type.

As an example, consider the protocol extension classes defined in ExProtocolExtension.h and ExProtocolExtension.cpp. The purpose of these classes is to dump the contents of the entities in a database, based on entity type. The implementation can be split into the following steps.

Create the Parent Dispatching Class

The following class is the parent class for our entity dumpers:

This class MUST be derived from OdRxObject.

Create One or More Child Dispatching Classes

The following code defines a class for dumping OdDb2dPolyline entities. It overrides the OdDbEntity_Dumper::dump() function to dump the data specific to this entity.

These classes MUST be derived from the parent dispatching class, in this case OdDbEntity_Dumper.

Register the Custom Dispatching Classes

The following function registers custom dumper classes for a number of entity types:

The Dumpers::addXs() function does the following:

All dumpers are registered for the OdDbEntity_Dumper class, so that the dumpers associated with a particular instance of OdDbEntity can be retrieved in a consistent manner by retrieving the dumper registered with OdDbEntity_Dumper.

Retrieve and Call the Registered Dispatching Class

The following function retrieves the custom dispatching class associated with a particular OdDbEntity instance and calls the virtual dump() method to display the contents of this entity.

The custom dispatching object registered for this object type is retrieved by a simple assignment.

Extracting Geometry from Unsupported Entity Types

The OdDbEntity class defines a virtual OdDbEntity::worldDraw() function, which is used for vectorization. Each class derived from OdDbEntity overrides this function in order to perform custom vectorization. This function provides a convenient method for extracting the geometric representation of an entity in a generic manner.

For example, a client application can extract the vector data associated with an unsupported OdDbEntity descendant by deriving a new class from OdGiWorldGeometry (see the OdGiWorldGeometryDumper class from OdReadEx as an example) and then passing an instance of this class to OdDbEntity::worldDraw() to receive the vector geometry and attributes from the entity. For the complete implemention of this class, see GiWorldDrawDumper.h and GiWorldDrawDumper.cpp within the OdReadEx sample folder.

The following code is a protocol extension class for OdDbEntity that uses the sample OdGiWorldGeometryDumper class to print the vector information contained in an OdEntity:

This method can be used to extract the geometry data from any OdDbEntity type.