Make use of Topology from Exchange imported data : select neighbouring faces

Hi, here is a quick tip for you to learn:

  • what is topology
  • how to make use of it within Visualize
  • some ideas to go beyond

Topology:

In CAD, topology represents structure of a solid and relationship between faces and edges. The key element here is that an edge can be shared amongst multiple faces. This is different from the tessellation edges and we can leverage it.

Use topology to select neighbours of a face

We will customize the default selection operator that we provide with the mfcsandbox project.

  1. Let’s update the mouse event to make use of our future fonctions.
  2. Let’s output the hierarchy with Components to verify we have the right information. void SandboxHighlightOperator::DebugComponentHierarchy()
  3. Let’s list the neighbours of a face. HPS::ComponentArray SandboxHighlightOperator::GetNeighbours(HPS::Component a_face)
  4. Let’s update the display to also highlight the neighbour faces. void SandboxHighlightOperator::ShowNeighbours()

Preparation:

//! [OnMouseDown]
bool SandboxHighlightOperator::OnMouseDown(
	HPS::MouseState const & in_state)
{
	auto sel_opts = GetSelectionOptions();
	sel_opts.SetLevel(SandboxHighlightOperator::SelectionLevel);
	SetSelectionOptions(sel_opts);

	if (IsMouseTriggered(in_state) && HPS::SelectOperator::OnMouseDown(in_state))
	{
		HighlightCommon();
		DebugComponentHierarchy();
		ShowNeighbours();
		return true;
	}
	return false;
}

First, we will output a little bit of code to get type of the component and go up the hierarchy. You can then set appropriate breakpoint and select an edge and see the output like below

image

// Used to output the structure of Components when imported via Exchange.
void SandboxHighlightOperator::DebugComponentHierarchy()
{
	// we will use this and select an edge to display the hierarchy
	// Typical output in debugger console if you output "type" value in the debugger:
	/*	ExchangeTopoEdge(20487)
		ExchangeTopoCoEdge(20486)
		ExchangeTopoLoop(20485)
		ExchangeTopoFace(20484)
		ExchangeTopoShell(20483)
		ExchangeTopoConnex(20482)
		ExchangeTopoBody(20481)
		ExchangeRIBRepModel(12289)
		ExchangePartDefinition(4099)
		ExchangeProductOccurrence(4098)*/
	// Notice there are some differences with the structure from HOOPS Exchange structures.
	HPS::SelectionResults selection_results = GetActiveSelection();
	size_t selected_count = selection_results.GetCount();
	if (selected_count > 0)
	{
		HPS::CADModel cad_model = cview->GetDocument()->GetCADModel();
		HPS::SelectionResultsIterator it = selection_results.GetIterator();
		if (it.IsValid())
		{
			HPS::ComponentPath component_path = cad_model.GetComponentPath(it.GetItem());
			HPS::Component c = component_path.Front();
			while (c.GetOwners().size() > 0)
			{
				auto type = c.GetComponentType();
				c = c.GetOwners()[0];
			}
			
		}
	}
}

Getting the list of neighbours is quite straighforward once you understand the hierarchy in place.

// Will return the adjacent faces using topology
HPS::ComponentArray SandboxHighlightOperator::GetNeighbours(HPS::Component a_face)
{
	HPS::ComponentArray neighbours;
	
	auto model = cview->GetCanvas().GetFrontView().GetAttachedModel();
	HPS::Exchange::CADModel cadModel = HPS::Exchange::CADModel(model);

	// Get list of edges. Edges are shared amongts adjacent faces
	auto edgesArray = a_face.GetAllSubcomponents(HPS::Component::ComponentType::ExchangeTopoEdge);

	for (int i = 0; i < edgesArray.size(); i++)
	{
		auto edge = edgesArray[i];

		auto owners = edge.GetOwners();
		// Recap of hierarchy: edge->coedge->loop->face
		// Test if this is not an open edge (no other neihgbour for this edge)
		if (owners.size() != 1)
		{
			for (int j = 0; j < owners.size(); j++)
			{
				auto coedge = owners[j];
				auto loop = coedge.GetOwners()[0];
				auto face = loop.GetOwners()[0];
				// we don't want to add current face, so we need to test the returned face in the array.
				if (!face.Equals(a_face))
				{
					neighbours.push_back(face);
				}
			}
		}
	}
	return neighbours;
}

Here we simply show the neighbour faces with same color highlight than the current face.

// Will highlight the neighbour faces of selected elements
void SandboxHighlightOperator::ShowNeighbours()
{
	HPS::Canvas canvas = cview->GetCanvas();
	HPS::HighlightOptionsKit highlight_options(HPS::HighlightOptionsKit::GetDefault());
	highlight_options.SetStyleName("highlight_style");
	highlight_options.SetSubentityHighlighting(SelectionLevel == HPS::Selection::Level::Subentity);
	highlight_options.SetOverlay(HPS::Drawing::Overlay::InPlace);

	HPS::SelectionResults selection_results = GetActiveSelection();
	size_t selected_count = selection_results.GetCount();
	if (selected_count > 0)
	{
		HPS::CADModel cad_model = cview->GetDocument()->GetCADModel();
		HPS::SelectionResultsIterator it = selection_results.GetIterator();
		HPS::ComponentPath component_path = cad_model.GetComponentPath(it.GetItem());
		if (!component_path.Empty())
		{
			// we check we did select a face and not an edge or something else
			if (HPS::Exchange::Component::ComponentType::ExchangeTopoFace == component_path.Front().GetComponentType())
			{
				auto neighbours = GetNeighbours(component_path.Front());
				for (int i = 0; i < neighbours.size(); i++)
				{
					auto face = neighbours[i];
					auto keyPath = face.GetKeyPath(face);
					auto facePath = cad_model.GetComponentPath(keyPath[0]);
					facePath.Highlight(canvas, highlight_options);
				}
				canvas.Update();
			}
		}
	}
}

You can see that now if you select a face (under cursor in orange), neighbour faces will be selected in orange.

Here is the source code if you want to quickly test it:
SandboxHighlightOp.cpp (7.3 KB)
SandboxHighlightOp.h (668 Bytes)

Go Beyond:

From there there are many things you can do:

  1. why not create your own rendering style to better distinguish selected face from neighbours?
  2. why not display edge normals for each selected face?
  3. why not add some clever restriction about selection (angle between faces, type of surface) to make concrete useful examples? Below I extend selection to faces that have angle less than 90 degrees.

I’m looking forward for some ideas you might have, some additional questions, please make this thread live, and let me know if these information were somehow useful and if you would like to see more topics like these on the forum.

3 Likes

Hi Florian,

Thanks for the article - is it possible to do the same thing in Hoops communicator?

Warren

1 Like

Hi Warren,

thanks you for your question, currently it’s not as staighforward with HOOPS Communicator API as we do not read topology information during conversion. That means you would have to build the topology separately using HOOPS Exchange after conversion, to load them in the browser and be able to use it. Do you have a concrete use case on your end right now?

It’s definitely a good article idea and we’ll work on showing how to do it.

Best regards,

1 Like

Hi Florian,

Yes, we’re looking at extracting faces associated with channels in a design – we are already using hoops exchange to parse and tesselate the file separately from Communicator, so it shouldn’t be that hard to extract the topology info at the same time… I’ve passed the info along to our dev team and we’ll discuss it further.

Thanks!

Warren

1 Like