How to use Markups (Redlines) in Teigha Visualize

Vlad Malka

May 24, 2018

Teigha Visualize supports features that help with reviewing and editing your files, in particular, creating and saving markups (redlines). Here are some examples of drawings that contain markups:


In the Teigha Visualize Viewer sample, you can try it for yourself. Use the Markups toolbar in the main window to create different type of markups:

  • Rect markup — Draw a rectangle markup;
  • Circle markup — Draw a circle markup;
  • Handle markup — Draw a free handle markup
  • Cloud markup — Draw a cloud markup;
  • Text markup — Draw a text markup.

Then use the two tools for saving and loading markup views:

  • Save view — Save the current view with markups that you’ve drawn;
  • Load view — Load a view that contains markups.

To save markups that you’ve drawn:

  1. Click Save; image5


  2. Enter the name for the saved markup view.

Here’s how it works:

Saved markups use a structure that contains parameters of the view: position, target, upVector, fieldWidth, fieldHeight, projection type, and render mode. These parameters are received from the view when the “Save” button is pressed. After, the structure writes binary data to a new entity with the entered name.

// create and append the binary data to the entity
OdTvByteUserData *data = new OdTvByteUserData(params, sizeof(struct SaveViewParams), OdTvByteUserData::kOwn, true);
      pEn->appendUserData(data, m_appTvId);

To load saved views that contain markups:

  1. Click Load; image6


  2. Select the view to load, then click Load. (You can also click Delete to remove the selected markup view.)

Here’s how it works:

When the Load button is pressed, first binary data is read from the entity with the selected name, next data is applied to the current view, and finally the visibility is turned on for the entity.

Implementation details

Here is a diagram of the implementation structure of markups:


In the database, different models are stored, one of which stores markup entities. This model has the name “$ODA_TVVIEWER_MARKUPS”. The model is created only when a markup is drawn. In the object tree, this model is in the “Services model.” Always perform a check for whether this model exists.

if (m_TvMarkupsModelId.isNull())
  m_TvMarkupsModelId = m_TvDatabaseId.openObject(OdTv::kForWrite)
    ->createModel(OD_TV_MARKUP_MODEL, OdTvModel::kDirect);


When markup creation begins, create a temporary entity “$MarkupTempEntity”, where all names are declared in macros with the appropriate names; the next markup creation will write to this entity. This entity is destroyed for almost any action (pan, orbit, zoom, etc.). Also, the model contains other entities that were saved, while the markup is not saved, and the work continues with the temporary entity. The following diagram shows the structure of a markup entity.


Any markup entity has just five subentities. Each subentity is a folder for the special type of markup:

  • Rectangle markups — Subentity “Rectangles”;
  • Circle markups — Subentity “Circles”;
  • Free handle markups — Subentity “Free handles”;
  • Cloud markups — Subentity “Clouds”;
  • Text markups — Subentity “Texts”.

The constructors for each markup search for a temporary entity, and if not found, creates a new temporary entity according to the folders for markup types.

// create main entity if not exist
OdTvModelPtr modelPtr = m_tvDraggersModelId.openObject(OdTv::kForWrite);
m_entityId = findEntityByName(modelPtr->getEntitiesIterator(), OD_TV_MARKUP_TEMP_ENTITY, false);
if (m_entityId.isNull())
  m_entityId = modelPtr->appendEntity(OD_TV_MARKUP_TEMP_ENTITY);
  m_entityId.openObject(OdTv::kForWrite)->setColor(OdTvColorDef(255, 0, 0));

// crate rectangles subEntity if not exist
m_rectFoldEntityId = findSubEntityByName(m_entityId.openObject()->getGeometryDataIterator(), OD_TV_MARKUP_RECTANGLES);
if (m_rectFoldEntityId.isNull())
  m_rectFoldEntityId = m_entityId.openObject(OdTv::kForWrite)->appendSubEntity(OD_TV_MARKUP_RECTANGLES);

Markup folders contain subentities that have the appropriate geometry data. Subentities with the geometry data are created using the updateFrame() methods when the create object flag is true.

//update or create entity
if (bCreate)
  m_rectEntityId = m_rectFoldEntityId.openAsSubEntity(OdTv::kForWrite)->appendSubEntity();
    OdTvEntityPtr entityNewPtr = m_rectEntityId.openAsSubEntity(OdTv::kForWrite);
    //create frame
    m_frameId = entityNewPtr->appendPolygon(m_pts);