HOOPS Visualize 3DF - Parasolid integration

Introduction

If you want to make a 3D modeling application using HOOPS Visualize with Siemens Parasolid, we recommend you to use the latest interface: HPS, rather than the legacy one: 3DF.

However, if you are an expert in 3DF and want to integrate Parasolid into your existing 3DF application or migrate an existing 3D kernel to Parasolid, this article will be a good reference for getting started and checking feasibilities.

Although HOOPS-Parasolid integration module project can be found in <HV-3DF SDK>\Dev_Tools\hp_bridge, there isn’t any integration sample in the latest SDK. (Parasolid Part Viewer is no longer provided after 3DF 24.00)

This is a step-by-step guide of how to integrate the hp_bridge to mfc_simple project.

1. SDK versions

This article is written using the following version of SDKs.

  • HOOPS Visualize 3DF 27.20
  • Parasolid 32.0

2. Prepare a project

Prepare a project

  1. Download HOOPS Visualize 3DF and Parasolid SDKs and unzip
  2. Locate valid hoops_license.h file under <HV-3DF SDK>\Dev_Tools\hoops_3dgs\source
  3. Make a copy of the <HV-3DF SDK>\demo\mfc\mfc_simple folder
    i.e.) mfc_simple_Ps
    hp_bridge-01
  4. Start Visual Studio 2017 and open mfc_simple_v141.vcxproj
  5. Close the Visual Studio and save a solution file under the mfc folder
    i.e.) mfc_simple_Ps_v141.sln
    hp_bridge-02
  6. Create mfc_simple_Ps_VS2017.bat file and implement the following commands to launch the project with environment variables (Change the Parasolid SDK path according to your environment)
SET PARASOLID_INSTALL_DIR=C:\SDK\Parasolid\v32.0\kernel
SET P_SCHEMA=C:\SDK\Parasolid\v32.0\kernel\base\schema
CALL "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\devenv.exe" mfc_simple_Ps_v141.sln
  1. Save and launch the solution using the bat file

Edit project properties

  1. Open the project properties
  2. Add the following additional include directories
    ..\..\..\Dev_Tools\hp_bridge\source
    $(PARASOLID_INSTALL_DIR)\base
  3. Add the following additional library directories
    ..\..\..\Dev_Tools\hp_bridge\lib\nt_x64_v141
    $(PARASOLID_INSTALL_DIR)\base
  4. Add the following additional dependencies
    Debug:
    hoops_utilsstatd_md.lib
    hp_bridgestatd_md.lib
    pskernel_archive.lib
    Release:
    hoops_utilsstat_md.lib
    hp_bridgestat_md.lib
    pskernel_archive.lib
  5. Build and start the application
  6. Verify whether the application is started properly

3. Initialize and shutdown Parasolid

Implement Parasolid initialization

To start Parasolid and the HOOPS/Parasolid Integration module, call HP_Init in an application initialization routine. This function initializes the Parasolid kernel and registers the HOOPS-specific implementation of the Parasolid. All geometry rendered via Parasolid’s PK_TOPOL_render_XXX routines will be mapped to corresponding segments and geometry in the HOOPS graphics database.

  1. Open SampleH.cpp
  2. Add the following includes
#include "parasolid_kernel.h"
#include "kernel_interface.h"
#include "hp_bridge.h"
  1. Add the following code in the InitInstance function to initialize Parasolid
