Table of Contents |
---|
Introduction
In this article, we’re Parsing and interpreting the PRC structure is non-trivial in some cases, as described in our documentation for PRC Basics.
In this forum post, we are going to describe traversing a PRC model file using a builtin Visitor pattern sample. Based on the ImportExport sample project, you will be able to make a prototype easily, which can retrieve or update model file.
As you can see in the below link, in some cases parsing and interpreting the PRC structures is non-trivial.
We are providing sample source code to traverse the model file using Visitor patternand update the model file. This sample is reusable and you can port the code into your application as it is even if you don’t know in the details of the traversing manner.
Instructions
Workbench project preparation
Prepare a workbench project based on ImportExport sample.
Copy ImportExport folder from <HOOPS_Exchange_Publish SDK dir>\samples\exchange\exchangesource and past in your project folder
Rename the copied folder name to “ImportTraverseExport“
Copy HOOPSExchangePublishSample.props file from <HOOPS_Exchange_Publish SDK dir>\samples and past in the ImportTraverseExport folder
Copy common.hpp file from <HOOPS_Exchange_Publish SDK dir>\samples\exchange\exchangesource and past in the project folder
Start Visual Studio 2015 and open the ImportExport project
Close Visual Studio with saving the solution as “ImportTraverseExport.sln”
Create a file naming “VS2015.bat“ and open it in a text editor
Edit the bat file so that it opens the solution with specifying HEXCHANGE_INSTALL_DIR environment variable
Code Block language text SET HEXCHANGE_INSTALL_DIR=C:\SDK\HOOPS_Exchange_Publish_2020_SP1 CALL "C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\devenv.exe" ImportTraverseExport.sln
C:\SDK\HOOPS_Exchange_Publish_2020_SP1
is your HOOPS Exchange install dirSave and close the bat file
Open the solution using the bat file
Add
$(HEXCHANGE_INSTALL_DIR)\include
in the Additional Include Directories of C/C++ - General page of project propertyOpen ImportExport.cpp and edit like below
Code Block language cpp ... #include "common.hpp" #include <sstream> ... // // ### INITIALIZE HOOPS EXCHANGE // std::wstringstream bin_dir; #ifdef _DEBUG std::wstring buffer; buffer.resize(_MAX_PATH * 2); if (GetEnvironmentVariable(L"HEXCHANGE_INSTALL_DIR", &buffer[0], static_cast<DWORD>(buffer.size()))) { bin_dir << buffer.data() << L"/bin/win64\0"; } #else bin_dir << L""; #endif A3DSDKHOOPSExchangeLoader sHoopsExchangeLoader(bin_dir.str().data()); ...
Build the project and verify that the import and export process works properly
Info |
---|
To run this application, it is necessary to specify source and destination CAD file names to the Command Arguments of Debugging page of the project property i.e.) |
Porting Visitor pattern
Though you can find some sample projects including Visitor pattern, visitor pattern of the Collision sample will be the best for reuse.
Copy visitor folder from <HOOPS_Exchange_Publish SDK dir>\samples\exchange\exchangesource\Collision and past in the ImportTraverseExport folder
Back to the project of Visual Studio.
Create a new filter named “visitor” under the Header Files and add all header files in the visitor folder
Create a new filter named “visitor” under the Source Files and add all cpp files in the visitor folder
Implement tree traverse function
Implement a function which calls the visitor pattern.
Create traverseModelFile function in ImportExport.cpp
Code Block | ||
---|---|---|
| ||
...
#include <sstream>
#include "visitor/VisitorContainer.h"
#include "visitor/VisitorTree.h"
static MY_CHAR acSrcFileName[_MAX_PATH * 2];
static MY_CHAR acDstFileName[_MAX_PATH * 2];
static MY_CHAR acLogFileName[_MAX_PATH * 2];
void traverseModelFile(A3DAsmModelFile* pModelFile)
{
// Prepare Visitor container
A3DVisitorContainer sA3DVisitorContainer(CONNECT_TRANSFO);
sA3DVisitorContainer.SetTraverseInstance(true);
// Prepare Tree traverse visitor and set to the container
A3DTreeVisitor *pA3DTreeVisitor = new A3DTreeVisitor(&sA3DVisitorContainer);
sA3DVisitorContainer.push(pA3DTreeVisitor);
// Prepare model file connector and call Traverse
A3DModelFileConnector sModelFileConnector(pModelFile);
A3DStatus sStatus = sModelFileConnector.Traverse(&sA3DVisitorContainer);
}
//######################################################################################################################
#ifdef _MSC_VER
int wmain(A3DInt32 iArgc, A3DUniChar** ppcArgv)
... |
Split the Convert process into Import and Export so that the traverse function can be called after importing
Code Block | ||
---|---|---|
| ||
...
A3DExport sExport(acDstFileName); // see A3DSDKInternalConvert.hxx for import and export detailed parameters
// perform conversion
CHECK_RET(sHoopsExchangeLoader.Import(sImport));
traverseModelFile(sHoopsExchangeLoader.m_psModelFile);
CHECK_RET(sHoopsExchangeLoader.Export(sExport));
... |
...
Build the project
...
Make a break point in the TreeVisitor.cpp and verify that a model file is traversed
...
Derived class creation
Thanks to the visitor pattern sample, now you can access model file from the root to leaves in correct manner. Though you can add your own retrieving and updating processes in it, we recommend you to make derived classes of A3DTreeVisitor so that you can divide code into common traverse and various use cases. You don't need to write traverse code in the each use case.
...
Create a derived class of A3DTreeVisitor
Code Block | ||
---|---|---|
| ||
...
static MY_CHAR acLogFileName[_MAX_PATH * 2];
class myTreeVisitor: public A3DTreeVisitor
{
public:
myTreeVisitor(A3DVisitorContainer* psContainer = NULL) : A3DTreeVisitor(psContainer) {};
~myTreeVisitor() {};
public:
virtual A3DStatus visitEnter(const A3DProductOccurrenceConnector& sConnector) override
{
A3DStatus iRet = A3DTreeVisitor::visitEnter(sConnector);
// My processes
return iRet;
}
virtual A3DStatus visitLeave(const A3DProductOccurrenceConnector& sConnector) override
{
A3DStatus iRet = A3D_SUCCESS;
// My processes
iRet = A3DTreeVisitor::visitLeave(sConnector);
return iRet;
}
};
void traverseModelFile(A3DAsmModelFile* pModelFile)
... |
Use the derived myTreeVisitor class instead of A3DTessVisitor in the traverseModelFile function
Code Block |
---|
...
// Prepare Tree traverse visitor and set to the container
myTreeVisitor *pMyTreeVisitor = new myTreeVisitor(&sA3DVisitorContainer);
sA3DVisitorContainer.push(pMyTreeVisitor);
... |
...
Build the project
...
Make break points in the myTreeVisitor class and verify that the class is called
...
Info |
---|
In the A3DTreeVisitor, model file is traversed recursively from root (pModelFile) to leaves (Product Occurrence, Part definition, Representation Item, …). The In the above sample, you can implement your own processes when Product Occurence is traversed by setting as |
The following is connector type list
Node type | Connector |
---|---|
A3DAsmModelFile | A3DModelFileConnector |
A3DAsmProductOccurrence | A3DProductOccurrenceConnector |
A3DAsmPartDefinition | A3DPartConnector |
A3DRiRepresentationItem | A3DRiConnector |
A3DRiBrepModel | A3DRiBrepModelConnector |
A3DRiSet | A3DRiSetConnector |
A3DRiPolyBrepModel | A3DPolyRiBrepModelConnector |
Logging component names
Add code to log component names in the console
Code Block | ||
---|---|---|
| ||
...
private:
int m_iLevel = 0;
public:
virtual A3DStatus visitEnter(const A3DProductOccurrenceConnector& sConnector) override
{
A3DStatus iRet = A3DTreeVisitor::visitEnter(sConnector);
// Increment level
m_iLevel++;
// Get the ProductOccurrence (PO)
const A3DEntity* pEntity = sConnector.GetA3DEntity();
A3DAsmProductOccurrence* pPO = (A3DAsmProductOccurrence*)pEntity;
// Get RootBaseData of the PO
A3DRootBaseData sRootBaseData;
A3D_INITIALIZE_DATA(A3DRootBaseData, sRootBaseData);
A3DRootBaseGet(pPO, &sRootBaseData);
// Get the PO name
A3DUniChar acName[256];
if (sRootBaseData.m_pcName)
A3DMiscUTF8ToUTF16(sRootBaseData.m_pcName, acName);
else
wcscpy_s(acName, _T("NO_NAME"));
// Show the PO name with level
for (int i = 0; i < m_iLevel; i++)
_tprintf(_T("+ "));
_tprintf(_T("%s\n"), acName);
return iRet;
}
virtual A3DStatus visitLeave(const A3DProductOccurrenceConnector& sConnector) override
{
A3DStatus iRet = A3D_SUCCESS;
// Decrement level
m_iLevel--;
iRet = A3DTreeVisitor::visitLeave(sConnector);
return iRet;
}
... |
...
Build the project
...
Verify that Product Occurrence name are displayed in the console
...
In the above image, we used Landing Gear model in the sample folder."$(HEXCHANGE_INSTALL_DIR)\samples\data\catiaV5\CV5_Landing Gear Model_LandingGear.CATProduct"
Getting component visibility
If you open the Landing_Gear_Assy in HOOPS Demo Viewer (HDV), you will see a component is invisible in default.
...
The visibility parameter is bit complex because parent visibility affects to the child components. This problem is managed using a separate visitor (A3DVisitorColorMaterials
) in the visitor pattern sample.
There are following Visitor classes and multiple visitors can be called via the visitor container.
Flag | Visitor class | Member |
---|---|---|
CONNECT_TRANSFO | A3DVisitorTransfo | Transform |
CONNECT_COLORS | A3DVisitorColorMaterials | Color, material, visibility |
CONNECT_MESH | A3DVisitorTessellation | Tessallation |
CONNECT_BREP | A3DVisitorBrep | B-rep |
Add CONNECT_COLORS
to the visitor container
Code Block | ||
---|---|---|
| ||
...
#include "visitor/VisitorTree.h"
#include "visitor/VisitorCascadedAttribute.h"
#include "visitor/CascadedAttributeConnector.h"
...
void traverseModelFile(A3DAsmModelFile* pModelFile)
{
// Prepare Visitor container
A3DVisitorContainer sA3DVisitorContainer(CONNECT_TRANSFO | CONNECT_COLORS);
sA3DVisitorContainer.SetTraverseInstance(true);
... |
Add code to access visibility info
Code Block | ||
---|---|---|
| ||
...
virtual A3DStatus visitEnter(const A3DProductOccurrenceConnector& sConnector) override
{
...
_tprintf(_T("%s"), acName);
A3DVisitorColorMaterials *pA3DCascadedVisitor = static_cast<A3DVisitorColorMaterials*>(m_psContainer->GetVisitorByName("CascadedAttribute"));
if (pA3DCascadedVisitor)
{
ColorMaterialsConnector sColorConnector(nullptr);
pA3DCascadedVisitor->GetColorMaterialConnector(sColorConnector);
if (sColorConnector.IsShow())
_tprintf(_T("\n"));
else
_tprintf(_T(" (Hidden)\n"));
}
return iRet;
}
... |
...
Build the project
...
Verify that (Hidden)
is added after the Product Occurence name
...
Getting instance transformation
You will see in HDV that some components are used multiple times in the assembly model.
...
Add code to get transformation of each instance.
You will see that the visitor to get transform is already assigned to the visitor container.A3DVisitorContainer sA3DVisitorContainer(CONNECT_TRANSFO | CONNECT_COLORS);
Add override function of A3DPartConnector and get the part transform
Code Block | ||
---|---|---|
| ||
class myTreeVisitor: public A3DTreeVisitor
{
...
virtual A3DStatus visitEnter(const A3DPartConnector& sConnector) override
{
A3DStatus iRet = A3DTreeVisitor::visitEnter(sConnector);
// Get transform connector via transform visitor
A3DVisitorTransfo* psVisitorTransfo = static_cast<A3DVisitorTransfo*>(m_psContainer->GetVisitorByName("Transformation"));
A3DTransfoConnector* pConnector = psVisitorTransfo->GetTransfoConnector();
A3DMatrix4x4 sTransfo;
pConnector->GetGlobalTransfo(sTransfo);
delete pConnector;
for (unsigned int i = 0; i < m_iLevel; i++)
_tprintf(_T("+ "));
_tprintf(_T(" (%.3f, %.3f, %.3f)\n"), sTransfo.m_adM[12], sTransfo.m_adM[13], sTransfo.m_adM[14]);
return iRet;
}
... |
...
Build the project
...
Verify that part locations differ by instance
...
Info |
---|
You will see If you set it as false, just the first part of instances will be traversed. |
...
If you refer the Collision sample, you will see a usage of Tree traverse Visitor.
Hence A3DCollisionCompute
requires two Representation Item groups to compute collision, it is necessary to get flatten array of Representation Items from the target model file.
...