Versions Compared

Key

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

...

  1. Refer How-To: Traverse PRC model file using Visitor pattern for rapid prototyping article and create a workbench project (form [Workbench project preparation] to [Porting Visitor patternImplement tree traverse function])

Derived class creation

Create a derived class of A3DTreeVisitor for this use case.

...

  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
    		m_iBodyCnt = 0;
    		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;
    	}
    ...

...

  1. Add visitLeave method of Product Occurrence and create an assembly model including the split parts when m_iBodyCnt is 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;
    			sNewPOData.m_pLocation = sData.m_pLocation;
    
    			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;
    class bodyOperationVisitor : public A3DTreeVisitor
    {
    ...
    	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;
    	}
    ...

...

  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

...

languagecpp

...

Detecting part instances

If you look at converted model in the model browser, you will see some components are instanced.

...

To manage part instances, each part instance points abstract product occurrence so called “prototype“.

...

  1. Add code to get prototype info

    Code Block
    languagecpp
    ...
    	virtual A3DStatus visitLeave(const A3DProductOccurrenceConnector& sConnector) override
    	{
    		A3DStatus iRet = A3D_SUCCESS;
    		...
    		// If multi body part
    		if (m_iBodyCnt)
    		{
    			// Get prototype
    			A3DAsmProductOccurrence *pProtoPO = sData.m_pPrototype;
    			A3DAsmProductOccurrenceData sProtoData;
    			A3DRootBaseData sProtoBaseData;
    			if (NULL != pProtoPO)
    			{
    				A3D_INITIALIZE_DATA(A3DAsmProductOccurrenceData, sProtoData);
    				iRet = A3DAsmProductOccurrenceGet(pProtoPO, &sProtoData);
    
    				A3D_INITIALIZE_DATA(A3DRootBaseData, sProtoBaseData);
    				iRet = A3DRootBaseGet(pProtoPO, &sProtoBaseData);
    			}
    
    			// Create new POs of the new parts
    			...

  2. Build the project

  3. Create a break point and verify that prototype info is gotten

    Image Added

  4. Keep the prototype pointer and instance transform in a member variable and detect part instance

    Code Block
    languagecpp
    ..
    	struct SplitPart
    	{
    		const A3DAsmProductOccurrence *parentPO;
    		A3DAsmProductOccurrence *oldPartPO;
    		A3DAsmProductOccurrence *newAssyPO;
    		A3DAsmProductOccurrence *protoPO;
    		A3DMiscTransformation *instanceTransform;
    		A3DRootBaseData instanceBaseData;
    	};
    	...
    virtual A3DStatus visitLeave(const A3DProductOccurrenceConnector& sConnector) override
    	{
    		A3DStatus iRet = A3D_SUCCESS;
    		...
    		// If multi body part
    		if (m_iBodyCnt)
    		{
    			...
    			if (NULL != pProtoPO)
    			{
    				A3D_INITIALIZE_DATA(A3DAsmProductOccurrenceData, sProtoData);
    				iRet = A3DAsmProductOccurrenceGet(pProtoPO, &sProtoData);
    
    				A3D_INITIALIZE_DATA(A3DRootBaseData, sProtoBaseData);
    				iRet = A3DRootBaseGet(pProtoPO, &sProtoBaseData);
    
    				// Chenck whether the same part is detected
    				for (int i = 0; i < m_aSplitPart.size(); i++)
    				{
    					if (pProtoPO == m_aSplitPart[i].protoPO)
    					{
    						// Keep the target POs
    						SplitPart splitPart;
    						splitPart.parentPO = sConnector.GetProductOccurrenceFather();
    						splitPart.oldPartPO = pPO;
    						splitPart.newAssyPO = m_aSplitPart[i].newAssyPO;
    						splitPart.instanceTransform = sData.m_pLocation;
    						splitPart.instanceBaseData = sRootBaseData;
    
    						m_aSplitPart.push_back(splitPart);
    
    						return iRet;
    					}
    				}
    			}
    			...
    			// 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;
    			// sNewPOData.m_pLocation = sData.m_pLocation; <= comment out this line
    			...
    			// If father PO isn't null, it is assembly model file 
    			if (NULL != pParentPO)
    			{
    				if (NULL != pProtoPO)
    				{
    					// Set prototype's RootBaseData
    					iRet = A3DRootBaseSet(m_pAssyPO, &sProtoBaseData);
    				}
    				
    				// Keep the target POs
    				SplitPart splitPart;
    				splitPart.parentPO = sConnector.GetProductOccurrenceFather();
    				splitPart.oldPartPO = pPO;
    				splitPart.newAssyPO = m_pAssyPO;
    				splitPart.protoPO = pProtoPO;
    				splitPart.instanceTransform = sData.m_pLocation;
    				splitPart.instanceBaseData = sRootBaseData;
    				...

  5. Build the project

  6. Create a break and verify that part instance is detected

    Image Added

Replacement using sub-assembly prototype

Multi body part should be replaced using sub-assembly prototype like below image

...

  1. Update part replacement code

    Code Block
    languagecpp
    ...
    	virtual A3DStatus visitLeave(const A3DProductOccurrenceConnector& sConnector) override
    	{
    		A3DStatus iRet = A3D_SUCCESS;
    		...
    		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)
    						{
    							// Create new sub-assembly instance
    							A3DAsmProductOccurrenceData sAssyInstData;
    							A3D_INITIALIZE_DATA(A3DAsmProductOccurrenceData, sAssyInstData);
    							sAssyInstData.m_pLocation = splitPart.instanceTransform;
    							sAssyInstData.m_pPrototype = splitPart.newAssyPO;
    
    							A3DAsmProductOccurrence *pAssyInstPO;
    							iRet = A3DAsmProductOccurrenceCreate(&sAssyInstData, &pAssyInstPO);
    
    							// Set RootBaseData of the instance
    							iRet = A3DRootBaseSet(pAssyInstPO, &splitPart.instanceBaseData);
    
    							// Replace part instance to sub-assembly instance 
    							sData.m_ppPOccurrences[ui] = pAssyInstPO;
    							break;
    						}
    					}
    
    					iRet = A3DAsmProductOccurrenceEdit(&sData, pPO);
    				}
    			}
    		}
    		...

  2. Build the project

  3. 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

Code Block
languagecpp
/***********************************************************************************************************************
*
* Copyright (c) 2017 by Tech Soft 3D, LLC.
* The information contained herein is confidential and proprietary to Tech Soft 3D, LLC., and considered a trade secret
* as defined under civil and criminal statutes. Tech Soft 3D shall pursue its civil and criminal remedies in the event
* of unauthorized use or misappropriation of its trade secrets. Use of this information by anyone other than authorized
* employees of Tech Soft 3D, LLC. is granted only under a written non-disclosure agreement, expressly prescribing the
* scope and manner of such use.
*
********************************************************************************************************************
*
* Copyright (c) 2017 by Tech Soft 3D, LLC.
* The information contained herein is confidential and proprietary to Tech Soft 3D, LLC., and considered a trade secret
* as defined under civil and criminal statutes. Tech Soft 3D shall pursue its civil and criminal remedies in the event
* of unauthorized use or misappropriation of its trade secrets. Use of this information by anyone other than authorized
* employees of Tech Soft 3D, LLC. is granted only under a written non-disclosure agreement, expressly prescribing the
* scope and manner of such use.
*
***********************************************************************************************************************/

/*
* This sample demonstrates how to load a model and export it as a model file of a different format. The chosen
* format is determined by the file extension of the output file name.
*/

#define INITIALIZE_A3D_API
#include <A3DSDKIncludes.h>

#include "common.hpp"
#include <sstream>
#include <vector>

#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];

static A3DAsmModelFile *s_pNewModelFile = NULL;

class bodyOperationVisitor : public A3DTreeVisitor
{
public:
	bodyOperationVisitor(A3DVisitorContainer* psContainer = NULL) : A3DTreeVisitor(psContainer) {};
	~bodyOperationVisitor() {};

private:
	int m_iBodyCnt = 0;
	A3DAsmPartDefinition** m_ppPart;
	A3DAsmProductOccurrence* m_pAssyPO = NULL;
	struct SplitPart
	{
		const A3DAsmProductOccurrence *parentPO;
		A3DAsmProductOccurrence *oldPartPO;
		A3DAsmProductOccurrence *newAssyPO;
	};
	std::vector<SplitPart> m_aSplitPart;

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;
	}

	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;
	*/

/*
* This sample demonstrates how to load a model and export it as a model file of a different format. The chosen
* format is determined by the file extension of the output file name.
*/

#define INITIALIZE_A3D_API
#include <A3DSDKIncludes.h>

#include "common.hpp"
#include <sstream>
#include <vector>

#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];
static A3DAsmModelFile *s_pNewModelFile = NULL;

class bodyOperationVisitor : public A3DTreeVisitor
{
public:
	bodyOperationVisitor(A3DVisitorContainer* psContainer = NULL) : A3DTreeVisitor(psContainer) {};
	~bodyOperationVisitor() {};

private:
	int m_iBodyCnt;
	A3DAsmPartDefinition** m_ppPart;
	A3DAsmProductOccurrence* m_pAssyPO = NULL;
	struct SplitPart
	{
		const A3DAsmProductOccurrence *parentPO;
		A3DAsmProductOccurrence *oldPartPO;
		A3DAsmProductOccurrence *newAssyPO;
		A3DAsmProductOccurrence *protoPO;
		A3DMiscTransformation *instanceTransform;
		A3DRootBaseData instanceBaseData;
	};
	std::vector<SplitPart> m_aSplitPart;

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
		m_iBodyCnt = 0;
		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;
	}

	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)
		{
			// Get prototype
			A3DAsmProductOccurrence *pProtoPO = sData.m_pPrototype;
			A3DAsmProductOccurrenceData sProtoData;
			A3DRootBaseData sProtoBaseData;
			if (NULL != pProtoPO)
			{
				A3D_INITIALIZE_DATA(A3DAsmProductOccurrenceData, sProtoData);
				iRet = A3DAsmProductOccurrenceGet(pProtoPO, &sProtoData);

				A3D_INITIALIZE_DATA(A3DRootBaseData, sProtoBaseData);
				iRet = A3DRootBaseGet(pProtoPO, &sProtoBaseData);

				// Chenck whether the same part is detected
				for (int i = 0; i < m_aSplitPart.size(); i++)
				{
					if (pProtoPO == m_aSplitPart[i].protoPO)
					{
						// Keep the target POs
						SplitPart splitPart;
						splitPart.parentPO = sConnector.GetProductOccurrenceFather();
						splitPart.oldPartPO = pPO;
						splitPart.newAssyPO = m_aSplitPart[i].newAssyPO;
						splitPart.instanceTransform = sData.m_pLocation;
						splitPart.instanceBaseData = sRootBaseData;

						m_aSplitPart.push_back(splitPart);

						return iRet;
					}
				}
			}

			// 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_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, sPOData)sNewPOData);
			sNewPOData.m_uiPOccurrencesSize = m_iBodyCnt;
			sNewPOData.m_dUnit = sData.m_dUnit;
				sPODatasNewPOData.m_dUnitppPOccurrences = sData.m_dUnitppPO;
				sPOData// sNewPOData.m_pLocation = sData.m_pLocation;

				sPOData.m_pPartiRet = A3DAsmProductOccurrenceCreate(&sNewPOData, &m_ppPart[ui]pAssyPO);

				iRet = A3DAsmProductOccurrenceCreate(&sPODataA3DRootBaseSet(m_pAssyPO, &ppPO[ui]sRootBaseData);

				// SetAccess partfather namePO
				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);
const A3DAsmProductOccurrence *pParentPO = sConnector.GetProductOccurrenceFather();

			// If father PO isn't null, it is assembly model file 
			if (NULL != pParentPO)
			{
				if (NULL != pProtoPO)
				{
					// Set prototype's RootBaseData
					iRet = A3DRootBaseSet(m_pAssyPO, &sProtoBaseData);
				}

				// CreateKeep athe newtarget assembly PO to include multiple parts
POs
				A3DAsmProductOccurrenceDataSplitPart sNewPODatasplitPart;
			A3D_INITIALIZE_DATA(A3DAsmProductOccurrenceData, sNewPOData	splitPart.parentPO = sConnector.GetProductOccurrenceFather();
				sNewPODatasplitPart.m_uiPOccurrencesSizeoldPartPO = m_iBodyCntpPO;
				sNewPODatasplitPart.m_dUnitnewAssyPO = sData.m_dUnitpAssyPO;
				sNewPODatasplitPart.m_ppPOccurrencesprotoPO = ppPOpProtoPO;

				iRetsplitPart.instanceTransform = A3DAsmProductOccurrenceCreate(&sNewPOData, & sData.m_pAssyPO)pLocation;

				iRetsplitPart.instanceBaseData = A3DRootBaseSet(m_pAssyPO, &sRootBaseData);

			// Access father PO
			const A3DAsmProductOccurrence *pParentPO = sConnector.GetProductOccurrenceFather();
	m_aSplitPart.push_back(splitPart);

				m_pAssyPO = NULL;
			}
			// IfNo father PO isn't null,maens that it is assemblypart model file

		}
		if (NULL != pParentPO)
else
			{
				// KeepCheck thewhether targetcurrent POsPO andis createdin assemblya modeltargets
				SplitPart splitPart;
				splitPart.parentPO = sConnector.GetProductOccurrenceFatherfor (int i = 0; i < m_aSplitPart.size(); i++)
				splitPart.oldPartPO = pPO;{
				SplitPart splitPart.newAssyPO = m_pAssyPOaSplitPart[i];

				m_aSplitPart.push_back(splitPart);

				m_pAssyPO = NULL;
			}
			// No father PO maens that it is part model file

			m_iBodyCnt = 0;
		}
		else
		{
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)
						{
							// CheckCreate whethernew currentsub-assembly PO is in a targets
instance
							for (int i = 0; i < m_aSplitPart.size(); i++)
			{
				SplitPart splitPart = m_aSplitPart[i];

				if (pPO == splitPart.parentPO)A3DAsmProductOccurrenceData sAssyInstData;
							A3D_INITIALIZE_DATA(A3DAsmProductOccurrenceData, sAssyInstData);
							sAssyInstData.m_pLocation = splitPart.instanceTransform;
							sAssyInstData.m_pPrototype = splitPart.newAssyPO;

				{
					// Find target multi body part PO and replace to assembly PO
A3DAsmProductOccurrence *pAssyInstPO;
							for (A3DUns32 uiiRet = 0; ui < sData.m_uiPOccurrencesSize; ui++)A3DAsmProductOccurrenceCreate(&sAssyInstData, &pAssyInstPO);

					{
		// Set RootBaseData of the instance
				if (sData.m_ppPOccurrences[ui] == splitPart.oldPartPO)
			iRet = A3DRootBaseSet(pAssyInstPO, &splitPart.instanceBaseData);

						{	// Replace part instance to sub-assembly instance 
							sData.m_ppPOccurrences[ui] = splitPart.newAssyPOpAssyInstPO;
							break;
						}
					}

					iRet = A3DAsmProductOccurrenceEdit(&sData, pPO);
				}
			}
		}

		iRet = A3DTreeVisitor::visitLeave(sConnector);
		return iRet;
	}

	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;
	}

};