BOOL CSampleHApp::InitInstance()
{
...
	LoadStdProfileSettings();   // Load standard INI file options (including MRU)

	bool has_schema = false;

	wchar_t *schema_path;
	schema_path = _wgetenv(L"P_SCHEMA");
	if (schema_path)
	{
		HP_Init(schema_path, PK_LOGICAL_false, PK_LOGICAL_true);
		has_schema = true;
	}
	
	if (!has_schema)
	{
		AfxMessageBox(_T("Could not determine location of your Schema Files.\ndefine P_SCHEMA environment variable."), MB_ICONSTOP);
		// we still wanna initialize the bridge, if not file load, 
		// user can atleast do geom creation and stuff - Rajesh B (30-May-03)
		HP_Init("", PK_LOGICAL_false, PK_LOGICAL_true);
	}

Note that the HP_Init function requires a Schema path and Boolean value. The Schema path instructs Parasolid where to look for some utility files necessary to read in .x_t files. The schema files are located in the /parasolid/schema directory.

The Boolean should be set to false, unless you are working with Tech Soft 3D to test a new automatic update feature that keeps HOOPS and Parasolid continuously in synchronization.

Implement Parasolid shutdown

To shutdown Parasolid as well as the HOOPS/Parasolid module, call HP_Close. This should be called in an application cleanup routine, when Parasolid is no longer needed.

  1. Add the following code in the ExitInstance function to shutdown Parasolid
int CSampleHApp::ExitInstance() 
{
...
	HP_Close();
	return CHoopsApp::ExitInstance();
}

Implement Parasolid partition functionality

To use a Parasolid session with multiple documents, it is necessary to independent sets of related parts in each document using Parasolid Partition.

  1. Open HSampleHModel.h
  2. Add includes and m_partition member variable and GetPartition function
#include "stdafx.h"
#include "parasolid_kernel.h"
#include "kernel_interface.h"
#include "hp_bridge.h"
...
private:
	PK_PARTITION_t m_partition;
	
public:
    ...
	PK_PARTITION_t GetPartition() { return m_partition; };
  1. Open HSampleHModel.cpp
  2. Add the following code which creates a new partition in the constructor
HSampleHModel::HSampleHModel()
{
	PK_ERROR_code_t PK_result = PK_PARTITION_create_empty(&m_partition);
}
  1. Build and start the application
  2. Verify whether the application is started properly

If you track source code in debug mode, you can see a Parasolid session is started in hp_bridge

Parasolid Partitions are provided to help the application organize entities in the modeling session into sets of related parts, and to save these parts as a single item.

4. Implement Parasolid file open function

Add a function that imports Parasolid file and visualizes it.

Implement Parasolid import function

To read a Parasolid file, call HP_Read_Xmt_File. This parses the specified Parasolid file, populates the Parasolid modeling kernel with its contents, and then maps the model to a corresponding HOOPS segment tree hierarchy that includes all geometry and attributes.

  1. Open HSampleHModel.h
  2. Add Read function declaration
public:
...
	HFileInputResult Read(LPCTSTR FileName);
  1. Open HSampleHModel.cpp
  2. Implement Reat functions
HFileInputResult HSampleHModel::Read(LPCTSTR FileName)
{
	// This process is going to take sometime, convey that to the user
	CWaitCursor show_hourglass_cursor_through_this_function;

	HFileInputResult success = InputOK;
	wchar_t extension[64];
	wchar_t const * ext = wcsrchr(FileName, L'.');
	wchar_t const * first_ext = wcschr(FileName, L'.');

	if (!ext)
		return InputBadFileName;

	++ext; //move one past the dot;
	++first_ext; //move one past the dot;

	if (ext)
		swprintf(extension, 64, L"%ls", ext);

	if (wcsieq(extension, L"xmt_txt") || wcsieq(extension, L"x_t") || wcsieq(extension, L"x_b") || wcsieq(extension, L"xmt_bin"))
	{
		HP_Set_Hash_Level(0);

		HC_Open_Segment_By_Key(m_ModelKey);

		HC_Set_Heuristics("polygon handedness = left");

		PK_PART_receive_o_t options;
		PK_ERROR_code_t		PK_result;

		PK_PART_receive_o_m(options);

		if (wcsieq(extension, L"x_b") || wcsieq(extension, L"xmt_bin"))
			options.transmit_format = PK_transmit_format_neutral_c;
		else
			options.transmit_format = PK_transmit_format_text_c;

		PK_result = HP_Read_Xmt_File(FileName, GetPartition(), 0, 0, &options, true, "custom");	// read an XMT file
		if (PK_result == PK_ERROR_schema_access_error)
		{
			TCHAR str[MVO_BUFFER_SIZE];
			_stprintf(str, _T("Could not find schema files at specified path."));
			AfxMessageBox(str, MB_ICONINFORMATION | MB_OK);
		}
		else if (PK_result != PK_ERROR_no_errors)
			AfxMessageBox(_T("Failed to read Parasolid file. HP_Read_Xmt_File returned an error"), MB_ICONINFORMATION | MB_OK);

		HC_Close_Segment();
	}

	return success;
}

Create file open dialog including Parasolid Files

  1. Open SampleH.h
  2. Add a function declaration of file open
	//{{AFX_MSG(CSampleHApp)
	afx_msg void OnAppAbout();
	afx_msg void OnFileOpen();
  1. Open SampleH.cpp
    4.Implement OnFileOpen function
void CSampleHApp::OnFileOpen()
{
	CString filter = _T("Parasolid Files (*.x_t, *.x_b, *.xmt_txt, *.xmt_bin)|*.x_t;*.x_b;*.xmt_txt;*.xmt_bin|HOOPS Stream Files (*.hsf)|*.hsf|All Files (*.*)|*.*||");

	CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY, filter, NULL);
	if (dlg.DoModal() == IDOK)
	{
		CString pathname;
		pathname = dlg.GetPathName();

		OpenDocumentFile(pathname);
	}
}
  1. Add a message map to connect the file open command to the function
