ODA Mechanical SDK: Work with Symbol Leaders

Dmytro Kabenok

December 17, 2021

Overview

AcmCLeaderNodeIterator provides functionality for interacting with the internal structure of a leader. Each leader can contain many nodes (or no nodes). These nodes are connected to each other and provide a tree-like structure. In most cases, the connection between nodes is simple and looks like an array of points along which lines are successively drawn. But there may be complicated cases when every main point has branches, which can look like a tree branch with leaves. AcmCLeaderNodeIterator was created to interact with each node separately regardless of the complexity of the tree. The iterator allows you to add new nodes, delete current nodes, search for a parent branch, get the coordinates of a node, find the number of branches, and so on.

Create a Leader

To use AcmCLeaderNodeIterator functionality, include the following file:

#include "AcmCLeaderNodeIterator.h"

To get an iterator from a mechanical symbol, you also need to include the following files:

#include "AcmCLeader.h"
#include "AcmSurfaceTexture.h" // In your case it can be any other mechanical symbol

Below is sample code for creating and obtaining an iterator from a mechanical symbol AcmSurfaceTexture (the code is similar for other mechanical symbols).

Note: PartList, PartRef, HoleChart and SectionSym objects do not have leader support.

OdDbDatabasePtr pDb = ... // get database
// Create and initialize symbol data. If something is wrong, such as a symbol or
// symbol standard is not supported by the current standard, a code other than
// Acm::eOk will be returned.
AcmSurfaceTexturePtr pSymb = AcmSurfaceTexture::createObject();
if (pSymb->setSymbolDefaults(pDb) != Acm::eOk)
  return;

// Add points to array and call the addLeader() method for leader creation
OdGePoint3dArray points;
points.setLogicalLength(3);
points[0].set(10.0, 30.0, 0.0);
points[1].set(50.0, 60.0, 0.0);
points[2].set(70.0, 65.0, 0.0);
// Acm::eOk will be returned if the leader for the symbol was successfully created
if (pSymb->addLeader(points) != Acm::eOk)
  return;

// Get leader. Since we have added only one leader, its index will be 0
AcmCLeader* pLeader = pSymb->getLeader(0);
if (!pLeader)
  return;

// Get leader iterator
AcmCLeaderNodeIteratorPtr pIt = pLeader->newNodeIterator();
if (pIt.isNull())
  return;

// Next step is iterator data initialization.
// This method will collect all nodes or only leafs of tree structure depending
// on how the input flag is set. By default, it collects all nodes.
pIt->start(false);

// To make sure that the iterator has been initialized correctly and the leader
// contains at least one node, call the done() iterator method. In the future,
// this method can be used to check if all nodes have been walked through.
if (pIt->done())
  return;

// Get and open ModelSpace blockRecord
OdDbBlockTableRecordPtr pMSpace = 
  OdDbBlockTableRecord::cast(pDb->getModelSpaceId().openObject(OdDb::kForWrite));
if (pMSpace.isNull())
  return;

// Now we can add AcmSurfaceTexture to the ModelSpace in order to look at the
// result of entity creation
pMSpace->appendOdDbEntity(pSymb);

As a result, a simple AcmSurfaceTexture entity is created:

 

a simple AcmSurfaceTexture entity is created

 

Modify a Leader

To move to the next node, use the iterator method step(). This method also allows you to step back if the iterator has previous nodes. To go one node back, set the incoming parameter for the step() method as true.

If you need to get the coordinates of the current node, call the vertex() method. If you want to set a new position for the current node, use the setVertex() method.

Note: The counting of nodes starts in the logical order of distance from the symbol, not in the order that the points were added to the array when calling the addLeader() method from the AcmSymbol-derived class. Below is a picture of vertex numbering (also considered a node) and vertex coordinates.

 

Work with Symbol Leaders

 

To move to different nodes and change the coordinates for the second node, see the following code, which also gets the coordinates of all nodes and finds out how many branches belong to the current node.

// Get the number of branches spun off from the current node
int branchesOfNode1 = pIt->numOfBranches(); // result: 1
// Get node coordinates
OdGePoint3d point1 = pIt->vertex(); // result: 70.0 65.0 0.0
// Step to the next node
pIt->step();

int branchesOfNode2 = pIt->numOfBranches(); // result: 1
OdGePoint3d point2 = pIt->vertex(); // result: 50.0 60.0 0.0
pIt->step();

int branchesOfNode3 = pIt->numOfBranches(); // result: 0 (because it's the last node)
OdGePoint3d point3 = pIt->vertex(); // result: 10.0 30.0 0.0

// Step backward and change vertex of second node
pIt->step(true);
pIt->setVertex(OdGePoint3d(40.0, 40.0, 0.0));
point2 = pIt->vertex(); // result: 40.0 40.0 0.0 (coordinates were updated)

After these manipulations, the symbol leader looks like:

 

the symbol leader after modification

 

Work with Leader Segments

To create a new leader segment (set of nodes) associated with the current node, use the iterator method addLeader().

Note: After adding a new segment, the iterator is not moved to the last-added node and remains at the current position. After adding a new segment, the number of branches departing from the current node increases. This can be checked using the numOfBranches() method.

// Add a new leader segment based on points from input array
OdGePoint3dArray pointsForNewNodes;
pointsForNewNodes.setLogicalLength(2);
pointsForNewNodes[0].set(25.0, 45.0, 0.0);
pointsForNewNodes[1].set(15.0, 45.0, 0.0);
pIt->addLeader(pointsForNewNodes);

