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

Andrew Markovich

February 28, 2019

This article is part of a series of articles about using DGN line styles in a .dwg file. For the previous article, see Part 3.

Creating point line style components

A point line style component provides the ability to set up symbol components to be vectorized for each stoke pattern dash (or gap) or for curve start/end points. Also it provides the ability to set up a symbol component to be vectorized on each polyline vertex. To set up symbols for stroke pattern dashes (and gaps), first create the stroke pattern component. In this example we use a simple stroke pattern component from the previous article:

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

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

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

This set of strokes and gaps generates the following line style:

image0

Now we can create a new point component (this code is typical for creating all DGN line style components) and attach the existing stroke pattern component to it:

// Create Point Component
OdDbLSPointComponentPtr pPtComponent = OdDbLSPointComponent::createObject();
pPtComponent->setComponentType(kLSPointComponent);
::oddbDgnLSIncrementUID(dgnLS_UID);
pPtComponent->setComponentUID(dgnLS_UID);

// Set up stroke component for point component
pPtComponent->setStrokeComponent(pLsComponent->objectId());

Note: The underlying stroke pattern component dashes are not vectorized when attached to a point component. To vectorize the stroke pattern component together with point component symbols, creation of a compound component is required. Compound components will be discussed in a future blog article.

To use point components, add an additional include file in the program source code:

#include "DgnLS/DbLSPointComponent.h"

Now, for example, connect the existing symbol component to the second segment in the stroke pattern component (this is the first gap in the stroke pattern):

// Set symbol for stroke component gaps
OdDbLSSymbolReference lsRef;
lsRef.setSymbolComponent(pSymComponent->objectId());
lsRef.setJustify(OdDbLSSymbolReference::kLSJustifyCenter);
pPtComponent->insertSymbolForStroke(&lsRef, 1);

The OdDbLSSymbolReference structure in this code represents a set of symbol parameters, for example, rotation, offsets, justification and some additional flags. In this example, attach the symbol component ID to the OdDbLSSymbolReference structure using the OdDbLSSymbolReference::setSymbolComponent method. Additionally, set the justification to display the symbol in the center of the stroke pattern gap. The justification parameter must be set to attach the symbol for a stroke. And finally, call the OdDbLSPointComponent::insertSymbolForStroke method to attach the symbol to the point component.

And attach the second symbol component to vectorize it on the ends of curves:

// Set symbol for line end
lsRef.setSymbolComponent(pSymComponent->objectId());
lsRef.setJustify(OdDbLSSymbolReference::kLSNoJustify);
pPtComponent->insertSymbolForVertex(&lsRef, OdDbLSSymbolReference::kLSAtEnd);

Finally, attach our point component to the DGN line style components dictionary:

// Add component into LineStyles dictionary
pDict->setAt(OD_T("DemoDgnPointComponent"), pPtComponent);

All of the next code was detailed in a previous article to create the line style definition component, attach it to OdDbLinetypeTableRecord and create a line entity for testing line style visualization. Here we use it again but with a small difference: attach to the line style definition a new point line style component instead of a stroke pattern component.

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

