Using .dgn Line Styles in a .dwg File (Part 1 of 4)

Andrew Markovich

December 20, 2018

Databases for .dwg support Autodesk® AutoCAD® linetypes, but these linetypes can include only dashes, dots and text in a linetype pattern. DGN line styles are much more powerful because they also support:

  • Embedding blocks inside patterns.
  • Drawing multiple patterns with offset (similar to OdDbMLine entity).
  • Many additional flags to enhance line style dash behavior.

ODA SDK has classes and functions to embed DGN line styles inside a .dwg database. Additionally, .dwg files saved by ODA SDK can be vectorized similarly in AutoCAD which has supported vectorizing DGN line styles since version 2012. One exception: DGN line styles can be saved only in .dwg file format and are lost if saved in .dxf file format.

Setting up the DGN line style module

All DGN line style embedding functionality is provided by the AcDgnLS.tx module. This module must be available to vectorize DGN line styles. For DGN line style creation, this module must be loaded and initialized. For dynamic library configuration, before using AcDgnLS.tx module functionality, it must be initialized:

::odrxDynamicLinker()->loadModule(OdDgnLSModuleName);

For working with DGN line style creation, applications must additionally link with the AcDgnLS.lib static library (AcDgnLS.a for non-Windows platforms).

For statically linked applications, the AcDgnLS.tx module must be registered in the static modules map:

 /************************************************************************/
/* Define a module map for statically linked modules                    */
/************************************************************************/
#if !defined(_TOOLKIT_IN_DLL_) || defined(__MWERKS__)

ODRX_DECLARE_STATIC_MODULE_ENTRY_POINT(OdDgnLSModule);

ODRX_BEGIN_STATIC_MODULE_MAP()
  ODRX_DEFINE_STATIC_APPMODULE(OdDgnLSModuleName, OdDgnLSModule)
ODRX_END_STATIC_MODULE_MAP()

#endif

Creating a stroke pattern component

Stroke patterns consist of a set of dashes (dots) and gaps (spaces). This is the simplest component for DGN line style creation. Each dash can have its own specific parameters. To simplify our examples, we will invoke only part of the parameters.

For components creation we need unique GUIDs. The AcDgnLS.tx module provides some functions to simplify working with them. These functions are available in a separate header file:

#include "DgnLS/DbLSMisc.h"

To initialize a GUID, call the oddbDgnLSInitializeImportUID function:

OdUInt8 dgnLS_UID[16];
::oddbDgnLSInitializeImportUID(dgnLS_UID);

Include the header file that contains the OdDbLSStrokePatternComponent class declaration:

#include "DgnLS/DbLSStrokePatternComponent.h"

Now we can create the stroke pattern component:


// Create Stroke LineStyle Component
OdDbLSStrokePatternComponentPtr pComponent = OdDbLSStrokePatternComponent::createObject();
pComponent->setComponentType(kLSStrokePatternComponent);
pComponent->setComponentUID(dgnLS_UID);

And add strokes and gaps inside the stroke pattern component:

// Append strokes
pComponent->insertStroke(0.008)->setDash();
pComponent->insertStroke(0.003)->setGap();
pComponent->insertStroke(0.002)->setDash();
pComponent->insertStroke(0.003)->setGap();

These set of strokes and gaps give us the following line style:

image1

For example, we can modify the first dash and specify additional parameters, such as width:

pComponent->insertStroke(0.008, 0.003, 0.002, OdDbLSStroke::kLSWidthFull)->setDash();

The following picture shows the result of this change:

image2

And finally, for example, we can modify behavior of dash end caps:

pComponent->insertStroke(0.008, 0.003, 0.002, OdDbLSStroke::kLSWidthFull, OdDbLSStroke::kLSCapsArc)->setDash();

And get the following line style:

image3

Of course dashes have more parameters, which we can modify, but describing them is outside the scope of this article.

After creating and configuring the stroke pattern component, we can add it to the DGN line styles dictionary in a .dwg database:

// Add component into LineStyles dictionary
OdDbDictionaryPtr pDict = ::oddbDgnLSGetComponentsDictionary(pDb, OdDb::kForWrite, true);
pDict->setAt(OD_T("DemoDgnLineStyleStrokeComponent"), pComponent);