BEGIN_MESSAGE_MAP(CSampleHApp, CHoopsApp)
	//{{AFX_MSG_MAP(CSampleHApp)
	ON_COMMAND(ID_APP_ABOUT, OnAppAbout)
	ON_COMMAND(ID_FILE_OPEN, OnFileOpen)

Associate a command to the read function

  1. Open SampleHDoc.cpp
  2. Add the following code to process Parasolid files
	if(!_tcscmp(extension, _T("hsf")))
	{
		...
	}
	else if (!_tcscmp(extension, _T("xmt_txt")) || !_tcscmp(extension, _T("x_t")) || !_tcscmp(extension, _T("x_b")) || !_tcscmp(extension, _T("xmt_bin")))
	{
		HFileInputResult result = ((HSampleHModel*)m_pHoopsModel)->Read(lpszPathName);
	}
  1. Build and start the application
  2. Verify whether Parasolid (x_t) file can be opened

Implement a create solid body function

Verify a solid model creating workflow using create block function as an example.

Implement Parasolid create block function

When Parasolid entities are being dynamically created, the HP_Render_Entity function needs to be called.

  1. Open HSampleHModel.h
  2. Add CreateBlock declaration and m_dUnit variable
public:
	...
	void CreateBlock(const double in_dX, const double in_dY, const double in_dZ, const double in_dOX, const double in_dOY, const double in_dOZ);
	
private:
	const double m_dUnit = 1000.0;
  1. Open HSampleModel.cpp
  2. Implement CreateBlock function
void HSampleHModel::CreateBlock(const double in_dX, const double in_dY, const double in_dZ, const double in_dOX, const double in_dOY, const double in_dOZ)
{
	double dX = in_dX / m_dUnit;
	double dY = in_dY / m_dUnit;
	double dZ = in_dZ / m_dUnit;
	double dOX = in_dOX / m_dUnit;
	double dOY = in_dOY / m_dUnit;
	double dOZ = in_dOZ / m_dUnit;

	PK_PARTITION_set_current(m_partition);

	PK_AXIS2_sf_s basis_set;
	basis_set.location.coord[0] = dOX;
	basis_set.location.coord[1] = dOY;
	basis_set.location.coord[2] = dOZ;
	basis_set.axis.coord[0] = 0.;
	basis_set.axis.coord[1] = 0.;
	basis_set.axis.coord[2] = 1.;
	basis_set.ref_direction.coord[0] = 1.;
	basis_set.ref_direction.coord[1] = 0.;
	basis_set.ref_direction.coord[2] = 0.;
	PK_BODY_t solid_body = PK_ENTITY_null;
	PK_ERROR_code_t PK_result = PK_BODY_create_solid_block(dX, dY, dZ, &basis_set, &solid_body);

	HC_Open_Segment_By_Key(m_ModelKey);
	HP_Render_Entity(solid_body);
	HC_Close_Segment();
}

Since Parasolid internal unit is meter, it is necessary to set length values in meter.

Add Create Block dialog box

  1. Open Resource View of Visual Studio and right click the Dialog group and select Insert Dialog
  2. Set IDD_CREATTE_BLOCK_DIALOG as ID of the dialog properties
  3. Layout controls and set IDs as below
  4. Right-click on the background of the dialog box and select Add Class...
  5. Set CreateBlockDlg as class name and click OK
  6. Add include in CreateBlockDlg.h
#pragma once
#include "Resource.h"
  1. Open CreateBlockDlg.cpp and edit includes
