Teigha Kernel: OdGsView Interactivity Mode

Egor Slupko

December 07, 2017

Introduction

Interactivity mode is a new feature of the Teigha graphics system . It allows for automatic interruption of the drawing process if the process is taking a long time. Of course in this case the drawing will not be drawn completely, but this feature can be useful for multiple consecutive redraws when the result of the intermediate redraw is not so important.

Using interactivity mode

Interactivity mode is controlled through the OdGsView API. However, this mode has no effect if the corresponding device does not support it. The following method of OdGsBaseVectorizeDevice checks whether the device supports this mode:

bool supportInteractiveViewMode() const;

Also, OdGsBaseVectorizeDevice has a method that allows you to manually change support of interactivity mode:

void setSupportInteractiveViewMode( bool );

The OdGsView API provides the following methods for interactivity mode:

  • virtual void beginInteractivityMode( double frameRateInHz )

    This method enables view interactivity mode. frameRateInHz specifies the desired frame rate (FPS). This value defines the desired minimum number of frames per second. In other words, if you want to limit the time that each redraw operation takes (e.g., “not more than X seconds”) frameRateInHz should be the inverse ratio of this time limit (1 / X).

  • virtual bool isInInteractivity() const

    Returns true if this view is in interactivity mode.

  • virtual double interactivityFrameRate() const

    Returns the desired frame rate, which was specified in beginInteractivityMode().

  • virtual void endInteractivity()

    This method disables view interactivity mode.

Implementation details

When a view is in interactivity mode, each time OdGsBaseVectorizer::beginViewVectorization is called (usually from OdGsDevice::update()), it starts a special timer that will be stopped in OdGsBaseVectorizer::endViewVectorization. Each request to OdGsBaseVectorizer::renderAbort checks this timer. If the elapsed time exceeds 1 / interactivityFrameRate(), OdGsBaseVectorizer::renderAbort() returns true and the rendering loop is interrupted.

The interactive mode has some limitations:

  • Since this mode is based on OdGsBaseVectorizer::renderAbort(), it has no effect if the device does not support renderAbort() functionality.
  • Since this mode works on the OdGsNode level, it may have no visual effect if the drawing contains only a few big (long time to draw) nodes (e.g. external references).
  • Sometimes a rendering loop cannot be interrupted by renderAbort(), only suppressed. In this case, interactivity mode still works but the resulting frame rate may differ from the preset.

Currently Teigha supports interactive mode for the following devices: WinGDI, WinDirectX, and WinOpenGL.

Example

The OdaMfcApp sample application uses interactivity mode for the Orbit command. The console command “interactivity” allows you to disable or enable this mode and to change the desired frame rate:

image1

Usage of the interactivity mode can be found in TD_DrawingExamplesCommon in EditorObject.cpp. We use a helper class to enable and disable interactivity mode:

class ViewInteractivityMode
{
  bool      m_enabled;
  OdGsView* m_pView;
public:
  ViewInteractivityMode( OdRxVariantValue enable, OdRxVariantValue frameRate, OdGsView* pView )
  {
    m_enabled = false;
    m_pView = pView;
    if( !enable.isNull() )
    {
      m_enabled = (bool)(enable);
      if( m_enabled && !frameRate.isNull() )
      {
        double rate = (double)(( (frameRate.get())->getDouble() ));
        pView->beginInteractivity( rate );
      }
    }
  }
  ~ViewInteractivityMode()
  {
    if( m_enabled ) m_pView->endInteractivity();
  }
};

A constructor of ViewInteractivityMode enables (if required) interactivity mode and a destructor disables it.

The function OdEx3dOrbitCmd::execute creates the instance of ViewInteractivityMode before it goes to an event loop:

void OdEx3dOrbitCmd::execute(OdEdCommandContext* pCmdCtx)
{
  OdDbCommandContextPtr pDbCmdCtx(pCmdCtx);
  OdDbDatabasePtr pDb = pDbCmdCtx->database();
  OdSmartPtr<OdDbUserIO> pIO = pDbCmdCtx->userIO();
  
  OdDbObjectPtr pVpObj = pDb->activeViewportId().safeOpenObject(OdDb::kForWrite);
  OdDbAbstractViewportDataPtr pAVD(pVpObj);

  OdGsView* pView = pAVD->gsView(pVpObj);  

  // There is one special case: layout with enabled 'draw viewports first' mode
  {
    if (!pDb->getTILEMODE())
    {
      OdDbLayoutPtr pLayout = pDb->currentLayoutId().openObject();
      if (pLayout->drawViewportsFirst())
      {
        if (pView->device()->viewAt(pView->device()->numViews() - 1) == pView)
          pView = pView->device()->viewAt(0);
      }
    }
  }
  //

  OdRxVariantValue interactiveMode = (OdRxVariantValue)pCmdCtx->arbitraryData( OD_T("OdaMfcApp InteractiveMode" ) );
  OdRxVariantValue interactiveFrameRate = (OdRxVariantValue)pCmdCtx->arbitraryData( OD_T("OdaMfcApp InteractiveFrameRate" ) );
  ViewInteractivityMode mode( interactiveMode, interactiveFrameRate, pView );

  OdStaticRxObject<RTOrbitTracker> tracker;
  for(;;)
  {
    try
    {
      tracker.init(pView, pIO->getPoint(OD_T("Press ESC or ENTER to exit."),
        OdEd::kInpThrowEmpty|OdEd::kGptNoUCS|OdEd::kGptNoOSnap|OdEd::kGptBeginDrag, 0, OdString::kEmpty, &tracker));
      pIO->getPoint(OD_T("Press ESC or ENTER to exit."),
        OdEd::kInpThrowEmpty|OdEd::kGptNoUCS|OdEd::kGptNoOSnap|OdEd::kGptEndDrag, 0, OdString::kEmpty, &tracker);
      tracker.reset();
    }
    catch(const OdEdCancel)
    {
      break;
    }
  }
}

When the function ends, the instance of ViewInteractivityMode automatically deletes, which calls OdGsView::endInteractivity().

Below are a few samples. In the first sample, the frame rate is small enough so all entities are shown while performing the Orbit command:

image2

Here the frame rate was increased so some parts of the drawing disappeared:

image3

And here the frame rate was increased again so only a few of the original entities are shown:

image4