BimRv SDK: Using Filter Rules for Collecting Elements

Evgeniy Tikhonov

January 09, 2020

To filter and collect elements in BimRv SDK, developers can use the OdBmElementCollector class to collect elements that pass through a set of criteria. The OdBmIterator class can also be used in cooperation with predicates or filter rules.

  • OdBmElementCollector — Collects elements in a database passed through a set of criteria. Defined in Database/BmElementCollector.h.
    Common methods:
    • OdBmElementCollector& wherePasses(const OdBmFilterRulePtr& pRule) — Applies an element filter to the collector.
    • OdBmElementPtrArray collect() const — Returns the set of elements that pass the filter.
    • OdBmElementCollector& ofCategory(OdBm::BuiltInCategory::Enum category) — Applies an ElementCategoryFilter to the collector.
    • OdBmElementCollector& ofClass(const OdTfClass* pClass) — Applies an ElementClassFilter to the collector.
    • OdBmElementCollector::iterator_type getElementIterator() const — Returns an element iterator (OdBmElementIterator) to the collected elements.
  • OdBmIterator — A class template that defines an iterator for objects of type T. Defined in Common/BmIterator.h. Common methods of iterator:
    • T object() — Returns the object that the iterator points to.
    • bool done() — Returns true if there are no more objects to iterate through.
    • bool next() — Returns the next element in the array.
    • OdBmIterator::pointer_type clone() — Returns a clone of the iterator.
    • OdArray< T > collect() — Returns an array of iterated objects.
    • OdBmIterator::pointer_type filter() — Creates a clone of the iterator and applies filter conditions to each object in the clone.
  • OdBmElementIterator — This iterator is used to iterate through elements in a database. Defined in Database/Entities/BmElemTable.h.
  • OdBmElemTableIterator — A public descendant of OdBmElementIterator. Defined in Database/Entities/BmElemTable.h.

To create OdBmElemTableIterator, use the OdBmDatabase::newElemTableIterator() method.

The main difference between the OdBmElementCollector and OdBmIterator classes is that OdBmIterator is stateless by design and the collect() method finishes the iterator (see the example below). OdBmElementCollector keeps its data.

To use an iterator more than once, just clone it before calling the collect() or next() methods:

OdBmElementIteratorPtr pElementsIt = pDb->newElemTableIterator()->filter(pCategoryRule);
OdBmElementIteratorPtr pElementsItClone = pElementsIt->clone();

The next example demonstrates the process of iterating and filtering elements in a database.

The example file contains seven duct curves, but only one of them has "foo" in the comments.

To construct a filtering rule for categories of elements:

OdBmDatabasePtr pDb = app->readFile(L"sample_file.rvt");
// Create filter rule based on OST_DuctCurves category
OdBmFilterCategoryRulePtr pCategoryRule = OdBmFilterCategoryRule::createObject();
pCategoryRule->addCategory(pDb->getObjectId(OdBm::BuiltInCategory::OST_DuctCurves, true));
// Collect elements using category rule
{
  // Create new ElemTable iterator and call filter method to get iterator of Elements that pass the filter rule
  OdBmElementIteratorPtr pElementsIt = pDb->newElemTableIterator()->filter(pCategoryRule);
  // Collect filtered elements into array
  OdBmElementPtrArray elements = pElementsIt->collect();
  // Please notice that the elements1 array will be empty, because iterator is stateless by design
  OdBmElementPtrArray elements1 = pElementsIt->collect();
}

To construct a string rule for the comments property of elements:

// Create Parameter value provider
OdBmParameterValueProviderPtr pParamEvalProvider = OdBmParameterValueProvider::createObject();
// Set flags and properties
pParamEvalProvider->setElemOrSymbol(1);
pParamEvalProvider->setParameter(OdBmParameterId(pDb->getObjectId(OdBm::BuiltInParameter::ALL_MODEL_INSTANCE_COMMENTS, true)));
// Create string rule with Equal evaluator
OdBmFilterStringRulePtr pStringRule = OdBmFilterStringRule::createObject();
pStringRule->setValueProvider(pParamEvalProvider);
OdBmFilterStringEqualsPtr pEvaluator = OdBmFilterStringEquals::createObject();
pStringRule->setEvaluator(pEvaluator);
pStringRule->setRuleString(L"foo");

To filter elements using a category and string rule:

OdBmElementIteratorPtr pElementsIt = pDb->newElemTableIterator()->filter(pCategoryRule)->filter(pStringRule);
OdBmElementPtrArray elements = pElementsIt->collect();

To collect elements using OdBmElementCollector:

OdBmDatabasePtr pDb_ = app->readFile(L"rooms.rvt");

// Create a filter rule for anything with ROOM_NAME that isn't "CLOSET"
OdBmParameterValueProviderPtr pParamEvalProvider = OdBmParameterValueProvider::createObject();
pParamEvalProvider->setElemOrSymbol(1);
pParamEvalProvider->setParameter(OdBmParameterId(pDb_->getObjectId(OdBm::BuiltInParameter::ROOM_NAME, true)));
OdBmFilterStringRuleEvaluatorPtr pEvaluator = OdBmFilterStringEquals::createObject();
OdBmFilterStringRulePtr pStringRule = OdBmFilterStringRule::createObject();
pStringRule->setValueProvider(pParamEvalProvider);
pStringRule->setEvaluator(pEvaluator);
pStringRule->setRuleString("CLOSET");
OdBmFilterInverseRulePtr pInverter = OdBmFilterInverseRule::createObject();
pInverter->setInnerRule(pStringRule);

// Get elements without a ROOM_NAME of "CLOSET"
OdBmElementCollector pFilterTest5 = OdBmElementCollector(pDb_).wherePasses(pInverter);
OdBmElementPtrArray arr5 = pFilterTest5.collect();

// Create a rule to filter out all rooms
OdBmFilterCategoryRulePtr pCategoryRule = OdBmFilterCategoryRule::createObject();
pCategoryRule->addCategory(pDb_->getObjectId(OdBm::BuiltInCategory::OST_Rooms, true));

// Get only rooms without a ROOM_NAME of "CLOSET"
OdBmElementCollector pFilterTest6 = pFilterTest5.wherePasses(pCategoryRule);

// Do the same by combining calls of wherePasses()
OdBmElementCollector pFilterTest7 = OdBmElementCollector(pDb_).wherePasses(pInverter).wherePasses(pCategoryRule);

// Get all elements of OdBm::BuiltInCategory::OST_Rooms category
OdBmElementCollector pFilterTest8 = OdBmElementCollector(pDb_).ofCategory(OdBm::BuiltInCategory::OST_Rooms);

// Get all elements of OdBmRoomElem class
OdBmElementCollector pFilterTest9 = OdBmElementCollector(pDb_).ofClass(OdBmRoomElem::desc());

Collecting elements using predicates with OdBmIterator

Also you can use predicates in cooperation with iterators, for example:

struct c_elemHandle : std::unary_function<OdBmElementPtr, bool> {
  c_elemHandle(OdBm::BuiltInCategory::Enum category) 
    : m_hndl(OdDbHandle(category)) {}
  bool operator()(const OdBmElementPtr& pObj) const {
    return m_hndl == pObj->getCategroryId().getHandle();
  }
private:
  OdDbHandle m_hndl;
};

OdBmElementIteratorPtr pElementsIt = 
pDb->newElemTableIterator()->filter(c_elemHandle(OdBm::BuiltInCategory::OST_Rooms));

For more details about classes and methods, including evaluators used to create filter rules, see the BimRv documentation (login required).