#include "stdafx.h"
#include "CreateBlockDlg.h"
#include "afxdialogex.h"

Add variables of controls

  1. Open IDD_CREATE_BLOCK_DIALOG using the Resource View

  2. Right-click the edit control of XL and select Add Variable...

  3. Select Value from Category, set m_dEditXL as Name, set double as Variable type, and click Finish

  4. Add variables of other edit controls as well

  5. Open CreateBlockDlg.h and edit the constructor’s declaration

	CreateBlockDlg(const double in_XL, const double in_YL, const double in_ZL, CWnd* pParent = nullptr);
  1. Open CreateBlockDlg.cpp and edit the constructor’s implementation
CreateBlockDlg::CreateBlockDlg(const double in_XL, const double in_YL, const double in_ZL, CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_CREATTE_BLOCK_DIALOG, pParent)
	, m_dEditXL(in_XL)
	, m_dEditYL(in_YL)
	, m_dEditZL(in_ZL)
	, m_dEditOX(0)
	...

Add Parasolid - Create a Block menu

  1. Open Resource View
  2. Open Menu - IDR_HOOPSTYPE
  3. Add Parasolid menu
  4. Add Create Block... as a sub menu

Associate menu to create block function with a dialog box

  1. Right-click the Create Block... menu and select Add Event Handler...
  2. Select CSampleHView from the Class List and click OK
  3. Implement code to create a block with the dialog box
#include "CreateBlockDlg.h"
...
void CSampleHView::OnParasolidCreateblock()
{
	CreateBlockDlg *pDlg = new CreateBlockDlg(100, 100, 100, this);
	if (pDlg->DoModal() == IDOK)
	{
		double dXL = pDlg->m_dEditXL;
		double dYL = pDlg->m_dEditYL;
		double dZL = pDlg->m_dEditZL;
		double dOX = pDlg->m_dEditOX;
		double dOY = pDlg->m_dEditOY;
		double dOZ = pDlg->m_dEditOZ;
		
		CSampleHDoc * pDoc = (CSampleHDoc *)GetDocument();
		((HSampleHModel*)(pDoc->m_pHoopsModel))->CreateBlock(dXL, dYL, dZL, dOX, dOY, dOZ);

		m_pHView->Update();
	}
}
  1. Build and start the application
  2. Verify whether blocks can be created properly

6. Implement an edit function with a custom operator

Verify a model editing workflow using the blend-R function as an example.

Implement Parasolid blend R function

When Parasolid entities are edited via the application’s user interface, the HP_Update_Entity function needs to be called.

  1. Open HSampleHModel.h
  2. Add BlendEdgeR function declaration
public:
	...
	void BlendEdgeR(PK_EDGE_t edge, double blendR);
  1. Open HSampleModel.cpp
  2. Implement BlandEdgeR function
void HSampleHModel::BlendEdgeR(PK_EDGE_t edge, double blendR)
{
	PK_PARTITION_set_current(m_partition);

	double dR = blendR / m_dUnit;

	int n_faces = 0;
	PK_FACE_t* faces = NULL;
	PK_ERROR_code_t error_code;

	error_code = PK_EDGE_ask_faces(edge, &n_faces, &faces);

	PK_BODY_t body = NULL;
	error_code = PK_FACE_ask_body(faces[0], &body);

	PK_EDGE_t edges[] = { edge };

	PK_EDGE_set_blend_constant_o_t options;
	PK_EDGE_set_blend_constant_o_m(options);

	options.properties.propagate = PK_blend_propagate_yes_c;
	options.properties.ov_cliff_end = PK_blend_ov_cliff_end_yes_c;

	int n_blend_edges = 0;
	PK_EDGE_t* blend_edges = NULL;

	error_code = PK_EDGE_set_blend_constant(1, edges, dR, &options, &n_blend_edges, &blend_edges);

	PK_BODY_t partr = PK_ENTITY_null;
	PK_EDGE_ask_body(blend_edges[0], &partr);

	PK_BODY_fix_blends_o_t optionsFix;
	PK_BODY_fix_blends_o_m(optionsFix);

	int n_blends = 0;
	PK_FACE_t* blends = 0;
	PK_FACE_array_t* unders = 0;
	int* topols = 0;
	PK_blend_fault_t fault;
	PK_EDGE_t fault_edge = PK_ENTITY_null;
	PK_ENTITY_t fault_topol = PK_ENTITY_null;

	// perform the Blend operation
	error_code = PK_BODY_fix_blends(partr, &optionsFix, &n_blends, &blends, &unders, &topols, &fault, &fault_edge, &fault_topol);

	if (error_code != 0)
	{
		wchar_t error_message[256];
		_swprintf(error_message, _T("failed during Blend operation \n Error Code : %d"), error_code);
		MessageBox(NULL, error_message, 0, MB_OK);
	}

	PK_MEMORY_free(faces);
	PK_MEMORY_free(blend_edges);
	PK_MEMORY_free(blends);
	PK_MEMORY_free(unders);

	HP_Update_Entity(body);
}

