Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

In this article, we’re going to describe updating the PRC model file structure using the example of splitting a multi body part into an assembly model.

If you import helloworld.stp in <HOOPS_Exchange_Publish SDK dir>\samples\data\step using HOOPS Demo Viewer (HDV), you will see the Part part has 10 bodies. Through this article, you can split the multi body part into an assembly model including multiple parts.

...

Create a derived class of A3DTreeVisitor for this time use case.

  1. Create a derived class of A3DTreeVisitor including visitEnter method for A3DPartConnector

    Code Block
    languagecpp
    ...
    static MY_CHAR acLogFileName[_MAX_PATH * 2];
    
    class bodyOperationVisitor: public A3DTreeVisitor
    {
    public:
    	bodyOperationVisitor(A3DVisitorContainer* psContainer = NULL) : A3DTreeVisitor(psContainer) {};
    	~bodyOperationVisitor() {};
    
    public:
    	virtual A3DStatus visitEnter(const A3DPartConnector& sConnector) override
    	{
    		A3DStatus iRet = A3DTreeVisitor::visitEnter(sConnector);
    
    		// My processes
    
    		return iRet;
    	}
    };
    
    void traverseModelFile(A3DAsmModelFile* pModelFile)
    ...

  2. Use the derived bodyOperationVisitor class instead of A3DTessVisitor in the traverseModelFile function

    Code Block
    languagecpp
    ...
    	// Prepare body operation visitor and set to the container
    	bodyOperationVisitor *pBodyOpVisitor = new bodyOperationVisitor(&sA3DVisitorContainer);
    	sA3DVisitorContainer.push(pBodyOpVisitor);
    ...

  3. Build the project

  4. Make Create break points in the bodyOperaitonVisitor class and verify that the class is called during run time

Info

Thanks to the bodyOperaitonVisitor, you can add inquiry and update functions while model file traversing.

Implementing functions and timing for splitting multi body part will be the following image:

Detecting a multi body part and

...

splitting the parts

If m_uiRepItemsSize of the Part Definition is more than 1, it means you have a multi body part.

  1. Add code to detect multi body part and split into individual parts

    Code Block
    languagecpp
    ...
    public:
    	virtual A3DStatus visitEnter(const A3DPartConnector& sConnector) override
    	{
    		A3DStatus iRet = A3DTreeVisitor::visitEnter(sConnector);
    
    		// Get the PartDefiniiton data
    		A3DAsmPartDefinitionData sData = sConnector.m_sPartData;
    
    		// Detect multi body part
    		if (1 < sData.m_uiRepItemsSize)
    		{
    			// It is multi body part
    		}
    		return iRet;
    	}
    ...

  2. Split the multi body into new parts

    Code Block
    languagecpp
    ...
    private:
    	int m_iBodyCnt = 0;
    	A3DAsmPartDefinition** m_ppPart;
    
    public:
    	virtual A3DStatus visitEnter(const A3DPartConnector& sConnector) override
    	{
    		A3DStatus iRet = A3DTreeVisitor::visitEnter(sConnector);
    
    		// Get the PartDefiniiton data
    		A3DAsmPartDefinitionData sData = sConnector.m_sPartData;
    
    		// Detect multi body part
    		if (1 < sData.m_uiRepItemsSize)
    		{
    			m_iBodyCnt = sData.m_uiRepItemsSize;
    
    			// Create new Parts for each body
    			m_ppPart = new A3DAsmPartDefinition*[m_iBodyCnt];
    			
    			for (A3DUns32 ui = 0; ui < m_iBodyCnt; ui++)
    			{
    				// Create a new part and set body
    				A3DAsmPartDefinitionData sNewPartData;
    				A3D_INITIALIZE_DATA(A3DAsmPartDefinitionData, sNewPartData);
    				sNewPartData.m_uiRepItemsSize = 1;
    				sNewPartData.m_ppRepItems = &sData.m_ppRepItems[ui];
    
    				iRet = A3DAsmPartDefinitionCreate(&sNewPartData, &m_ppPart[ui]);
    			}
    		}
    		return iRet;
    	}
    ...

Creating an assembly model file