Attaching a line style to a .dwg file (OdDbLinetypeTableRecord

To apply a DGN line style to .dwg database entities, attach it to a native .dwg database object that describes linetypes in the database — this is OdDbLinetypeTableRecord.

First, include the header file that contains the OdDbLSDefinition class declaration:

#include "DgnLS/DbLSDefinition.h"

Now we can create OdDbLSDefinition, which will be attached to the linetype table record:

// Create LineStyle Definition
OdDbLSDefinitionPtr pLSDef = OdDbLSDefinition::createObject();
pLSDef->setComponent(pComponent->objectId());
pLSDef->setComponentUID(pComponent->componentUID());

A line style definition refers to an existing line style component. Of course it contains additional parameters, like scaling and so on, but we will not change them here for simplicity. Now we can create a .dwg database linetype:

// Create Linetype Table Record
OdDbLinetypeTableRecordPtr pLtpRec = OdDbLinetypeTableRecord::createObject();
pLtpRec->setName(OD_T("DemoDgnLineStyleStroke"));
pLtpRec->setComments(OD_T("Stroke component"));
OdDbObjectId lsId = OdDbLinetypeTable::cast(pDb->getLinetypeTableId().safeOpenObject(OdDb::kForWrite))->add(pLtpRec);

And attach our line style definition to the Linetype table record:

// Attach LineStyle Definition to Linetype Table Record
pLtpRec->createExtensionDictionary();
OdDbDictionaryPtr pLtpDict = OdDbDictionary::cast(pLtpRec->extensionDictionary().openObject(OdDb::kForWrite));
pLtpDict->setAt(::oddbDgnLSGetDefinitionKeyName(), pLSDef);

Now we can use the created linetype with any .dwg database entity, as with standard linetypes. To finalize our example and show the result, populate a new line using this linetype in the database:

// Create line for LineStyle testing
OdDbLinePtr pLine = OdDbLine::createObject();
pLine->setDatabaseDefaults(pDb);
pLine->setLinetype(lsId);
pLine->setEndPoint(OdGePoint3d(1.0, 0.0, 0.0));
OdDbBlockTableRecord::cast(pDb->getActiveLayoutBTRId().safeOpenObject(OdDb::kForWrite))->appendOdDbEntity(pLine);

Stroke pattern creation (full command source code)

#include "DbLinetypeTable.h"
#include "DbLinetypeTableRecord.h"
#include "DbLine.h"
#include "DgnLS/DbLSDefinition.h"
#include "DgnLS/DbLSStrokePatternComponent.h"
#include "DgnLS/DbLSMisc.h"

void _CreateDgnLineStyle_Stroke_func(OdEdCommandContext* pCmdCtx)
{
  OdDbDatabasePtr pDb = pCmdCtx->baseDatabase();
  OdDbUserIOPtr pIO = pCmdCtx->userIO();

  ::odrxDynamicLinker()->loadModule(OdDgnLSModuleName);

  // We need GUID for DgnLS objects
  OdUInt8 dgnLS_UID[16];
  ::oddbDgnLSInitializeImportUID(dgnLS_UID);

  // Create Stroke LineStyle Component
  OdDbLSStrokePatternComponentPtr pComponent = OdDbLSStrokePatternComponent::createObject();
  pComponent->setComponentType(kLSStrokePatternComponent);
  pComponent->setComponentUID(dgnLS_UID);

  // Append strokes
  pComponent->insertStroke(0.008, 0.003, 0.002, OdDbLSStroke::kLSWidthFull, OdDbLSStroke::kLSCapsArc)->setDash();
  pComponent->insertStroke(0.003)->setGap();
  pComponent->insertStroke(0.002)->setDash();
  pComponent->insertStroke(0.003)->setGap();

  // Add component into LineStyles dictionary
  OdDbDictionaryPtr pDict = ::oddbDgnLSGetComponentsDictionary(pDb, OdDb::kForWrite, true);
  pDict->setAt(OD_T("DemoDgnLineStyleStrokeComponent"), pComponent);

  // Create LineStyle Definition
  OdDbLSDefinitionPtr pLSDef = OdDbLSDefinition::createObject();
  pLSDef->setComponent(pComponent->objectId());
  pLSDef->setComponentUID(pComponent->componentUID());

  // Create Linetype Table Record
  OdDbLinetypeTableRecordPtr pLtpRec = OdDbLinetypeTableRecord::createObject();
  pLtpRec->setName(OD_T("DemoDgnLineStyleStroke"));
  pLtpRec->setComments(OD_T("Stroke component"));
  OdDbObjectId lsId = OdDbLinetypeTable::cast(pDb->getLinetypeTableId().safeOpenObject(OdDb::kForWrite))->add(pLtpRec);
  // Attach LineStyle Definition to Linetype Table Record
  pLtpRec->createExtensionDictionary();
  OdDbDictionaryPtr pLtpDict = OdDbDictionary::cast(pLtpRec->extensionDictionary().openObject(OdDb::kForWrite));
  pLtpDict->setAt(::oddbDgnLSGetDefinitionKeyName(), pLSDef);

  // Create line for LineStyle testing
  OdDbLinePtr pLine = OdDbLine::createObject();
  pLine->setDatabaseDefaults(pDb);
  pLine->setLinetype(lsId);
  pLine->setEndPoint(OdGePoint3d(1.0, 0.0, 0.0));
  OdDbBlockTableRecord::cast(pDb->getActiveLayoutBTRId().safeOpenObject(OdDb::kForWrite))->appendOdDbEntity(pLine);
}

The next article in this series describes working with internal line style components.