// Get coordinates for the current node
OdGePoint3d currentVertex = pIt->vertex(); // result: 40.0 40.0 0.0
// Count of branches will increase after adding a new segment
int branchesCount = pIt->numOfBranches(); // result: 2

After adding a new segment, the symbol leader looks like:

 

a new segment

 

To create a new leader iterator for iterating the leader segment connected to this node, use the newNodeIterator() method. As an input parameter, specify the index of the segment you want to walk through. Select a newly created segment as an example; its index is 1. This is because a new branch is added to the end of the array and previously there was already one branch in the array.

You can also use the isLeafNode() method to find out if this node is the last one and a leaf. This method can be called not only from the iterator for the new segment but also from the main iterator.

// Create a new iterator for the new leader segment
AcmCLeaderNodeIteratorPtr pNewSegIt = pIt->newNodeIterator(1);
if (pNewSegIt.isNull())
  return;

// Initialize the new iterator and check if it contains data or not
pNewSegIt->start();
if (pNewSegIt->done())
  return;

// Check if the current node is a leaf
bool isLeaf = pNewSegIt->isLeafNode(); // result: false

You can move to the next node and check whether it is a leaf. You can also change the coordinates of the vertex using the transformBy() method.

pNewSegIt->step();
isLeaf = pNewSegIt->isLeafNode(); // result: true

OdGeMatrix3d mtrx;
mtrx.setToScaling(1.2);
pNewSegIt->transformBy(mtrx);

After these manipulations, the symbol leader looks like:

 

Symbol Leader

 

Work with Nodes

To return the iterator to the previous node, in addition to the step() method you can also use the parent() method. The parent() method is more relevant because if you initialize an iterator with only leaf elements, the step back sets the iterator to the previous node in the iterator nodes array instead of the parent element, which may not be in the array at all. If the iterator moves successfully to the parent node, true is returned as the result.

// Set iterator to the parent node
pNewSegIt->parent(); // result: true
Add a Node

To add a new node before the current one, use the insertBefore() method. As an input parameter, pass the point with coordinates in which the new node is created. If the current node has no parent or the iterator is not initialized, Acm::eBad is returned as the result.

// Insert a new node in front of the node that the iterator is currently pointing at
Acm::ErrorStatus es = pNewSegIt->insertBefore(OdGePoint3d(32.0, 48.0, 0.0)); // result: Acm::eOk

After inserting a new node, the symbol leader looks like:

 

a new node

 

Remove a Node

To remove one of the nodes, use the remove() method. For example, assume the current node is named the node to which the iterator points. Therefore, the remove() method merges the next node after the current one with the previous node from the current one and removes the current node. It will look as if the current node is thrown out of the chain, but the chain does not break and is rebuilt.

// Remove the node to which the iterator is pointing now
pNewSegIt->remove();

After deleting one of the nodes, the leader symbol looks like:

 

After deleting one of the nodes, the leader symbol looks like:

 

Instead of removing only one node, you can call the trim() method to completely cut off all branches including the current node to which the iterator points. In this case the iterator moves to the previous node and all other nodes disappear. This method performs the same actions as the removeLeader() method.

After performing several actions with the iterators, update the data in the main iterator by initializing it again so you can reach the node you need. To make the deletion more visual, first add one more node, and then call the trim() method.

// Update data in the iterator after calls of different methods
pIt->start(); // set the iterator to the first node
pIt->step(); // move the iterator pointer to the second node where forking is present
pIt->insertBefore(OdGePoint3d(60.0, 62.5, 0.0)); // insert a new node
pIt->trim(); // cut off all next leader segments starting from the current

The figures below show how the leader looks after adding a new node (left) and after trimming (right):

 

how the leader looks after adding a new node

 

Find a Node

To find a leader node, use one of the three iterator methods:

  • virtual Acm::ErrorStatus find(const OdGePoint3d& pt, bool fromTipPoint = true) = 0;
    Searches according to the given point and can specify where to start searching. If you specify fromTipPoint as true (by default), the search is performed from the first node, regardless of where the iterator currently points. If you specify fromTipPoint as false, the search is carried out on the nodes that remain in the iterator array starting from the current node.
  • virtual Acm::ErrorStatus findClosest(const OdGePoint3d& pt) = 0;
    Searches for the closest point. The search is performed in nodes that are dependent on the current node. If a point is found, the iterator pointer is set to it.
  • virtual Acm::ErrorStatus findByGrip(int indice) = 0;
    Finds a node using its grip index and sets the iterator pointer to the found node.

For all three methods, a result different from Acm::eOk is returned in the case of failure.

Work with Node Geometry

AcmCLeaderNodeIterator has the following methods for working with geometry:

  • virtual Acm::ErrorStatus attachGeomRef(const void* pGeometry, const OdDbObjectId& viewId, OdGePoint3d& prevLocation, bool hasReactor = false) = 0;
    Attaches geometry to a node. This method requires the same approach as the attachGeomRef() method for the heirs of the AcmSymbol class (for details see the Mechanical Examples located in the /Mechanical/Examples directory, where <INSTALL_DIR> is a directory where you unpacked the Mechanical SDK archive).
  • virtual Acm::ErrorStatus detachGeomRef() = 0;
    Detaches geometry from the current node, if geometry is present.
  • virtual void releaseGeomRef() = 0;
    Executes a release of geometry.
Get the Status of Attachments

There are also three methods used to get information about the status of current attachments:

  • virtual OdDbObjectId viewId() const = 0;
    Gets the ID of the view the node is associated with.
  • virtual bool isAttachedNoView() const = 0;
    Checks whether the node is not connected to a view.
  • virtual bool isAttachedToView(OdDbObjectId& viewId) const = 0;
    Checks whether the node is connected to a view.