In the above code, when a multi body part is detected, the m_iBodyCnt member variable is set. The first visitLeave of Product Occurrence after setting the body count will be the Product Occurrence of the target part.

  1. Add visitLeave method of Product Occurrence and create an assembly model including the split parts when m_iBodyCnt is more greater than 1.

    Code Block
    languagecpp
    ...
    private:
    	int m_iBodyCnt = 0;
    	A3DAsmPartDefinition** m_ppPart;
    	A3DAsmProductOccurrence* m_pAssyPO = NULL;
    ...
    	virtual A3DStatus visitLeave(const A3DProductOccurrenceConnector& sConnector) override
    	{
    		A3DStatus iRet = A3D_SUCCESS;
    
    		// Get the ProductOccurrence (PO)
    		const A3DEntity* pEntity = sConnector.GetA3DEntity();
    		A3DAsmProductOccurrence* pPO = (A3DAsmProductOccurrence*)pEntity;
    
    		// Get PO data
    		A3DAsmProductOccurrenceData sData = sConnector.m_sProductOccurrenceData;
    
    		// Get RootBaseData of the PO
    		A3DRootBaseData sRootBaseData;
    		A3D_INITIALIZE_DATA(A3DRootBaseData, sRootBaseData);
    		A3DRootBaseGet(pPO, &sRootBaseData);
    
    		// If multi body part
    		if (m_iBodyCnt)
    		{
    			// Create new POs of the new parts
    			A3DAsmProductOccurrence **ppPO;
    			ppPO = new A3DAsmProductOccurrence*[m_iBodyCnt];
    
    			for (A3DUns32 ui = 0; ui < m_iBodyCnt; ui++)
    			{
    				// Create new PO
    				A3DAsmProductOccurrenceData sPOData;
    				A3D_INITIALIZE_DATA(A3DAsmProductOccurrenceData, sPOData);
    				sPOData.m_dUnit = sData.m_dUnit;
    				sPOData.m_pLocation = sData.m_pLocation;
    				sPOData.m_pPart = m_ppPart[ui];
    
    				iRet = A3DAsmProductOccurrenceCreate(&sPOData, &ppPO[ui]);
    
    				// Set part name (org name - id)
    				char name[1024];
    				sprintf_s(name, sizeof(name), "%s - %d", sRootBaseData.m_pcName, ui + 1);
    				A3DRootBaseData sNewBaseData;
    				A3D_INITIALIZE_DATA(A3DRootBaseData, sNewBaseData);
    				sNewBaseData.m_pcName = name;
    
    				iRet = A3DRootBaseSet(ppPO[ui], &sNewBaseData);
    			}
    
    			// Create a new assembly PO for split parts
    			A3DAsmProductOccurrenceData sNewPOData;
    			A3D_INITIALIZE_DATA(A3DAsmProductOccurrenceData, sNewPOData);
    			sNewPOData.m_uiPOccurrencesSize = m_iBodyCnt;
    			sNewPOData.m_dUnit = sData.m_dUnit;
    			sNewPOData.m_ppPOccurrences = ppPO;
    
    			iRet = A3DAsmProductOccurrenceCreate(&sNewPOData, &m_pAssyPO);
    
    			iRet = A3DRootBaseSet(m_pAssyPO, &sRootBaseData);
    
    			m_iBodyCnt = 0;
    		}
    
    		iRet = A3DTreeVisitor::visitLeave(sConnector);
    		return iRet;
    	}
    ...

  2. Add visitLeave method of model file and create a new model file when an assembly model is created in the previous process.

    Code Block
    languagecpp
    ...
    static MY_CHAR acLogFileName[_MAX_PATH * 2];
    static A3DAsmModelFile *s_pNewModelFile = NULL;
    ...
    	virtual A3DStatus visitLeave(const A3DModelFileConnector& sConnector) override
    	{
    		A3DStatus iRet = A3D_SUCCESS;
    
    		const A3DEntity* pEntity = sConnector.GetA3DEntity();
    		A3DAsmModelFile* pModelFile = (A3DAsmModelFile*)pEntity;
    
    		A3DAsmModelFileData sData = sConnector.m_sModelFileData;
    
    		A3DRootBaseData sRootBaseData;
    		A3D_INITIALIZE_DATA(A3DRootBaseData, sRootBaseData);
    		A3DRootBaseGet(pModelFile, &sRootBaseData);
    
    		if (m_pAssyPO)
    		{
    			// Create assemly pModelFile
    			A3DAsmModelFileData sModelFileData = sConnector.m_sModelFileData;
    			sModelFileData.m_bUnitFromCAD = sData.m_bUnitFromCAD;
    			sModelFileData.m_dUnit = sData.m_dUnit;
    			sModelFileData.m_eModellerType = kA3DModellerPrc;
    			sModelFileData.m_uiPOccurrencesSize = 1;
    			sModelFileData.m_ppPOccurrences = &m_pAssyPO;
    
    			iRet = A3DAsmModelFileCreate(&sModelFileData, &s_pNewModelFile);
    
    			// Set root made data of original model file
    			iRet = A3DRootBaseSet(s_pNewModelFile, &sRootBaseData);
    			
    			m_pAssyPO = NULL;
    		}
    
    		return iRet;
    	}
    ...

