Collision Detection in ODA Visualize (Part 2 of 3)

Egor Slupko

November 15, 2018

This article is part of a series of articles about the collision detection mechanism in ODA Visualize. For the previous article, see Part 1.

Trying out OdGsView::collide

The ODA Drawings Debug application contains two commands that use collision detection: “CollideAll” and “Collide”. Their implementation can be found in the TD_DrawingsExamplesCommon project in the classes OdExCollideAllCmd and OdExCollideCmd.

The CollideAll command is implemented in the OdExCollideAllCmd class. This command detects collisions between all drawables in the view.

First it implements an inheritor of OdGsCollisionDetectionReactor that copies paths in OdArray:

class OdExCollisionDetectionReactor : public OdGsCollisionDetectionReactor
{
  OdArray< OdExCollideGsPath* > m_pathes;
public:
  OdExCollisionDetectionReactor()
  {
  };
  ~OdExCollisionDetectionReactor()
  {
  }
  virtual OdUInt32 collisionDetected(const OdGiPathNode* pPathNode1, const OdGiPathNode* pPathNode2 )
  {
    OdExCollideGsPath* p1 = fromGiPath( pPathNode1 );
    OdExCollideGsPath* p2 = fromGiPath( pPathNode2 );
    m_pathes.push_back( p1 );
    m_pathes.push_back( p2 );
    return OdUInt32(OdGsCollisionDetectionReactor::kContinue);
  }
  OdArray< OdExCollideGsPath* >& pathes() {return m_pathes; }
};

Depending on user input it sets up OdGsCollisionDetectionContext to detect only intersections or both intersections and touching:

int nChoise = pIO->getInt( OD_T("Input 1 to detect only intersections, any other to detect all"), 0, 0 );
OdGsCollisionDetectionContext cdCtx;
cdCtx.setIntersectionOnly( nChoise == 1 );

Then it calls the collision detection method and collects detected paths from the reactor:

pView->collide( NULL, 0, &reactor, NULL, 0, &cdCtx );
OdArray< OdExCollideGsPath* >& pathes = reactor.pathes();

Next it highlights all paths and un-highlights those after user input:

for( OdUInt32 i = 0; i < pathes.size(); ++i )
{
  const OdGiPathNode* p = &(pathes[i]->operator const OdGiPathNode &());
  pModel->highlight( *p );
}
pIO->getInt( OD_T("Specify any number to exit"), 0, 0 );
for( OdUInt32 i = 0; i < pathes.size(); ++i )
{
  const OdGiPathNode* p = &(pathes[i]->operator const OdGiPathNode &());
  pModel->highlight( *p, false );
  delete pathes[i];
}
pathes.clear();

The result of performing this command:

image01

The Collide command is implemented in the OdExCollideCmd class. This command is based on the Move command and detects collisions between selected drawables and all other drawables in the view.

The OdGsCollisionDetectionReactor implementation copies only the second OdGiPathNode since the first node is one of the input nodes:

class OdExCollisionDetectionReactor : public OdGsCollisionDetectionReactor
{
  OdArray< OdExCollideGsPath* > m_pathes;
public:
  OdExCollisionDetectionReactor()
  {
  };
  ~OdExCollisionDetectionReactor()
  {
  }
  virtual OdUInt32 collisionDetected(const OdGiPathNode* /*pPathNode1*/, const OdGiPathNode* pPathNode2 )
  {
    OdExCollideGsPath* p = fromGiPath( pPathNode2 );
    if( p || pPathNode2->persistentDrawableId() )
    {
      m_pathes.push_back(p);
    }
    return OdUInt32(OdGsCollisionDetectionReactor::kContinue);
  }
  OdArray< OdExCollideGsPath* >& pathes() { return m_pathes; }
}; 

When the command is executed, it asks the user to select some objects (or use already selected objects) for input, creates an instance of its own tracker and executes the pIO->getPoint method with a tracker:

OdDbSelectionSetPtr pSSet = pIO->select(L"Collide: Select objects to be checked:", 
                                          OdEd::kSelAllowObjects | 
                                          OdEd::kSelAllowSubents |
                                          OdEd::kSelLeaveHighlighted);

if (!pSSet->numEntities()) throw OdEdCancel();
OdExTransactionSaver saver( pDb );
saver.startTransaction();
OdGePoint3d ptBase   = pIO->getPoint(L"Collide: Specify base point:");
CollideMoveTracker tracker( ptBase, pSSet, pDb, pView );
OdGePoint3d ptOffset = pIO->getPoint(L"Collide: Specify second point:", OdEd::kGdsFromLastPoint | OdEd::kGptRubberBand, 0, OdString::kEmpty, &tracker);

This tracker creates an OdGiPathNode representation of selected objects and for each mouse move event uses it as an input list for the OdGsView::collide method:

OdExCollisionDetectionReactor reactor;
m_pView->collide( inputArray.asArrayPtr(), inputArray.size(), &reactor, NULL, 0 );
highlight( reactor.pathes() );

After the OdGsView::collide method, it un-highlights already highlighted objects and highlights newly detected collisions:

void CollideMoveTracker::highlight( OdArray< OdExCollideGsPath* >& newPathes )
{
  //1) Unhighlight old paths
  if( !m_prevHLPathes.empty() )
    {
      for( OdUInt32 i = 0; i < m_prevHLPathes.size(); ++i )
      {
        m_pModel->highlight( m_prevHLPathes[i]->operator const OdGiPathNode &(), false );
        delete m_prevHLPathes[i];
      }
      m_prevHLPathes.clear();
    }
  //2) Highlight new paths
  for( OdUInt32 i = 0; i < newPathes.size(); ++i )
  {
    m_pModel->highlight( newPathes[i]->operator const OdGiPathNode &(), true );
    m_prevHLPathes.push_back( newPathes[i] );
  }
}

This command allows moving entities and seeing whether they have collisions with each other:

image02

 

image03

 

The next article in this series will describe OdGiCollideProc and OdGiCollisionDetector.