void traverseModelFile(A3DAsmModelFile* pModelFile)
{
	// Prepare Visitor container
	A3DVisitorContainer sA3DVisitorContainer(CONNECT_TRANSFO);
	sA3DVisitorContainer.SetTraverseInstance(true);

	// Prepare Tree traverse visitor and set to the container
	bodyOperationVisitor *pBodyOpVisitor = new bodyOperationVisitor(&sA3DVisitorContainer);
	sA3DVisitorContainer.push(pBodyOpVisitor);

	// Prepare model file connector and call Traverse
	A3DModelFileConnector sModelFileConnector(pModelFile);
	A3DStatus sStatus = sModelFileConnector.Traverse(&sA3DVisitorContainer);

}

//######################################################################################################################
#ifdef _MSC_VER
int wmain(A3DInt32 iArgc, A3DUniChar** ppcArgv)
#else
int main(A3DInt32 iArgc, A3DUTF8Char** ppcArgv)
#endif
{
	//
	// ### COMMAND LINE ARGUMENTS
	//

	if (iArgc < 2)
	{
		MY_PRINTF2("Usage:\n %s [input CAD file] [output CAD file] [output LOG file]\n", ppcArgv[0]);
		MY_PRINTF("  Default output CAD file is [input CAD file].prc\n");
		MY_PRINTF("  Default output LOG file is [output CAD file]_Log.txt\n\n");
		return A3D_ERROR;
	}

	if (iArgc > 1) MY_STRCPY(acSrcFileName, ppcArgv[1]);
	else           MY_STRCPY(acSrcFileName, DEFAULT_INPUT_CAD);
	if (iArgc > 2) MY_STRCPY(acDstFileName, ppcArgv[2]);
	else           MY_SPRINTF(acDstFileName, "%s.prc", acSrcFileName);
	if (iArgc > 3) MY_STRCPY(acLogFileName, ppcArgv[3]);
	else           MY_SPRINTF(acLogFileName, "%s_Log.txt", acDstFileName);
	GetLogFile(acLogFileName); // Initialize log file

	//
	// ### 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());
	CHECK_RET(sHoopsExchangeLoader.m_eSDKStatus);

	// Uncomment these lines to track memory leaks
	//CHECK_RET(A3DDllSetCallbacksMemory(CheckMalloc, CheckFree));
	CHECK_RET(A3DDllSetCallbacksReport(PrintLogMessage, PrintLogWarning, PrintLogError));

	//
	// ### PROCESS SAMPLE CODE
	//

	// specify input file
	A3DImport sImport(acSrcFileName); // see A3DSDKInternalConvert.hxx for import and export detailed parameters

									  // specify output file
	A3DExport sExport(acDstFileName); // see A3DSDKInternalConvert.hxx for import and export detailed parameters

									  // perform conversion
	CHECK_RET(sHoopsExchangeLoader.Import(sImport));

	traverseModelFile(sHoopsExchangeLoader.m_psModelFile);

	if (NULL != s_pNewModelFile)
	{
		A3DStatus iRet = A3DAsmModelFileDelete(sHoopsExchangeLoader.m_psModelFile);

		sHoopsExchangeLoader.m_psModelFile = s_pNewModelFile;

		s_pNewModelFile = NULL;
	}

	CHECK_RET(sHoopsExchangeLoader.Export(sExport));

	//
	// ### TERMINATE HOOPS EXCHANGE
	//

	// Check memory allocations
	//if (siCheckMallocCounter != 0)
	//	fprintf(GetLogFile(), "stiMallocCounter=%d", siCheckMallocCounter);

	return A3D_SUCCESS;
}

...