Info

If you update a node of model file, it is necessary to update it at end of traversing (visitLeave) . If you update a node at beginning of traversing (visitEnter), it affect its child nodes traversing and cause of application crash.

Replacing the model file

Apart from the imported model file, a new model file (s_pNewModelFile) is created in the above code.

  1. After traversing the model file, replace the original model file when the new model file is created.

    Code Block
    languagecpp
    ...
    	traverseModelFile(sHoopsExchangeLoader.m_psModelFile);
    
    	if (NULL != s_pNewModelFile)
    	{
    		A3DStatus iRet = A3DAsmModelFileDelete(sHoopsExchangeLoader.m_psModelFile);
    
    		sHoopsExchangeLoader.m_psModelFile = s_pNewModelFile;
    		
    		s_pNewModelFile = NULL;
    	}
    ...

  2. Build the project

  3. Verify that multi body part is split into assembly model

Multi body parts in assembly model

The above code just supports multi body part model file. If you open the _LandingGear.CATProduct in the '<HOOPS_Exchange_Publish SDK dir>\samples\data\catiaV5\CV5_Landing Gear Model' directory using the HDV, you will see some multi body parts in the assembly model file.

...

In an assembly model file, though it is necessary to replace detected multi body part (step 4 in below image) to assembly model (step 6), the timing (step 13) is a bit complicated.

...

  1. Prepare member array of struct to keep target POs and assembly models

    Code Block
    languagecpp
    ...
    #include <sstream>
    #include <vector>
    ...
    class bodyOperationVisitor : public A3DTreeVisitor
    {
    ...
    	A3DAsmProductOccurrence* m_pAssyPO = NULL;
    	struct SplitPart
    	{
    		const A3DAsmProductOccurrence *parentPO;
    		A3DAsmProductOccurrence *oldPartPO;
    		A3DAsmProductOccurrence *newAssyPO;
    	};
    	std::vector<SplitPart> m_aSplitPart;
    ...

  2. Try to get father PO to check whether the model file is part model file or assembly model file

    Code Block
    languagecpp
    ...
    	virtual A3DStatus visitLeave(const A3DProductOccurrenceConnector& sConnector) override
    	{
    		...
    		// If multi body part
    		if (m_iBodyCnt)
    		{
    			...
    			iRet = A3DAsmProductOccurrenceCreate(&sNewPOData, &m_pAssyPO);
    
    			iRet = A3DRootBaseSet(m_pAssyPO, &sRootBaseData);
    			
    			// Access father PO
    			const A3DAsmProductOccurrence *pParentPO = sConnector.GetProductOccurrenceFather();
    
    			// If father PO isn't null, it is assembly model file 
    			if (NULL != pParentPO)
    			{
    				// Keep the target POs and created assembly model
    				SplitPart splitPart;
    				splitPart.parentPO = sConnector.GetProductOccurrenceFather();
    				splitPart.oldPartPO = pPO;
    				splitPart.newAssyPO = m_pAssyPO;
    
    				m_aSplitPart.push_back(splitPart);
    
    				m_pAssyPO = NULL;
    			}
    			// No father PO maens that it is part model file
    
    			m_iBodyCnt = 0;
    		}
    		...

  3. Replace multi body part to assembly model in proper timing

    Code Block
    languagecpp
    ...
    	virtual A3DStatus visitLeave(const A3DProductOccurrenceConnector& sConnector) override
    	{
    		...
    		// If multi body part
    		if (m_iBodyCnt)
    		{
    			...
    		}
    		else
    		{
    			// Check whether current PO is in a targets
    			for (int i = 0; i < m_aSplitPart.size(); i++)
    			{
    				SplitPart splitPart = m_aSplitPart[i];
    
    				if (pPO == splitPart.parentPO)
    				{
    					// Find target multi body part PO and replace to assembly PO
    					for (A3DUns32 ui = 0; ui < sData.m_uiPOccurrencesSize; ui++)
    					{
    						if (sData.m_ppPOccurrences[ui] == splitPart.oldPartPO)
    						{
    							sData.m_ppPOccurrences[ui] = splitPart.newAssyPO;
    							break;
    						}
    					}
    
    					iRet = A3DAsmProductOccurrenceEdit(&sData, pPO);
    				}
    			}
    		}
    
    		iRet = A3DTreeVisitor::visitLeave(sConnector);
    		return iRet;
    	}
    ...

  4. Build the project

  5. Verify that multi body parts in the assembly model are split into assembly models

Here is full source code of ImportExport.cpp for this article

...