Reference Models and Model References: two terms commonly found in the Be Communities MicroStation Programming Forums. What do those names mean for an MDL programmer? Questions similar to this appear on the Bentley Discussion Groups. These questions, or something like them, appeared in the MDL discussion group.
Q Questions about DGN Models and Reference Models pop up occasionally …
A A MicroStation V8 DGN file is a container for one or more models. Each model is a container for a 2D or 3D drawing. A file can contain a mixture of 2D and 3D models. In other words, there is no such thing as a 2D or 3D DGN file, because the file has no dimensionality: it's just a container for some models.
A DGN model is represented in MDL by a pointer to an anonymous struct: DgnModelRefP.
A special model, the active model,
contains the data that the MicroStation user is currently using.
You can always get a DgnModelRefP to the active model using macro ACTIVEMODEL,
which is nothing more than a shortcut to MDL function mdlModelRef_getActive.
A common MicroStation idiom is the use of reference attachments.
A reference attachment is a DGN model that is read-only and visible in the same coordinate system
as the active model.
A DGN model may have zero or many attachments.
Each attachment is a DgnModelRefP.
However, the DgnModelRefP of a reference is similar but different to the
DgnModelRefP of the active model and other models in the active DGN file.
The DgnModelRefP representing a model in
the active DGN file and the DgnModelRefP representing a reference attachment
are similar.
Unsurpringly, this similarity causes confusion at times.
A common task for MDL developers is to iterate reference attachments.
A less common task is to iterate DGN models.
This article attempts to clarify both procedures.
A portion of clarity is provided by careful nomenclature. In this article …
DgnModelRefP that belongs to
the active DGN file
DgnModelRefP that belongs to
the reference model collection owned by a particular DGN model
It you want to iterate the models contained in a DGN file, you have several ways …
mdlModelIterator_api
mdlDgnFileObj_traverseRootModelRefs
mdlDgnFileObj_api
Yongan Fu,
a regular contributor to the
Be Communities,
made these comments about the three APIs:
mdlDgnFileObj_traverseRootModelRefs() can only traverse loaded models but the other two can traverse all models include loaded and unloaded.
mdlDgnFileObj_api needs a callback function but mdlModelIterator_api doesn't.
The above is just my point of view.
In this example we use the mdlModelIterator_api …
DgnIndexIteratorP iterator = mdlModelIterator_create (mdlModelRef_getDgnFile (ACTIVEMODEL));
DgnIndexItemP item = mdlModelIterator_getFirst (iterator);
while (item)
{
// Do something with item
item = mdlModelIterator_getNext (iterator);
}
mdlModelIterator_free (iterator);
What kind of action can you take on each model?
You may want to get data about each model, in which case mdlModelItem_getData is useful.
However, if you want to make a model the active model so that a user can work with it,
you have to do a little more …
DgnIndexIteratorP iterator = mdlModelIterator_create (mdlModelRef_getDgnFile (ACTIVEMODEL));
DgnIndexItemP item = mdlModelIterator_getFirst (iterator);
MSWChar modelName [MAX_MODEL_NAME_LENGTH];
while (item)
{
mdlModelItem_getName (item, modelName, MAX_MODEL_NAME_LENGTH);
models_activate (modelName);
item = mdlModelIterator_getNext (iterator);
}
mdlModelIterator_free (iterator);
// Active a model to enable a user to work with it
BoolInt models_activate
(
MSWChar const* modelName // => Activate this model
)
{
BoolInt activated = FALSE;
MSWChar activeName [MAX_MODEL_NAME_LENGTH];
mdlModelRef_getModelName (ACTIVEMODEL, activeName);
DgnFileObjP dgnFileObj = mdlModelRef_getDgnFile (ACTIVEMODEL);
DgnModelRefP modelRef = NULL;
if (SUCCESS == mdlModelRef_createWorkingByName (&modelRef, dgnFileObj, modelName, TRUE, FALSE)
&&
SUCCESS == mdlModelRef_activateAndDisplay (modelRef))
{
activated = models_areSame (modelRef, ACTIVEMODEL);
}
mdlModelRef_freeWorking (modelRef);
return activated;
}
Comparing two DgnModelRefP pointers doesn't work,
because although those pointers might be looking at the same model,
they could have been created in different ways and comparison would fail.
The correct way to find if two models are the same is to determine
what model cache they derive from.
If the cache is the same then the models are the same.
This example uses MDL function mdlModelRef_getCache
to test whether two models are the same …
// Implements alternative to unpublished function mdlModelRef_modelsAreSame
BoolInt models_areSame
(
DgnModelRefP modelRef1,
DgnModelRefP modelRef2
)
{
return mdlModelRef_getCache (modelRef1) == mdlModelRef_getCache (modelRef2);
}
If you're writing C++ then use a class to wrap the mdlModelIterator_api functions.
Use the
RAII idiom
to allocate memory in the class constructor and deallocate in its destructor.
Something like this …
// Header file
class CModelIterator
{
DgnIndexIteratorP iterator_;
public:
DgnIndexItemP GetNext () const;
CModelIterator (DgnModelRefP rootModelRef);
~CModelIterator ();
};
// Implementation file// ConstructorCModelIterator::CModelIterator (DgnModelRefP rootModelRef) : iterator_ (0) { DgnFileObjP oDgnFile = mdlModelRef_getDgnFile (rootModelRef); iterator_ = mdlModelIterator_create (oDgnFile); }// DestructorCModelIterator::~CModelIterator () { mdlModelIterator_free (iterator_); }// IterationDgnIndexItemP CModelIterator::GetNext () const { return mdlModelIterator_getNext (iterator_); }
Example usage …
// Constructor allocates memory automaticallyCModelIterator iterator (ACTIVEMODEL); DgnIndexItemP item = iterator.GetNext (); while (item) {// Do something with itemitem = iterator.GetNext (); }// Destructor frees memory automatically
A DGN model, whether the active model or not, may have reference attachments.
From the MDL programmer's viewpoint, each attachment is a DgnModelRefP.
However, those model references are created in a different way to an active or working
model reference and cannot be treated similarly.
You can iterate the reference attachment collection and gather data about each attachment.
You can update attachment data and make your changes persistent.
Your configuration of MicroStation may allow nested references.
If you want to iterate nested attachments,
then you must observe the MRITERATE_Xxx policies and iteration depth when iterating reference attachments.
A reference attachment is a model.
Here's how to iterate the attachments of a given model.
What you receive is a DgnModelRefP pointer to each attachment …
#include <msdgnmodelref.fdf>
ModelRefIteratorP iterator = NULL;
mdlModelRefIterator_create (&iterator, ACTIVEMODEL, MRITERATE_PrimaryChildRefs, 0);
DgnModelRefP refModel = mdlModelRefIterator_getFirst (iterator);
while (refModel)
{
// Do something with refModel
refModel = mdlModelRefIterator_getNext (iterator);
}
mdlModelRefIterator_free (&iterator);
If you want to examine the attachment, then the mdlRefFile_api is useful.
In particular, mdlRefFile_getParameters can a get about thirty-five pieces
of information about a reference attachment.
For example, here's how to determine whether a given attachment is displayed or not …
#include <msreffil.fdf>
int displayed = 0;
DgnModelRefP refModel = // Get reference model from somewhere
mdlRefFile_getParameters (&displayed, REFERENCE_DISPLAY, refModel);
if (displayed)
{
}
If you need even more detailed information, then mdlRefFile_getInfo
gives you a non-const ReferenceFile* pointer.
The pointer provides read/write access to the contents of the ReferenceFile
struct for a given attachment.
// #include <refernce.h>struct referenceFile { File_id_desc file_id;/* file identification */Ref_display display;/* display information */Ref_attach_desc attach;/* attachment description */Clip_desc clip;/* clipping descriptor(points separate) */UInt32 *colorMap_deprecated[2];/* DEPRECATED: element to screen color mappings */RefLevelMasks levelMask;/* Level mask */MSElementDescrP readOnlyRefAttachEdP;/* Attachment descriptor for reference attached during readonly session */MiscellaneousRefData *extraDataP;/* variable sized part of reference structure. More can be added to it. */void *additionalDataP[2];/* Future use */};
#include <msreffil.fdf>
// Get the attachments origin in its parent model
ReferenceFile* info = mdlRefFile_getInfo (refModel);
DPoint3d origin = info->display.mast_org;
You can modify an attachment using mdlRefFile_setParameters,
which covers most common uses.
You can also modify the data pointed at by the ReferenceFile* pointer.
In either case, you must call mdlRefFile_writeAttachment to persist the change.
When you modify an attachment, it is the parent model reference that is changed, not the attachment.
You must have write access to the parent model in order to make a change permanent.
If you're writing C++ then use a class to wrap the mdlModelRefIterator_api functions.
Use the
RAII idiom
to allocate memory in the class constructor and deallocate in its destructor.
Something like this …
// Header fileclass CModelRefIterator { ModelRefIteratorP iterator_; public: UInt32 ElementCount () const; bool Valid () const { return NULL != iterator_; } DgnModelRefP GetFirst () const; DgnModelRefP GetNext () const; explicit CModelRefIterator (bool refs); explicit CModelRefIterator (int scanPolicy); ~CModelRefIterator (); private:// Private copy constructor and assignment operator prevent copying // (declared but not implemented)CModelRefIterator (const CModelRefIterator& other); CModelRefIterator& operator=(const CModelRefIterator& other); };
// Implementation file// ConstructorCModelRefIterator::CModelRefIterator (bool refs) : iterator_ (NULL) { const int policy (refs? MRITERATE_Root | MRITERATE_PrimaryChildRefs: MRITERATE_Root); mdlModelRefIterator_create (&iterator_, ACTIVEMODEL, policy, 0); }// DestructorCModelRefIterator::~CModelRefIterator () { mdlModelRefIterator_free (&iterator_); }// GetFirstDgnModelRefP CModelRefIterator::GetFirst () const { return mdlModelRefIterator_getFirst (iterator_); }// GetNextDgnModelRefP CModelRefIterator::GetNext () const { return mdlModelRefIterator_getNext (iterator_); }// Element CountUInt32 CModelRefIterator::ElementCount () const { UInt32 nElements = 0; DgnModelRefP modelRef = GetFirst (); nElements = dgnCache_getElementCount (mdlModelRef_getCache (modelRef), DGNCACHE_SECTION_GRAPHIC_ELMS); while (NULL != (modelRef = GetNext ())) { nElements += dgnCache_getElementCount (mdlModelRef_getCache (modelRef), DGNCACHE_SECTION_GRAPHIC_ELMS); } return nElements; }
Here's how to use that class …
const int policy = MRITERATE_PrimaryChildRefs;
CModelRefIterator iterator (policy);
if (iterator.Valid ())
{
DgnModelRefP modelRef = iterator.GetFirst ();
for (; NULL != modelRef; modelRef = iterator.GetNext ())
{
// Check if file not found
int fnf = 0;
mdlRefFile_getParameters (&fnf, REFERENCE_FILENOTFOUND, modelRef);
// Attachment data
ReferenceFile* refInfo = mdlRefFile_getInfo (modelRef);
}
}
Return to MDL articles index.