Add Brend-R dialog box

Create a dialog box to specify the blend R value.

  1. Right-click the Dialog group of Resource View and select Insert Dialog
  2. Set IDD_BLEND_R_DIALOG as ID
  3. Layout controls and set ID
    hp_bridge-09
  4. Right-click on the background of the dialog box and select Add Class...
  5. Set BlendRDlg as the class name and click OK
  6. Add include in BlendRDlg.h
#pragma once
#include "Resource.h"
  1. Open BlendRDlg.cpp and edit includes
#include "stdafx.h"
#include "CreateBlockDlg.h"
#include "afxdialogex.h"

Add variables of controls

  1. Open IDD_BLEND_R_DIALOG using the Resource View
  2. Right-click the edit control of R and select Add Variable...
  3. Select Value from Category, set m_dBlendR as Name, set double as Variable type and click Finish
  4. Open BlendRDlg.h and edit constructor’s declaration
BlendRDlg(const double in_R, CWnd* pParent = nullptr);
  1. Open BlendRDlg.cpp and edit constructor’s implementation
BlendRDlg::BlendRDlg(const double in_R, CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_BLEND_R_DIALOG, pParent)
	, m_dBlendR(in_R)

Create a custom operator

Create a custom operator to specify the target edge. This operator can commonly used by commands that are invoked by clicking an entity such as delete body.

  1. Select Project - Add Class...
  2. Set EntityOneClickOperator as Class name, HOpSelectApeture as Base class and click OK
  3. Implement EntityOneClickOperator.h
#pragma once
#include "HOpSelectAperture.h"
#include "HSampleHModel.h"

class HBaseOperator;

class EntityOneClickOperator :
	public HOpSelectAperture
{
public:
	EntityOneClickOperator(HBaseView* view, const char* entityType, const char* processType, int DoRepeat = 0, int DoCapture = 1);
	~EntityOneClickOperator();

	const char * GetName();
	virtual int OnLButtonDown(HEventInfo &event);

private:
	const char* m_pCEntityType;
	const char* m_pCProcessType;

	void normalize_vector(double* vector);

};
  1. Open EntityOneClickOperator.cpp and implement
#include "EntityOneClickOperator.h"
#include "HBaseView.h"

#include "vlist.h"
#include "BlendRDlg.h"
#include "HSelectionSet.h"
#include "HEventManager.h"

EntityOneClickOperator::EntityOneClickOperator(HBaseView* view, const char* entityType, const char* processType, int DoRepeat, int DoCapture)
	: HOpSelectAperture(view, DoRepeat, DoCapture)
	, m_pCEntityType(entityType)
	, m_pCProcessType(processType)
{
	if (!strcmp(m_pCEntityType, "EDGE"))
	{
		GetView()->SetDynamicHighlighting(false);
		GetView()->SetViewSelectionLevel(HViewSelectionLevel::HSelectionLevelEntity);

		HSelectionSet* selection = GetView()->GetSelection();
		selection->SetSelectionLevel(HSelectLevel::HSelectEntity);
	}
}

EntityOneClickOperator::~EntityOneClickOperator()
{
	GetView()->SetDynamicHighlighting(false);

	HSelectionSet* selection = GetView()->GetSelection();
	selection->Init();
}


// normalize double precision vector in place 
void EntityOneClickOperator::normalize_vector(double* vector)
{
	double length;
	length = sqrt(vector[0] * vector[0] + vector[1] * vector[1] + vector[2] * vector[2]);
	vector[0] /= length;
	vector[1] /= length;
	vector[2] /= length;
}