// Create Linetype Table Record
OdDbLinetypeTableRecordPtr pLtpRec = OdDbLinetypeTableRecord::createObject();
pLtpRec->setName(OD_T("DemoDgnLineStylePoint"));
pLtpRec->setComments(OD_T("Point 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 line style that results after executing the code looks like the following images (at different zoom levels):

image1image2image3image4

The block containing four helixes is displayed for each linetype (stroke) pattern repetition, and the block containing the polyline arrow is displayed at the end point of the OdDbLine entity.

Point component creation (full command source code)

#include "DbLinetypeTable.h"
#include "DbLinetypeTableRecord.h"
#include "DbLine.h"
#include "DbHelix.h"
#include "DbPolyline.h"
#include "DbBlockTable.h"
#include "DgnLS/DbLSDefinition.h"
#include "DgnLS/DbLSStrokePatternComponent.h"
#include "DgnLS/DbLSPointComponent.h"
#include "DgnLS/DbLSSymbolComponent.h"
#include "DgnLS/DbLSMisc.h"

static OdDbHelixPtr createHelix(OdGePoint3d location, double radius)
{
  OdDbHelixPtr helPtr = OdDbHelix::createObject();
  helPtr->setStartPoint(location - OdGeVector3d::kZAxis);
  helPtr->setConstrain(OdDbHelix::kTurns);
  helPtr->setBaseRadius(radius);
  helPtr->setTopRadius(radius / 5);
  helPtr->setTurns(4.0);
  helPtr->setAxisVector(OdGeVector3d::kZAxis);
  helPtr->createHelix();
  return helPtr;
}

static OdDbBlockTableRecordPtr createSegmentBlock()
{ const double fBlockScale = 1.0 / 1000.0;
  OdDbBlockTableRecordPtr btrPtr = OdDbBlockTableRecord::createObject();
  btrPtr->appendOdDbEntity(createHelix(OdGePoint3d(-fBlockScale, -fBlockScale, 0.0), fBlockScale));
  btrPtr->appendOdDbEntity(createHelix(OdGePoint3d( fBlockScale, -fBlockScale, 0.0), fBlockScale));
  btrPtr->appendOdDbEntity(createHelix(OdGePoint3d(-fBlockScale,  fBlockScale, 0.0), fBlockScale));
  btrPtr->appendOdDbEntity(createHelix(OdGePoint3d( fBlockScale,  fBlockScale, 0.0), fBlockScale));
  btrPtr->setName(OD_T("4helix"));
  return btrPtr;
}

static OdDbBlockTableRecordPtr createArrowBlock()
{
  const double fBlockScale = 1.0 / 1000.0;
  OdDbPolylinePtr pPl = OdDbPolyline::createObject();
  pPl->addVertexAt(0, OdGePoint2d(-fBlockScale, -fBlockScale));
  pPl->addVertexAt(1, OdGePoint2d(-fBlockScale,  fBlockScale));
  pPl->addVertexAt(2, OdGePoint2d( fBlockScale,  0.0));
  pPl->setClosed(true);
  OdDbBlockTableRecordPtr btrPtr = OdDbBlockTableRecord::createObject();
  btrPtr->appendOdDbEntity(pPl);
  btrPtr->setName(OD_T("arrow"));
  return btrPtr;
}

static OdDbObjectId appendBlock(OdDbDatabase *pDb, OdDbBlockTableRecordPtr btrPtr)
{
  return OdDbBlockTable::cast(pDb->getBlockTableId().openObject(OdDb::kForWrite))->add(btrPtr);
}

void _CreateDgnLineStyle_Point_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 pLsComponent = OdDbLSStrokePatternComponent::createObject();
  pLsComponent->setComponentType(kLSStrokePatternComponent);
  pLsComponent->setComponentUID(dgnLS_UID);

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

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

  // Create symbol component for first block
  OdDbLSSymbolComponentPtr pSymComponent = OdDbLSSymbolComponent::createObject();
  pSymComponent->setComponentType(kLSSymbolComponent);
  pSymComponent->setDescription(OD_T("DemoDgnSymbol1"));
  ::oddbDgnLSIncrementUID(dgnLS_UID);
  pSymComponent->setComponentUID(dgnLS_UID);

  // Attach block to symbol component
  pSymComponent->setBlockTableRecord(::appendBlock(pDb, ::createSegmentBlock()));

  // Add component into LineStyles dictionary
  pDict->setAt(OD_T("DemoDgnSymbolForSegment"), pSymComponent);

  // Create Point Component
  OdDbLSPointComponentPtr pPtComponent = OdDbLSPointComponent::createObject();
  pPtComponent->setComponentType(kLSPointComponent);
  ::oddbDgnLSIncrementUID(dgnLS_UID);
  pPtComponent->setComponentUID(dgnLS_UID);

  // Set up stroke component for point component
  pPtComponent->setStrokeComponent(pLsComponent->objectId());

  // Set symbol for stroke component gaps
  OdDbLSSymbolReference lsRef;
  lsRef.setSymbolComponent(pSymComponent->objectId());
  lsRef.setJustify(OdDbLSSymbolReference::kLSJustifyCenter);
  pPtComponent->insertSymbolForStroke(&lsRef, 1);

  // Create symbol component for second block
  pSymComponent = OdDbLSSymbolComponent::createObject();
  pSymComponent->setComponentType(kLSSymbolComponent);
  pSymComponent->setDescription(OD_T("DemoDgnSymbol2"));
  ::oddbDgnLSIncrementUID(dgnLS_UID);
  pSymComponent->setComponentUID(dgnLS_UID);

  // Attach block to symbol component
  pSymComponent->setBlockTableRecord(::appendBlock(pDb, ::createArrowBlock()));

  // Add component into LineStyles dictionary
  pDict->setAt(OD_T("DemoDgnSymbolForLineEnd"), pSymComponent);

  // Set symbol for line end
  lsRef.setSymbolComponent(pSymComponent->objectId());
  lsRef.setJustify(OdDbLSSymbolReference::kLSNoJustify);
  pPtComponent->insertSymbolForVertex(&lsRef, OdDbLSSymbolReference::kLSAtEnd);

  // Add component into LineStyles dictionary
  pDict->setAt(OD_T("DemoDgnPointComponent"), pPtComponent);

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

  // Create Linetype Table Record
  OdDbLinetypeTableRecordPtr pLtpRec = OdDbLinetypeTableRecord::createObject();
  pLtpRec->setName(OD_T("DemoDgnLineStylePoint"));
  pLtpRec->setComments(OD_T("Point 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);
}