const char * EntityOneClickOperator::GetName()
{
	return m_pCProcessType;
}

int EntityOneClickOperator::OnLButtonDown(HEventInfo &event)
{
	HPoint  new_pos;
	int		res = 0;

	HSelectionSet* selection = GetView()->GetSelection();

	new_pos = event.GetMouseWindowPos();

	HC_Open_Segment_By_Key(GetView()->GetViewKey());

	res = HC_Compute_Selection(".",
		(selection->GetSubwindowPenetration() ? "" : "./scene/overwrite"),
		"v, selection bias = lines, related selection limit = -1, selection sorting, internal selection limit = 0", new_pos.x, new_pos.y);

	HC_Close_Segment();

	if (0 < res) {
		if (!strcmp(m_pCProcessType, "BLEND"))
		{
			HC_KEY  key = INVALID_KEY;
			HC_Show_Selection_Original_Key(&key);

			PK_ENTITY_t entity = HP_Compute_TagID(key, PK_CLASS_entity);

			PK_CLASS_t ent_class;
			PK_ENTITY_ask_class(entity, &ent_class);

			if (PK_CLASS_edge == ent_class)
			{
				HandleSelection(event);
				GetView()->Update();	// update the scene to reflect the new highlight attributes

				BlendRDlg *pDlg = new BlendRDlg(10.0);
				if (pDlg->DoModal() == IDOK)
				{
					double blendR = pDlg->m_dBlendR;
					((HSampleHModel*)GetView()->GetModel())->BlendEdgeR(entity, blendR);

					GetView()->Update();
				}
			}
		}
	}

	return (HOP_OK);
}

It is possible to get Parasolid entity tag ID from selected HC_KEY using the HP_Compute_TagID API.

Add Parasolid - Blend-R menu and associate to the operator

  1. Open Menu - IDR_HOOPSTYPE of Resource View
  2. Add Blend-R menu as sub-menu of Parasolid
  3. Right-click the Blend-R menu and select Add Event Handler...
  4. Select CSampleHView from the Class List and click OK
  5. Implement code to enable EntityOneClickOperator
#include "EntityOneClickOperator.h"
...
void CSampleHView::OnParasolidBlend()
{
	LocalSetOperator(new EntityOneClickOperator(m_pHView, "EDGE", "BLEND"));
}
  1. Add code in LocalSetOperator function, so the edge lines appear while the Blend-R command
void CSampleHView::LocalSetOperator(HBaseOperator * NewOperator) 
{
	const char* name = NewOperator->GetName();
	HC_Open_Segment_By_Key(m_pHView->GetSceneKey());
	if (!strcmp(name, "BLEND"))
		HC_Set_Visibility("lines = on");
	else
		HC_Set_Visibility("lines = off");
	HC_Close_Segment();
	m_pHView->Update();
	...
  1. Build and start the application
  2. Create a block and verify whether blend-R can be inserted

7. Implement a delete body function

Verify a model deleting workflow.

Implement Parasolid delete entity function

When a Parasolid entity is deleted, it is necessary to call HP_Delete_Entity_Geometry function to delete the HOOPS geometry associated with a Parasolid entity.

  1. Open HSampleHModel.h
  2. Add DeleteEntity function declaration
public:
...
	void DeleteEntity(PK_ENTITY_t entity);`
  1. Open HSampleModel.cpp
  2. Implement DeleteEntity function
void HSampleHModel::DeleteEntity(PK_ENTITY_t entity)
{
	HP_Delete_Entity_Geometry(1, &entity);
	PK_ENTITY_delete(1, &entity);
}

Edit operator

  1. Open EntityOneClickOperator.cpp
  2. Add code to call the delete entity
EntityOneClickOperator::EntityOneClickOperator(HBaseView* view, const char* entityType, const char* processType, int DoRepeat, int DoCapture)
	: HOpSelectAperture(view, DoRepeat, DoCapture)
	, m_pCEntityType(entityType)
	, m_pCProcessType(processType)
{
	if (!strcmp(m_pCEntityType, "EDGE"))
	{
		...
	}
	else if (!strcmp(m_pCEntityType, "BODY"))
	{
		GetView()->SetDynamicHighlighting(true);
		GetView()->SetViewSelectionLevel(HViewSelectionLevel::HSelectionLevelSegment);

		HSelectionSet *highlightSelectionSet = GetView()->GetHighlightSelection();
		highlightSelectionSet->SetSelectionLevel(HSelectLevel::HSelectSegment);
	}
}
...
int EntityOneClickOperator::OnLButtonDown(HEventInfo &event)
{
...
	if (0 < res) {
		if (!strcmp(m_pCProcessType, "BLEND"))
		{
			...
		}
		else if (!strcmp(m_pCProcessType, "DELETE_BODY"))
		{
			PK_ENTITY_t* entities;
			int num_entities;
			HP_Compute_Selected_Entity_List(&entities, &num_entities);

			if (0 < num_entities)
			{
				PK_BODY_t body = entities[0];

				((HSampleHModel*)GetView()->GetModel())->DeleteEntity(body);

				GetView()->Update();
			}
		}
	}

HP_Compute_Selected_Entity_List returns Parasolid entity Tag IDs of current selection list.

Add Parasolid - Delete Body menu and associate to the operator

  1. Open Menu - IDR_HOOPSTYPE of Resource View
  2. Add the Delete Body menu as sub-menu of Parasolid
  3. Right-click the Delete Body menu and select Add Event Handler...
  4. Select CSampleHView from the Class List and click OK
  5. Implement code to enable EntityOneClickOperator
void CSampleHView::OnParasolidDeletebody()
{
	LocalSetOperator(new EntityOneClickOperator(m_pHView, "BODY", "DELETE_BODY"));
}
  1. Build and start the application
  2. Create a block and verify whether the body can be removed

8. Implement Boolean function

Let’s try to make boolean - unite, subtract, and intersect functions.

Implement Parasolid boolean function

HOOPS geometries associated target and tool bodies should be deleted using HP_Delete_Entity_Geometry and then updating target body using HP_Render_Entities.

  1. Open HSampleHModel.h
  2. Add Boolean function declaration
public:
	...
	bool Boolean(PK_boolean_function_t operation, PK_BODY_t target, int number_of_tools, PK_BODY_t *tools);
  1. Open HSampleModel.cpp
  2. Implement Boolean function
bool HSampleHModel::Boolean(PK_boolean_function_t operation, PK_BODY_t target, int number_of_tools, PK_BODY_t *tools)
{
	PK_ERROR_code_t error_code;
	PK_BODY_boolean_o_t options;
	PK_TOPOL_track_r_t tracking;
	PK_boolean_r_t results;
	PK_PARTITION_t partition;

	HP_Delete_Entity_Geometry(1, &target);
	HP_Delete_Entity_Geometry(number_of_tools, tools);

	//assert(target);
	//assert(number_of_tools > 0);
	PK_BODY_boolean_o_m(options);
	options.function = operation;
	//options.target_material_side = PK_boolean_material_outside_c;

	PK_ENTITY_ask_partition(target, &partition);

	for (int i = 0; i < number_of_tools; i++)
		PK_BODY_change_partition(tools[i], partition);

	// perform the Boolean operation
	error_code = PK_BODY_boolean_2(target, number_of_tools, tools, &options, &tracking, &results);

	int n_attribs = 0;
	PK_ATTRIB_t* attribs = NULL;
	PK_PART_ask_all_attribs(target, 0, &n_attribs, &attribs);

	if (error_code != 0)
	{
		wchar_t error_message[256];
		_swprintf(error_message, _T("failed during Boolean operation \n Error Code : %d"), error_code);
		MessageBox(NULL, error_message, 0, MB_OK);
		return 1;
	}

	// render bodies
	HC_Open_Segment_By_Key(m_ModelKey);
	HP_Render_Entities(results.n_bodies, results.bodies);
	HC_Close_Segment();

	return 0;
}

Create a custom operator

  1. Locate PsBooleanOperator. and PsBooleanOperator.cpp in your project folder
    PsBooleanOperator.zip (2.0 KB)
  2. Select Project - Add Existing Items...
  3. Select PsBooleanOperator. and PsBooleanOperator.cpp files

Add Parasolid - Boolean menu

  1. Open Menu - IDR_HOOPSTYPE of Resource View
  2. Add Boolean menu as sub-menu of Parasolid
  3. Add Unite, Subtract and Intersect as sub-menus of Boolean

Associate a menu to the operator

  1. Right-click the Unite menu and select Add Event Handler...
  2. Select CSampleHView from the Class List and click OK
  3. Add event handlers of Subtract and Intersect menus as well
  4. Implement code to enable PsBooleanOperator
#include "PsBooleanOperator.h"
...
void CSampleHView::OnBooleanUnite()
{
	LocalSetOperator(new PsBooleanOperator(m_pHView, "UNITE"));
}

void CSampleHView::OnBooleanSubtrac()
{
	LocalSetOperator(new PsBooleanOperator(m_pHView, "SUBTRACT"));
}

void CSampleHView::OnBooleanInersect()
{
	LocalSetOperator(new PsBooleanOperator(m_pHView, "INTERSECT"));
}
  1. Build and start the application
  2. Create blocks with offset and verify whether boolean can be worked

9. Implement Parasolid file saving function

Implement Parasolid file save function

The Parasolid transmit function is encapsulated by HP_Write_Xmt_File function.

  1. Open HSampleHModel.h
  2. Add Write function declaration
public:
	...
	HFileOutputResult Write(const wchar_t * FileName);
  1. Open HSampleModel.cpp
  2. Implement Write function
HFileOutputResult HSampleHModel::Write(const wchar_t * FileName)
{
	HFileInputResult success = OutputOK;
	int						n_bodies;
	PK_BODY_t				*bodies;
	PK_PART_transmit_o_t	options;
	PK_ERROR_code_t			PK_result;

	PK_PART_transmit_o_m(options);

	options.transmit_format = PK_transmit_format_text_c;

	PK_PARTITION_ask_bodies(m_partition, &n_bodies, &bodies);
	PK_ASSEMBLY_t *assemblies;
	int n_assemblies;
	PK_PARTITION_ask_assemblies(m_partition, &n_assemblies, &assemblies);
	PK_PART_t *parts = new PK_PART_t[n_assemblies + n_bodies];
	int n_parts = 0;
	int i;
	for (i = 0; i < n_bodies; i++)
	{
		int numref;
		PK_PART_ask_ref_instances(bodies[i], &numref, 0);
		if (!numref)
			parts[n_parts++] = bodies[i];
	}

	for (i = 0; i < n_assemblies; i++)
	{
		int numref;
		PK_PART_ask_ref_instances(assemblies[i], &numref, 0);
		if (!numref)
			parts[n_parts++] = assemblies[i];
	}

	PK_PARTITION_set_current(m_partition);

	PK_result = HP_Write_Xmt_File((FileName), n_parts, parts, &options);	// write an XMT file

	if (PK_result < 0) success = OutputFail;

	if (n_bodies > 0)
		PK_MEMORY_free(bodies);
	if (n_assemblies > 0)
		PK_MEMORY_free(assemblies);
	delete parts;

	return success;
}

Edit file save as function

  1. Open SampleHView.cpp
  2. Edit OnFileSaveAs function
void CSampleHView::OnFileSaveAs() 
{
	CString filter = _T("Parasolid Files (*.x_t)|*.x_t|HOOPS Stream File (*.hsf)|*.hsf|HOOPS Metafile (*.hmf)|*.hmf|Postscript File (*.ps)|*.ps|HPGL/2 Plot File (*.hp)|*.hp|TIFF Image File (*.tif)|*.tif||");
	...
		if (!_tcseq(pathname, _T("")))
		{
			if( _tcseq(extension, _T("hsf")) )
			{
				...
			}
			else if (_tcseq(extension, _T("x_t")))
			{
				HFileInputResult result = ((HSampleHModel*)(pDoc->m_pHoopsModel))->Write(pathname);
			}
  1. Build and start the application
  2. Verify whether the created body can be saved

Source code

Download sample projects from the following Git repository:

Parasolid samples

If you want to see more Parasolid source code, please download Parasolid Jumpstart kit from our Dev Zone.

When you install it, you will find sample application project in Source folder of
C:\Program Files (x86)\Parasolid\Parasolid Jumpstart Kit\example_applications\C++
Please refer the Using the Example Application.pdf and build the application. (Make sure to do Rebuild after chusing a code example)

You can try various Parasolid functions and refer the source code int it.


You will be able to port your favorite sample code to the mfc_simple project as well as the above steps.

1 Like